diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4acae17
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+SOURCES/gnulib-fixes.tar.gz
+SOURCES/grub-2.06.tar.xz
+SOURCES/theme.tar.bz2
+SOURCES/unifont-13.0.06.pcf.gz
diff --git a/.grub2.metadata b/.grub2.metadata
new file mode 100644
index 0000000..88b5cc8
--- /dev/null
+++ b/.grub2.metadata
@@ -0,0 +1,4 @@
+1a07692dfaa916675a92b1383885141f490f98d2 SOURCES/gnulib-fixes.tar.gz
+c9f93f1e195ec7a5a21d36a13b469788c0b29f0f SOURCES/grub-2.06.tar.xz
+cf0b7763c528902da7e8b05cfa248f20c8825ce5 SOURCES/theme.tar.bz2
+3b39cb0830367171760ec536cab805abdbe08bc5 SOURCES/unifont-13.0.06.pcf.gz
diff --git a/SOURCES/0001-Revert-templates-Fix-user-facing-typo-with-an-incorr.patch b/SOURCES/0001-Revert-templates-Fix-user-facing-typo-with-an-incorr.patch
new file mode 100644
index 0000000..fcc7825
--- /dev/null
+++ b/SOURCES/0001-Revert-templates-Fix-user-facing-typo-with-an-incorr.patch
@@ -0,0 +1,24 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Fri, 11 Jun 2021 12:10:45 +0200
+Subject: [PATCH] Revert "templates: Fix user-facing typo with an incorrect use
+ of "it's""
+
+This reverts commit 722737630889607c3b5761f1f5a48f1674cd2821.
+---
+ util/grub.d/30_os-prober.in | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in
+index 5984e92d291..94622481284 100644
+--- a/util/grub.d/30_os-prober.in
++++ b/util/grub.d/30_os-prober.in
+@@ -36,7 +36,7 @@ if ! command -v os-prober > /dev/null || ! command -v linux-boot-prober > /dev/n
+   exit 0
+ fi
+ 
+-grub_warn "$(gettext_printf "os-prober will be executed to detect other bootable partitions.\nIts output will be used to detect bootable binaries on them and create new boot entries.")"
++grub_warn "$(gettext_printf "os-prober will be executed to detect other bootable partitions.\nIt's output will be used to detect bootable binaries on them and create new boot entries.")"
+ 
+ OSPROBED="`os-prober | tr ' ' '^' | paste -s -d ' '`"
+ if [ -z "${OSPROBED}" ] ; then
diff --git a/SOURCES/0002-Revert-templates-Properly-disable-the-os-prober-by-d.patch b/SOURCES/0002-Revert-templates-Properly-disable-the-os-prober-by-d.patch
new file mode 100644
index 0000000..4187765
--- /dev/null
+++ b/SOURCES/0002-Revert-templates-Properly-disable-the-os-prober-by-d.patch
@@ -0,0 +1,71 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Fri, 11 Jun 2021 12:10:54 +0200
+Subject: [PATCH] Revert "templates: Properly disable the os-prober by default"
+
+This reverts commit 54e0a1bbf1e9106901a557195bb35e5e20fb3925.
+---
+ util/grub-mkconfig.in       | 5 +----
+ util/grub.d/30_os-prober.in | 8 ++++----
+ 2 files changed, 5 insertions(+), 8 deletions(-)
+
+diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in
+index f8cbb8d7a2b..d3e879b8e5c 100644
+--- a/util/grub-mkconfig.in
++++ b/util/grub-mkconfig.in
+@@ -140,9 +140,6 @@ GRUB_DEVICE_PARTUUID="`${grub_probe} --device ${GRUB_DEVICE} --target=partuuid 2
+ GRUB_DEVICE_BOOT="`${grub_probe} --target=device /boot`"
+ GRUB_DEVICE_BOOT_UUID="`${grub_probe} --device ${GRUB_DEVICE_BOOT} --target=fs_uuid 2> /dev/null`" || true
+ 
+-# Disable os-prober by default due to security reasons.
+-GRUB_DISABLE_OS_PROBER="true"
+-
+ # Filesystem for the device containing our userland.  Used for stuff like
+ # choosing Hurd filesystem module.
+ GRUB_FS="`${grub_probe} --device ${GRUB_DEVICE} --target=fs 2> /dev/null || echo unknown`"
+@@ -204,7 +201,6 @@ export GRUB_DEVICE \
+   GRUB_DEVICE_PARTUUID \
+   GRUB_DEVICE_BOOT \
+   GRUB_DEVICE_BOOT_UUID \
+-  GRUB_DISABLE_OS_PROBER \
+   GRUB_FS \
+   GRUB_FONT \
+   GRUB_PRELOAD_MODULES \
+@@ -246,6 +242,7 @@ export GRUB_DEFAULT \
+   GRUB_BACKGROUND \
+   GRUB_THEME \
+   GRUB_GFXPAYLOAD_LINUX \
++  GRUB_DISABLE_OS_PROBER \
+   GRUB_INIT_TUNE \
+   GRUB_SAVEDEFAULT \
+   GRUB_ENABLE_CRYPTODISK \
+diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in
+index 94622481284..80685b15f4d 100644
+--- a/util/grub.d/30_os-prober.in
++++ b/util/grub.d/30_os-prober.in
+@@ -26,8 +26,8 @@ export TEXTDOMAINDIR="@localedir@"
+ 
+ . "$pkgdatadir/grub-mkconfig_lib"
+ 
+-if [ "x${GRUB_DISABLE_OS_PROBER}" = "xtrue" ]; then
+-  grub_warn "$(gettext_printf "os-prober will not be executed to detect other bootable partitions.\nSystems on them will not be added to the GRUB boot configuration.\nCheck GRUB_DISABLE_OS_PROBER documentation entry.")"
++if [ "x${GRUB_DISABLE_OS_PROBER}" = "xfalse" ]; then
++  gettext_printf "os-prober will not be executed to detect other bootable partitions.\nSystems on them will not be added to the GRUB boot configuration.\nCheck GRUB_DISABLE_OS_PROBER documentation entry.\n"
+   exit 0
+ fi
+ 
+@@ -36,12 +36,12 @@ if ! command -v os-prober > /dev/null || ! command -v linux-boot-prober > /dev/n
+   exit 0
+ fi
+ 
+-grub_warn "$(gettext_printf "os-prober will be executed to detect other bootable partitions.\nIt's output will be used to detect bootable binaries on them and create new boot entries.")"
+-
+ OSPROBED="`os-prober | tr ' ' '^' | paste -s -d ' '`"
+ if [ -z "${OSPROBED}" ] ; then
+   # empty os-prober output, nothing doing
+   exit 0
++else
++  grub_warn "$(gettext_printf "os-prober was executed to detect other bootable partitions.\nIt's output will be used to detect bootable binaries on them and create new boot entries.")"
+ fi
+ 
+ osx_entry() {
diff --git a/SOURCES/0003-Revert-templates-Disable-the-os-prober-by-default.patch b/SOURCES/0003-Revert-templates-Disable-the-os-prober-by-default.patch
new file mode 100644
index 0000000..68cacfc
--- /dev/null
+++ b/SOURCES/0003-Revert-templates-Disable-the-os-prober-by-default.patch
@@ -0,0 +1,70 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Fri, 11 Jun 2021 12:10:58 +0200
+Subject: [PATCH] Revert "templates: Disable the os-prober by default"
+
+This reverts commit e346414725a70e5c74ee87ca14e580c66f517666.
+---
+ docs/grub.texi              | 18 ++++++++----------
+ util/grub.d/30_os-prober.in |  5 +----
+ 2 files changed, 9 insertions(+), 14 deletions(-)
+
+diff --git a/docs/grub.texi b/docs/grub.texi
+index f8b4b3b21a7..69f08d289f9 100644
+--- a/docs/grub.texi
++++ b/docs/grub.texi
+@@ -1519,13 +1519,10 @@ boot sequence.  If you have problems, set this option to @samp{text} and
+ GRUB will tell Linux to boot in normal text mode.
+ 
+ @item GRUB_DISABLE_OS_PROBER
+-The @command{grub-mkconfig} has a feature to use the external
+-@command{os-prober} program to discover other operating systems installed on
+-the same machine and generate appropriate menu entries for them. It is disabled
+-by default since automatic and silent execution of @command{os-prober}, and
+-creating boot entries based on that data, is a potential attack vector. Set
+-this option to @samp{false} to enable this feature in the
+-@command{grub-mkconfig} command.
++Normally, @command{grub-mkconfig} will try to use the external
++@command{os-prober} program, if installed, to discover other operating
++systems installed on the same system and generate appropriate menu entries
++for them.  Set this option to @samp{true} to disable this.
+ 
+ @item GRUB_OS_PROBER_SKIP_LIST
+ List of space-separated FS UUIDs of filesystems to be ignored from os-prober
+@@ -1853,9 +1850,10 @@ than zero; otherwise 0.
+ @section Multi-boot manual config
+ 
+ Currently autogenerating config files for multi-boot environments depends on
+-os-prober and has several shortcomings. Due to that it is disabled by default.
+-It is advised to use the power of GRUB syntax and do it yourself. A possible
+-configuration is detailed here, feel free to adjust to your needs.
++os-prober and has several shortcomings. While fixing it is scheduled for the
++next release, meanwhile you can make use of the power of GRUB syntax and do it
++yourself. A possible configuration is detailed here, feel free to adjust to your
++needs.
+ 
+ First create a separate GRUB partition, big enough to hold GRUB. Some of the
+ following entries show how to load OS installer images from this same partition,
+diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in
+index 80685b15f4d..1b91c102f35 100644
+--- a/util/grub.d/30_os-prober.in
++++ b/util/grub.d/30_os-prober.in
+@@ -26,8 +26,7 @@ export TEXTDOMAINDIR="@localedir@"
+ 
+ . "$pkgdatadir/grub-mkconfig_lib"
+ 
+-if [ "x${GRUB_DISABLE_OS_PROBER}" = "xfalse" ]; then
+-  gettext_printf "os-prober will not be executed to detect other bootable partitions.\nSystems on them will not be added to the GRUB boot configuration.\nCheck GRUB_DISABLE_OS_PROBER documentation entry.\n"
++if [ "x${GRUB_DISABLE_OS_PROBER}" = "xtrue" ]; then
+   exit 0
+ fi
+ 
+@@ -40,8 +39,6 @@ OSPROBED="`os-prober | tr ' ' '^' | paste -s -d ' '`"
+ if [ -z "${OSPROBED}" ] ; then
+   # empty os-prober output, nothing doing
+   exit 0
+-else
+-  grub_warn "$(gettext_printf "os-prober was executed to detect other bootable partitions.\nIt's output will be used to detect bootable binaries on them and create new boot entries.")"
+ fi
+ 
+ osx_entry() {
diff --git a/SOURCES/0004-Add-support-for-Linux-EFI-stub-loading.patch b/SOURCES/0004-Add-support-for-Linux-EFI-stub-loading.patch
new file mode 100644
index 0000000..73d231a
--- /dev/null
+++ b/SOURCES/0004-Add-support-for-Linux-EFI-stub-loading.patch
@@ -0,0 +1,983 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Matthew Garrett <mjg@redhat.com>
+Date: Tue, 10 Jul 2012 11:58:52 -0400
+Subject: [PATCH] Add support for Linux EFI stub loading.
+
+Also:
+
+commit 71c843745f22f81e16d259e2e19c99bf3c1855c1
+Author: Colin Watson <cjwatson@ubuntu.com>
+Date:   Tue Oct 23 10:40:49 2012 -0400
+
+Don't allow insmod when secure boot is enabled.
+
+Hi,
+
+Fedora's patch to forbid insmod in UEFI Secure Boot environments is fine
+as far as it goes.  However, the insmod command is not the only way that
+modules can be loaded.  In particular, the 'normal' command, which
+implements the usual GRUB menu and the fully-featured command prompt,
+will implicitly load commands not currently loaded into memory.  This
+permits trivial Secure Boot violations by writing commands implementing
+whatever you want to do and pointing $prefix at the malicious code.
+
+I'm currently test-building this patch (replacing your current
+grub-2.00-no-insmod-on-sb.patch), but this should be more correct.  It
+moves the check into grub_dl_load_file.
+---
+ grub-core/Makefile.core.def       |  16 +-
+ grub-core/kern/dl.c               |  21 +++
+ grub-core/kern/efi/efi.c          |  28 ++++
+ grub-core/kern/efi/mm.c           |  32 ++++
+ grub-core/loader/arm64/linux.c    | 118 +++++++-------
+ grub-core/loader/arm64/xen_boot.c |   1 -
+ grub-core/loader/efi/linux.c      |  70 ++++++++
+ grub-core/loader/i386/efi/linux.c | 335 ++++++++++++++++++++++++++++++++++++++
+ grub-core/loader/i386/pc/linux.c  |  10 +-
+ include/grub/arm/linux.h          |   9 +
+ include/grub/arm64/linux.h        |   9 +
+ include/grub/efi/efi.h            |   7 +-
+ include/grub/efi/linux.h          |  31 ++++
+ 13 files changed, 618 insertions(+), 69 deletions(-)
+ create mode 100644 grub-core/loader/efi/linux.c
+ create mode 100644 grub-core/loader/i386/efi/linux.c
+ create mode 100644 include/grub/efi/linux.h
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 8022e1c0a79..45d3edaa4dc 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -1734,13 +1734,6 @@ module = {
+   enable = i386_pc;
+ };
+ 
+-
+-module = {
+-  name = linux16;
+-  common = loader/i386/pc/linux.c;
+-  enable = x86;
+-};
+-
+ module = {
+   name = ntldr;
+   i386_pc = loader/i386/pc/ntldr.c;
+@@ -1796,7 +1789,9 @@ module = {
+ 
+ module = {
+   name = linux;
+-  x86 = loader/i386/linux.c;
++  i386_pc = loader/i386/pc/linux.c;
++  x86_64_efi = loader/i386/efi/linux.c;
++  i386_efi = loader/i386/efi/linux.c;
+   i386_xen_pvh = loader/i386/linux.c;
+   xen = loader/i386/xen.c;
+   i386_pc = lib/i386/pc/vesa_modes_table.c;
+@@ -1811,9 +1806,14 @@ module = {
+   arm64 = loader/arm64/linux.c;
+   riscv32 = loader/riscv/linux.c;
+   riscv64 = loader/riscv/linux.c;
++  emu = loader/emu/linux.c;
++  fdt = lib/fdt.c;
++
+   common = loader/linux.c;
+   common = lib/cmdline.c;
+   enable = noemu;
++
++  efi = loader/efi/linux.c;
+ };
+ 
+ module = {
+diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
+index 48f8a79073d..b7149370950 100644
+--- a/grub-core/kern/dl.c
++++ b/grub-core/kern/dl.c
+@@ -38,6 +38,14 @@
+ #define GRUB_MODULES_MACHINE_READONLY
+ #endif
+ 
++#ifdef GRUB_MACHINE_EMU
++#include <sys/mman.h>
++#endif
++
++#ifdef GRUB_MACHINE_EFI
++#include <grub/efi/efi.h>
++#endif
++
+ 
+ 
+ #pragma GCC diagnostic ignored "-Wcast-align"
+@@ -695,6 +703,19 @@ grub_dl_load_file (const char *filename)
+   void *core = 0;
+   grub_dl_t mod = 0;
+ 
++#ifdef GRUB_MACHINE_EFI
++  if (grub_efi_secure_boot ())
++    {
++#if 0
++      /* This is an error, but grub2-mkconfig still generates a pile of
++       * insmod commands, so emitting it would be mostly just obnoxious. */
++      grub_error (GRUB_ERR_ACCESS_DENIED,
++		  "Secure Boot forbids loading module from %s", filename);
++#endif
++      return 0;
++    }
++#endif
++
+   grub_boot_time ("Loading module %s", filename);
+ 
+   file = grub_file_open (filename, GRUB_FILE_TYPE_GRUB_MODULE);
+diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
+index 8cff7be0289..35b8f670602 100644
+--- a/grub-core/kern/efi/efi.c
++++ b/grub-core/kern/efi/efi.c
+@@ -286,6 +286,34 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid,
+   return grub_efi_get_variable_with_attributes (var, guid, datasize_out, data_out, NULL);
+ }
+ 
++grub_efi_boolean_t
++grub_efi_secure_boot (void)
++{
++  grub_efi_guid_t efi_var_guid = GRUB_EFI_GLOBAL_VARIABLE_GUID;
++  grub_size_t datasize;
++  char *secure_boot = NULL;
++  char *setup_mode = NULL;
++  grub_efi_boolean_t ret = 0;
++
++  secure_boot = grub_efi_get_variable("SecureBoot", &efi_var_guid, &datasize);
++
++  if (datasize != 1 || !secure_boot)
++    goto out;
++
++  setup_mode = grub_efi_get_variable("SetupMode", &efi_var_guid, &datasize);
++
++  if (datasize != 1 || !setup_mode)
++    goto out;
++
++  if (*secure_boot && !*setup_mode)
++    ret = 1;
++
++ out:
++  grub_free (secure_boot);
++  grub_free (setup_mode);
++  return ret;
++}
++
+ #pragma GCC diagnostic ignored "-Wcast-align"
+ 
+ /* Search the mods section from the PE32/PE32+ image. This code uses
+diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
+index 9838fb2f50d..f6aef0ef649 100644
+--- a/grub-core/kern/efi/mm.c
++++ b/grub-core/kern/efi/mm.c
+@@ -113,6 +113,38 @@ grub_efi_drop_alloc (grub_efi_physical_address_t address,
+     }
+ }
+ 
++/* Allocate pages below a specified address */
++void *
++grub_efi_allocate_pages_max (grub_efi_physical_address_t max,
++			     grub_efi_uintn_t pages)
++{
++  grub_efi_status_t status;
++  grub_efi_boot_services_t *b;
++  grub_efi_physical_address_t address = max;
++
++  if (max > 0xffffffff)
++    return 0;
++
++  b = grub_efi_system_table->boot_services;
++  status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS, GRUB_EFI_LOADER_DATA, pages, &address);
++
++  if (status != GRUB_EFI_SUCCESS)
++    return 0;
++
++  if (address == 0)
++    {
++      /* Uggh, the address 0 was allocated... This is too annoying,
++	 so reallocate another one.  */
++      address = max;
++      status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS, GRUB_EFI_LOADER_DATA, pages, &address);
++      grub_efi_free_pages (0, pages);
++      if (status != GRUB_EFI_SUCCESS)
++	return 0;
++    }
++
++  return (void *) ((grub_addr_t) address);
++}
++
+ /* Allocate pages. Return the pointer to the first of allocated pages.  */
+ void *
+ grub_efi_allocate_pages_real (grub_efi_physical_address_t address,
+diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
+index ef3e9f9444c..a312c668685 100644
+--- a/grub-core/loader/arm64/linux.c
++++ b/grub-core/loader/arm64/linux.c
+@@ -29,6 +29,7 @@
+ #include <grub/efi/efi.h>
+ #include <grub/efi/fdtload.h>
+ #include <grub/efi/memory.h>
++#include <grub/efi/linux.h>
+ #include <grub/efi/pe32.h>
+ #include <grub/i18n.h>
+ #include <grub/lib/cmdline.h>
+@@ -41,6 +42,7 @@ static int loaded;
+ 
+ static void *kernel_addr;
+ static grub_uint64_t kernel_size;
++static grub_uint32_t handover_offset;
+ 
+ static char *linux_args;
+ static grub_uint32_t cmdline_size;
+@@ -67,7 +69,8 @@ grub_arch_efi_linux_check_image (struct linux_arch_kernel_header * lh)
+ static grub_err_t
+ finalize_params_linux (void)
+ {
+-  int node, retval;
++  grub_efi_loaded_image_t *loaded_image = NULL;
++  int node, retval, len;
+ 
+   void *fdt;
+ 
+@@ -102,79 +105,70 @@ finalize_params_linux (void)
+   if (grub_fdt_install() != GRUB_ERR_NONE)
+     goto failure;
+ 
+-  return GRUB_ERR_NONE;
+-
+-failure:
+-  grub_fdt_unload();
+-  return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT");
+-}
+-
+-grub_err_t
+-grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args)
+-{
+-  grub_efi_memory_mapped_device_path_t *mempath;
+-  grub_efi_handle_t image_handle;
+-  grub_efi_boot_services_t *b;
+-  grub_efi_status_t status;
+-  grub_efi_loaded_image_t *loaded_image;
+-  int len;
+-
+-  mempath = grub_malloc (2 * sizeof (grub_efi_memory_mapped_device_path_t));
+-  if (!mempath)
+-    return grub_errno;
+-
+-  mempath[0].header.type = GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE;
+-  mempath[0].header.subtype = GRUB_EFI_MEMORY_MAPPED_DEVICE_PATH_SUBTYPE;
+-  mempath[0].header.length = grub_cpu_to_le16_compile_time (sizeof (*mempath));
+-  mempath[0].memory_type = GRUB_EFI_LOADER_DATA;
+-  mempath[0].start_address = addr;
+-  mempath[0].end_address = addr + size;
+-
+-  mempath[1].header.type = GRUB_EFI_END_DEVICE_PATH_TYPE;
+-  mempath[1].header.subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
+-  mempath[1].header.length = sizeof (grub_efi_device_path_t);
+-
+-  b = grub_efi_system_table->boot_services;
+-  status = b->load_image (0, grub_efi_image_handle,
+-			  (grub_efi_device_path_t *) mempath,
+-			  (void *) addr, size, &image_handle);
+-  if (status != GRUB_EFI_SUCCESS)
+-    return grub_error (GRUB_ERR_BAD_OS, "cannot load image");
+-
+-  grub_dprintf ("linux", "linux command line: '%s'\n", args);
++  grub_dprintf ("linux", "Installed/updated FDT configuration table @ %p\n",
++                fdt);
+ 
+   /* Convert command line to UCS-2 */
+-  loaded_image = grub_efi_get_loaded_image (image_handle);
++  loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle);
++  if (!loaded_image)
++    goto failure;
++
+   loaded_image->load_options_size = len =
+-    (grub_strlen (args) + 1) * sizeof (grub_efi_char16_t);
++    (grub_strlen (linux_args) + 1) * sizeof (grub_efi_char16_t);
+   loaded_image->load_options =
+     grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
+   if (!loaded_image->load_options)
+-    return grub_errno;
++    return grub_error(GRUB_ERR_BAD_OS, "failed to create kernel parameters");
+ 
+   loaded_image->load_options_size =
+     2 * grub_utf8_to_utf16 (loaded_image->load_options, len,
+-			    (grub_uint8_t *) args, len, NULL);
++                           (grub_uint8_t *) linux_args, len, NULL);
+ 
+-  grub_dprintf ("linux", "starting image %p\n", image_handle);
+-  status = b->start_image (image_handle, 0, NULL);
++  return GRUB_ERR_NONE;
+ 
+-  /* When successful, not reached */
+-  b->unload_image (image_handle);
+-  grub_efi_free_pages ((grub_addr_t) loaded_image->load_options,
+-		       GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
++failure:
++  grub_fdt_unload();
++  return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT");
++}
+ 
+-  return grub_errno;
++static void
++free_params (void)
++{
++  grub_efi_loaded_image_t *loaded_image = NULL;
++
++  loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle);
++  if (loaded_image)
++    {
++      if (loaded_image->load_options)
++       grub_efi_free_pages ((grub_efi_physical_address_t)(grub_efi_uintn_t)loaded_image->load_options,
++                            GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
++      loaded_image->load_options = NULL;
++      loaded_image->load_options_size = 0;
++    }
++}
++
++grub_err_t
++grub_arch_efi_linux_boot_image (grub_addr_t addr, char *args)
++{
++  grub_err_t retval;
++
++  retval = finalize_params_linux ();
++  if (retval != GRUB_ERR_NONE)
++    return grub_errno;
++
++  grub_dprintf ("linux", "linux command line: '%s'\n", args);
++
++  retval = grub_efi_linux_boot ((char *)addr, handover_offset, (void *)addr);
++
++  /* Never reached... */
++  free_params();
++  return retval;
+ }
+ 
+ static grub_err_t
+ grub_linux_boot (void)
+ {
+-  if (finalize_params_linux () != GRUB_ERR_NONE)
+-    return grub_errno;
+-
+-  return (grub_arch_efi_linux_boot_image((grub_addr_t)kernel_addr,
+-                                          kernel_size, linux_args));
++  return (grub_arch_efi_linux_boot_image((grub_addr_t)kernel_addr, linux_args));
+ }
+ 
+ static grub_err_t
+@@ -288,6 +282,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ {
+   grub_file_t file = 0;
+   struct linux_arch_kernel_header lh;
++  struct grub_armxx_linux_pe_header *pe;
+   grub_err_t err;
+ 
+   grub_dl_ref (my_mod);
+@@ -333,6 +328,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 
+   grub_dprintf ("linux", "kernel @ %p\n", kernel_addr);
+ 
++  if (!grub_linuxefi_secure_validate (kernel_addr, kernel_size))
++    {
++      grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), argv[0]);
++      goto fail;
++    }
++
++  pe = (void *)((unsigned long)kernel_addr + lh.hdr_offset);
++  handover_offset = pe->opt.entry_addr;
++
+   cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE);
+   linux_args = grub_malloc (cmdline_size);
+   if (!linux_args)
+diff --git a/grub-core/loader/arm64/xen_boot.c b/grub-core/loader/arm64/xen_boot.c
+index 22cc25eccd9..d9b7a9ba400 100644
+--- a/grub-core/loader/arm64/xen_boot.c
++++ b/grub-core/loader/arm64/xen_boot.c
+@@ -266,7 +266,6 @@ xen_boot (void)
+     return err;
+ 
+   return grub_arch_efi_linux_boot_image (xen_hypervisor->start,
+-					  xen_hypervisor->size,
+ 					  xen_hypervisor->cmdline);
+ }
+ 
+diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c
+new file mode 100644
+index 00000000000..c24202a5dd1
+--- /dev/null
++++ b/grub-core/loader/efi/linux.c
+@@ -0,0 +1,70 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2014 Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/err.h>
++#include <grub/mm.h>
++#include <grub/types.h>
++#include <grub/cpu/linux.h>
++#include <grub/efi/efi.h>
++#include <grub/efi/pe32.h>
++#include <grub/efi/linux.h>
++
++#define SHIM_LOCK_GUID \
++ { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }
++
++struct grub_efi_shim_lock
++{
++  grub_efi_status_t (*verify) (void *buffer, grub_uint32_t size);
++};
++typedef struct grub_efi_shim_lock grub_efi_shim_lock_t;
++
++grub_efi_boolean_t
++grub_linuxefi_secure_validate (void *data, grub_uint32_t size)
++{
++  grub_efi_guid_t guid = SHIM_LOCK_GUID;
++  grub_efi_shim_lock_t *shim_lock;
++
++  shim_lock = grub_efi_locate_protocol(&guid, NULL);
++
++  if (!shim_lock)
++    return 1;
++
++  if (shim_lock->verify(data, size) == GRUB_EFI_SUCCESS)
++    return 1;
++
++  return 0;
++}
++
++#pragma GCC diagnostic push
++#pragma GCC diagnostic ignored "-Wcast-align"
++
++typedef void (*handover_func) (void *, grub_efi_system_table_t *, void *);
++
++grub_err_t
++grub_efi_linux_boot (void *kernel_addr, grub_off_t offset,
++		     void *kernel_params)
++{
++  handover_func hf;
++
++  hf = (handover_func)((char *)kernel_addr + offset);
++  hf (grub_efi_image_handle, grub_efi_system_table, kernel_params);
++
++  return GRUB_ERR_BUG;
++}
++
++#pragma GCC diagnostic pop
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+new file mode 100644
+index 00000000000..bb2616a8092
+--- /dev/null
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -0,0 +1,335 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2012  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/loader.h>
++#include <grub/file.h>
++#include <grub/err.h>
++#include <grub/types.h>
++#include <grub/mm.h>
++#include <grub/cpu/linux.h>
++#include <grub/command.h>
++#include <grub/i18n.h>
++#include <grub/lib/cmdline.h>
++#include <grub/efi/efi.h>
++#include <grub/efi/linux.h>
++
++GRUB_MOD_LICENSE ("GPLv3+");
++
++static grub_dl_t my_mod;
++static int loaded;
++static void *kernel_mem;
++static grub_uint64_t kernel_size;
++static grub_uint8_t *initrd_mem;
++static grub_uint32_t handover_offset;
++struct linux_kernel_params *params;
++static char *linux_cmdline;
++
++#define BYTES_TO_PAGES(bytes)   (((bytes) + 0xfff) >> 12)
++
++static grub_err_t
++grub_linuxefi_boot (void)
++{
++  int offset = 0;
++
++#ifdef __x86_64__
++  offset = 512;
++#endif
++  asm volatile ("cli");
++
++  return grub_efi_linux_boot ((char *)kernel_mem, handover_offset + offset,
++			      params);
++}
++
++static grub_err_t
++grub_linuxefi_unload (void)
++{
++  grub_dl_unref (my_mod);
++  loaded = 0;
++  if (initrd_mem)
++    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)initrd_mem,
++			 BYTES_TO_PAGES(params->ramdisk_size));
++  if (linux_cmdline)
++    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)
++			 linux_cmdline,
++			 BYTES_TO_PAGES(params->cmdline_size + 1));
++  if (kernel_mem)
++    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)kernel_mem,
++			 BYTES_TO_PAGES(kernel_size));
++  if (params)
++    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)params,
++			 BYTES_TO_PAGES(16384));
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
++                 int argc, char *argv[])
++{
++  grub_file_t *files = 0;
++  int i, nfiles = 0;
++  grub_size_t size = 0;
++  grub_uint8_t *ptr;
++
++  if (argc == 0)
++    {
++      grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
++      goto fail;
++    }
++
++  if (!loaded)
++    {
++      grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
++      goto fail;
++    }
++
++  files = grub_zalloc (argc * sizeof (files[0]));
++  if (!files)
++    goto fail;
++
++  for (i = 0; i < argc; i++)
++    {
++      files[i] = grub_file_open (argv[i], GRUB_FILE_TYPE_LINUX_INITRD | GRUB_FILE_TYPE_NO_DECOMPRESS);
++      if (! files[i])
++        goto fail;
++      nfiles++;
++      size += ALIGN_UP (grub_file_size (files[i]), 4);
++    }
++
++  initrd_mem = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(size));
++  if (!initrd_mem)
++    {
++      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate initrd"));
++      goto fail;
++    }
++
++  params->ramdisk_size = size;
++  params->ramdisk_image = (grub_uint32_t)(grub_addr_t) initrd_mem;
++
++  ptr = initrd_mem;
++
++  for (i = 0; i < nfiles; i++)
++    {
++      grub_ssize_t cursize = grub_file_size (files[i]);
++      if (grub_file_read (files[i], ptr, cursize) != cursize)
++        {
++          if (!grub_errno)
++            grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
++                        argv[i]);
++          goto fail;
++        }
++      ptr += cursize;
++      grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4));
++      ptr += ALIGN_UP_OVERHEAD (cursize, 4);
++    }
++
++  params->ramdisk_size = size;
++
++ fail:
++  for (i = 0; i < nfiles; i++)
++    grub_file_close (files[i]);
++  grub_free (files);
++
++  if (initrd_mem && grub_errno)
++    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)initrd_mem,
++			 BYTES_TO_PAGES(size));
++
++  return grub_errno;
++}
++
++static grub_err_t
++grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
++		int argc, char *argv[])
++{
++  grub_file_t file = 0;
++  struct linux_i386_kernel_header lh;
++  grub_ssize_t len, start, filelen;
++  void *kernel = NULL;
++
++  grub_dl_ref (my_mod);
++
++  if (argc == 0)
++    {
++      grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
++      goto fail;
++    }
++
++  file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
++  if (! file)
++    goto fail;
++
++  filelen = grub_file_size (file);
++
++  kernel = grub_malloc(filelen);
++
++  if (!kernel)
++    {
++      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer"));
++      goto fail;
++    }
++
++  if (grub_file_read (file, kernel, filelen) != filelen)
++    {
++      grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"), argv[0]);
++      goto fail;
++    }
++
++  if (! grub_linuxefi_secure_validate (kernel, filelen))
++    {
++      grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"),
++		  argv[0]);
++      goto fail;
++    }
++
++  params = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(16384));
++
++  if (! params)
++    {
++      grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters");
++      goto fail;
++    }
++
++  grub_memset (params, 0, 16384);
++
++  grub_memcpy (&lh, kernel, sizeof (lh));
++
++  if (lh.boot_flag != grub_cpu_to_le16 (0xaa55))
++    {
++      grub_error (GRUB_ERR_BAD_OS, N_("invalid magic number"));
++      goto fail;
++    }
++
++  if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
++    {
++      grub_error (GRUB_ERR_BAD_OS, N_("too many setup sectors"));
++      goto fail;
++    }
++
++  if (lh.version < grub_cpu_to_le16 (0x020b))
++    {
++      grub_error (GRUB_ERR_BAD_OS, N_("kernel too old"));
++      goto fail;
++    }
++
++  if (!lh.handover_offset)
++    {
++      grub_error (GRUB_ERR_BAD_OS, N_("kernel doesn't support EFI handover"));
++      goto fail;
++    }
++
++  grub_dprintf ("linux", "setting up cmdline\n");
++  linux_cmdline = grub_efi_allocate_pages_max(0x3fffffff,
++					 BYTES_TO_PAGES(lh.cmdline_size + 1));
++
++  if (!linux_cmdline)
++    {
++      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate cmdline"));
++      goto fail;
++    }
++
++  grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
++  grub_create_loader_cmdline (argc, argv,
++                              linux_cmdline + sizeof (LINUX_IMAGE) - 1,
++			      lh.cmdline_size - (sizeof (LINUX_IMAGE) - 1),
++			      GRUB_VERIFY_KERNEL_CMDLINE);
++
++  lh.cmd_line_ptr = (grub_uint32_t)(grub_addr_t)linux_cmdline;
++
++  handover_offset = lh.handover_offset;
++
++  start = (lh.setup_sects + 1) * 512;
++  len = grub_file_size(file) - start;
++
++  kernel_mem = grub_efi_allocate_pages_max(lh.pref_address,
++					   BYTES_TO_PAGES(lh.init_size));
++
++  if (!kernel_mem)
++    kernel_mem = grub_efi_allocate_pages_max(0x3fffffff,
++					     BYTES_TO_PAGES(lh.init_size));
++
++  if (!kernel_mem)
++    {
++      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate kernel"));
++      goto fail;
++    }
++
++  grub_memcpy (kernel_mem, (char *)kernel + start, len);
++  grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0);
++  loaded=1;
++
++  lh.code32_start = (grub_uint32_t)(grub_uint64_t) kernel_mem;
++  grub_memcpy (params, &lh, 2 * 512);
++
++  params->type_of_loader = 0x21;
++
++ fail:
++
++  if (file)
++    grub_file_close (file);
++
++  if (kernel)
++    grub_free (kernel);
++
++  if (grub_errno != GRUB_ERR_NONE)
++    {
++      grub_dl_unref (my_mod);
++      loaded = 0;
++    }
++
++  if (linux_cmdline && !loaded)
++    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)
++			 linux_cmdline,
++			 BYTES_TO_PAGES(lh.cmdline_size + 1));
++
++  if (kernel_mem && !loaded)
++    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)kernel_mem,
++			 BYTES_TO_PAGES(kernel_size));
++
++  if (params && !loaded)
++    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)params,
++			 BYTES_TO_PAGES(16384));
++
++  return grub_errno;
++}
++
++static grub_command_t cmd_linux, cmd_initrd;
++static grub_command_t cmd_linuxefi, cmd_initrdefi;
++
++GRUB_MOD_INIT(linux)
++{
++  cmd_linux =
++    grub_register_command ("linux", grub_cmd_linux,
++                           0, N_("Load Linux."));
++  cmd_linuxefi =
++    grub_register_command ("linuxefi", grub_cmd_linux,
++                           0, N_("Load Linux."));
++  cmd_initrd =
++    grub_register_command ("initrd", grub_cmd_initrd,
++                           0, N_("Load initrd."));
++  cmd_initrdefi =
++    grub_register_command ("initrdefi", grub_cmd_initrd,
++                           0, N_("Load initrd."));
++  my_mod = mod;
++}
++
++GRUB_MOD_FINI(linux)
++{
++  grub_unregister_command (cmd_linux);
++  grub_unregister_command (cmd_linuxefi);
++  grub_unregister_command (cmd_initrd);
++  grub_unregister_command (cmd_initrdefi);
++}
+diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c
+index 2a299520160..8be4c3b3f48 100644
+--- a/grub-core/loader/i386/pc/linux.c
++++ b/grub-core/loader/i386/pc/linux.c
+@@ -474,14 +474,20 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+   return grub_errno;
+ }
+ 
+-static grub_command_t cmd_linux, cmd_initrd;
++static grub_command_t cmd_linux, cmd_linux16, cmd_initrd, cmd_initrd16;
+ 
+ GRUB_MOD_INIT(linux16)
+ {
+   cmd_linux =
++    grub_register_command ("linux", grub_cmd_linux,
++			   0, N_("Load Linux."));
++  cmd_linux16 =
+     grub_register_command ("linux16", grub_cmd_linux,
+ 			   0, N_("Load Linux."));
+   cmd_initrd =
++    grub_register_command ("initrd", grub_cmd_initrd,
++			   0, N_("Load initrd."));
++  cmd_initrd16 =
+     grub_register_command ("initrd16", grub_cmd_initrd,
+ 			   0, N_("Load initrd."));
+   my_mod = mod;
+@@ -490,5 +496,7 @@ GRUB_MOD_INIT(linux16)
+ GRUB_MOD_FINI(linux16)
+ {
+   grub_unregister_command (cmd_linux);
++  grub_unregister_command (cmd_linux16);
+   grub_unregister_command (cmd_initrd);
++  grub_unregister_command (cmd_initrd16);
+ }
+diff --git a/include/grub/arm/linux.h b/include/grub/arm/linux.h
+index bcd5a7eb186..b582f67f661 100644
+--- a/include/grub/arm/linux.h
++++ b/include/grub/arm/linux.h
+@@ -20,6 +20,7 @@
+ #ifndef GRUB_ARM_LINUX_HEADER
+ #define GRUB_ARM_LINUX_HEADER 1
+ 
++#include <grub/efi/pe32.h>
+ #include "system.h"
+ 
+ #define GRUB_LINUX_ARM_MAGIC_SIGNATURE 0x016f2818
+@@ -34,9 +35,17 @@ struct linux_arm_kernel_header {
+   grub_uint32_t hdr_offset;
+ };
+ 
++struct grub_arm_linux_pe_header
++{
++  grub_uint32_t magic;
++  struct grub_pe32_coff_header coff;
++  struct grub_pe32_optional_header opt;
++};
++
+ #if defined(__arm__)
+ # define GRUB_LINUX_ARMXX_MAGIC_SIGNATURE GRUB_LINUX_ARM_MAGIC_SIGNATURE
+ # define linux_arch_kernel_header linux_arm_kernel_header
++# define grub_armxx_linux_pe_header grub_arm_linux_pe_header
+ #endif
+ 
+ #if defined GRUB_MACHINE_UBOOT
+diff --git a/include/grub/arm64/linux.h b/include/grub/arm64/linux.h
+index 7e22b4ab699..ea030312df3 100644
+--- a/include/grub/arm64/linux.h
++++ b/include/grub/arm64/linux.h
+@@ -19,6 +19,7 @@
+ #ifndef GRUB_ARM64_LINUX_HEADER
+ #define GRUB_ARM64_LINUX_HEADER 1
+ 
++#include <grub/efi/pe32.h>
+ #include <grub/types.h>
+ 
+ #define GRUB_LINUX_ARM64_MAGIC_SIGNATURE 0x644d5241 /* 'ARM\x64' */
+@@ -38,9 +39,17 @@ struct linux_arm64_kernel_header
+   grub_uint32_t hdr_offset;	/* Offset of PE/COFF header */
+ };
+ 
++struct grub_arm64_linux_pe_header
++{
++  grub_uint32_t magic;
++  struct grub_pe32_coff_header coff;
++  struct grub_pe64_optional_header opt;
++};
++
+ #if defined(__aarch64__)
+ # define GRUB_LINUX_ARMXX_MAGIC_SIGNATURE GRUB_LINUX_ARM64_MAGIC_SIGNATURE
+ # define linux_arch_kernel_header linux_arm64_kernel_header
++# define grub_armxx_linux_pe_header grub_arm64_linux_pe_header
+ #endif
+ 
+ #endif /* ! GRUB_ARM64_LINUX_HEADER */
+diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
+index 83d958f9945..6295df85f3f 100644
+--- a/include/grub/efi/efi.h
++++ b/include/grub/efi/efi.h
+@@ -47,6 +47,9 @@ EXPORT_FUNC(grub_efi_allocate_fixed) (grub_efi_physical_address_t address,
+ 				      grub_efi_uintn_t pages);
+ void *
+ EXPORT_FUNC(grub_efi_allocate_any_pages) (grub_efi_uintn_t pages);
++void *
++EXPORT_FUNC(grub_efi_allocate_pages_max) (grub_efi_physical_address_t max,
++					  grub_efi_uintn_t pages);
+ void EXPORT_FUNC(grub_efi_free_pages) (grub_efi_physical_address_t address,
+ 				       grub_efi_uintn_t pages);
+ grub_efi_uintn_t EXPORT_FUNC(grub_efi_find_mmap_size) (void);
+@@ -88,6 +91,7 @@ EXPORT_FUNC (grub_efi_set_variable) (const char *var,
+ 				     const grub_efi_guid_t *guid,
+ 				     void *data,
+ 				     grub_size_t datasize);
++grub_efi_boolean_t EXPORT_FUNC (grub_efi_secure_boot) (void);
+ int
+ EXPORT_FUNC (grub_efi_compare_device_paths) (const grub_efi_device_path_t *dp1,
+ 					     const grub_efi_device_path_t *dp2);
+@@ -101,8 +105,7 @@ void *EXPORT_FUNC(grub_efi_get_firmware_fdt)(void);
+ grub_err_t EXPORT_FUNC(grub_efi_get_ram_base)(grub_addr_t *);
+ #include <grub/cpu/linux.h>
+ grub_err_t grub_arch_efi_linux_check_image(struct linux_arch_kernel_header *lh);
+-grub_err_t grub_arch_efi_linux_boot_image(grub_addr_t addr, grub_size_t size,
+-                                           char *args);
++grub_err_t grub_arch_efi_linux_boot_image(grub_addr_t addr, char *args);
+ #endif
+ 
+ grub_addr_t grub_efi_modules_addr (void);
+diff --git a/include/grub/efi/linux.h b/include/grub/efi/linux.h
+new file mode 100644
+index 00000000000..d9ede36773b
+--- /dev/null
++++ b/include/grub/efi/linux.h
+@@ -0,0 +1,31 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2014  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++#ifndef GRUB_EFI_LINUX_HEADER
++#define GRUB_EFI_LINUX_HEADER	1
++
++#include <grub/efi/api.h>
++#include <grub/err.h>
++#include <grub/symbol.h>
++
++grub_efi_boolean_t
++EXPORT_FUNC(grub_linuxefi_secure_validate) (void *data, grub_uint32_t size);
++grub_err_t
++EXPORT_FUNC(grub_efi_linux_boot) (void *kernel_address, grub_off_t offset,
++				  void *kernel_param);
++
++#endif /* ! GRUB_EFI_LINUX_HEADER */
diff --git a/SOURCES/0005-Rework-linux-command.patch b/SOURCES/0005-Rework-linux-command.patch
new file mode 100644
index 0000000..9954dd0
--- /dev/null
+++ b/SOURCES/0005-Rework-linux-command.patch
@@ -0,0 +1,109 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Matthew Garrett <mjg59@coreos.com>
+Date: Sun, 9 Aug 2015 16:12:39 -0700
+Subject: [PATCH] Rework linux command
+
+We want a single buffer that contains the entire kernel image in order to
+perform a TPM measurement. Allocate one and copy the entire kernel into it
+before pulling out the individual blocks later on.
+
+Signed-off-by: Matthew Garrett <mjg59@coreos.com>
+---
+ grub-core/loader/i386/linux.c | 35 +++++++++++++++++++++++------------
+ 1 file changed, 23 insertions(+), 12 deletions(-)
+
+diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c
+index 9f74a96b19a..dccf3bb3005 100644
+--- a/grub-core/loader/i386/linux.c
++++ b/grub-core/loader/i386/linux.c
+@@ -649,13 +649,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ {
+   grub_file_t file = 0;
+   struct linux_i386_kernel_header lh;
++  grub_uint8_t *linux_params_ptr;
+   grub_uint8_t setup_sects;
+-  grub_size_t real_size, prot_size, prot_file_size;
++  grub_size_t real_size, prot_size, prot_file_size, kernel_offset;
+   grub_ssize_t len;
+   int i;
+   grub_size_t align, min_align;
+   int relocatable;
+   grub_uint64_t preferred_address = GRUB_LINUX_BZIMAGE_ADDR;
++  grub_uint8_t *kernel = NULL;
+ 
+   grub_dl_ref (my_mod);
+ 
+@@ -669,7 +671,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   if (! file)
+     goto fail;
+ 
+-  if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
++  len = grub_file_size (file);
++  kernel = grub_malloc (len);
++  if (!kernel)
++    {
++      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer"));
++      goto fail;
++    }
++
++  if (grub_file_read (file, kernel, len) != len)
+     {
+       if (!grub_errno)
+ 	grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+@@ -677,6 +687,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+       goto fail;
+     }
+ 
++  grub_memcpy (&lh, kernel, sizeof (lh));
++  kernel_offset = sizeof (lh);
++
+   if (lh.boot_flag != grub_cpu_to_le16_compile_time (0xaa55))
+     {
+       grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
+@@ -784,13 +797,11 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   /* We've already read lh so there is no need to read it second time. */
+   len -= sizeof(lh);
+ 
+-  if ((len > 0) &&
+-      (grub_file_read (file, (char *) &linux_params + sizeof (lh), len) != len))
++  linux_params_ptr = (void *)&linux_params;
++  if (len > 0)
+     {
+-      if (!grub_errno)
+-	grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+-		    argv[0]);
+-      goto fail;
++      grub_memcpy (linux_params_ptr + sizeof (lh), kernel + kernel_offset, len);
++      kernel_offset += len;
+     }
+ 
+   linux_params.code32_start = prot_mode_target + lh.code32_start - GRUB_LINUX_BZIMAGE_ADDR;
+@@ -853,7 +864,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 
+   /* The other parameters are filled when booting.  */
+ 
+-  grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE);
++  kernel_offset = real_size + GRUB_DISK_SECTOR_SIZE;
+ 
+   grub_dprintf ("linux", "bzImage, setup=0x%x, size=0x%x\n",
+ 		(unsigned) real_size, (unsigned) prot_size);
+@@ -1007,9 +1018,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   }
+ 
+   len = prot_file_size;
+-  if (grub_file_read (file, prot_mode_mem, len) != len && !grub_errno)
+-    grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+-		argv[0]);
++  grub_memcpy (prot_mode_mem, kernel + kernel_offset, len);
+ 
+   if (grub_errno == GRUB_ERR_NONE)
+     {
+@@ -1020,6 +1029,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 
+  fail:
+ 
++  grub_free (kernel);
++
+   if (file)
+     grub_file_close (file);
+ 
diff --git a/SOURCES/0006-Rework-linux16-command.patch b/SOURCES/0006-Rework-linux16-command.patch
new file mode 100644
index 0000000..2c2d6f0
--- /dev/null
+++ b/SOURCES/0006-Rework-linux16-command.patch
@@ -0,0 +1,99 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Matthew Garrett <mjg59@coreos.com>
+Date: Sun, 9 Aug 2015 16:20:58 -0700
+Subject: [PATCH] Rework linux16 command
+
+We want a single buffer that contains the entire kernel image in order to
+perform a TPM measurement. Allocate one and copy the entire kernel int it
+before pulling out the individual blocks later on.
+
+Signed-off-by: Matthew Garrett <mjg59@coreos.com>
+---
+ grub-core/loader/i386/pc/linux.c | 33 +++++++++++++++++++++------------
+ 1 file changed, 21 insertions(+), 12 deletions(-)
+
+diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c
+index 8be4c3b3f48..4b1750e360e 100644
+--- a/grub-core/loader/i386/pc/linux.c
++++ b/grub-core/loader/i386/pc/linux.c
+@@ -124,13 +124,14 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   grub_file_t file = 0;
+   struct linux_i386_kernel_header lh;
+   grub_uint8_t setup_sects;
+-  grub_size_t real_size;
++  grub_size_t real_size, kernel_offset = 0;
+   grub_ssize_t len;
+   int i;
+   char *grub_linux_prot_chunk;
+   int grub_linux_is_bzimage;
+   grub_addr_t grub_linux_prot_target;
+   grub_err_t err;
++  grub_uint8_t *kernel = NULL;
+ 
+   grub_dl_ref (my_mod);
+ 
+@@ -144,7 +145,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   if (! file)
+     goto fail;
+ 
+-  if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
++  len = grub_file_size (file);
++  kernel = grub_malloc (len);
++  if (!kernel)
++    {
++      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer"));
++      goto fail;
++    }
++
++  if (grub_file_read (file, kernel, len) != len)
+     {
+       if (!grub_errno)
+ 	grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+@@ -152,6 +161,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+       goto fail;
+     }
+ 
++  grub_memcpy (&lh, kernel, sizeof (lh));
++  kernel_offset = sizeof (lh);
++
+   if (lh.boot_flag != grub_cpu_to_le16_compile_time (0xaa55))
+     {
+       grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
+@@ -320,13 +332,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   grub_memmove (grub_linux_real_chunk, &lh, sizeof (lh));
+ 
+   len = real_size + GRUB_DISK_SECTOR_SIZE - sizeof (lh);
+-  if (grub_file_read (file, grub_linux_real_chunk + sizeof (lh), len) != len)
+-    {
+-      if (!grub_errno)
+-	grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+-		    argv[0]);
+-      goto fail;
+-    }
++  grub_memcpy (grub_linux_real_chunk + sizeof (lh), kernel + kernel_offset,
++	       len);
++  kernel_offset += len;
+ 
+   if (lh.header != grub_cpu_to_le32_compile_time (GRUB_LINUX_I386_MAGIC_SIGNATURE)
+       || grub_le_to_cpu16 (lh.version) < 0x0200)
+@@ -364,9 +372,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   }
+ 
+   len = grub_linux16_prot_size;
+-  if (grub_file_read (file, grub_linux_prot_chunk, len) != len && !grub_errno)
+-    grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+-		argv[0]);
++  grub_memcpy (grub_linux_prot_chunk, kernel + kernel_offset, len);
++  kernel_offset += len;
+ 
+   if (grub_errno == GRUB_ERR_NONE)
+     {
+@@ -376,6 +383,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 
+  fail:
+ 
++  grub_free (kernel);
++
+   if (file)
+     grub_file_close (file);
+ 
diff --git a/SOURCES/0007-Add-secureboot-support-on-efi-chainloader.patch b/SOURCES/0007-Add-secureboot-support-on-efi-chainloader.patch
new file mode 100644
index 0000000..bfb5a9b
--- /dev/null
+++ b/SOURCES/0007-Add-secureboot-support-on-efi-chainloader.patch
@@ -0,0 +1,1382 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Raymund Will <rw@suse.com>
+Date: Mon, 8 Jul 2019 11:55:18 +0200
+Subject: [PATCH] Add secureboot support on efi chainloader
+
+Expand the chainloader to be able to verify the image by means of shim
+lock protocol. The PE/COFF image is loaded and relocated by the
+chainloader instead of calling LoadImage and StartImage UEFI boot
+Service as they require positive verification result from keys enrolled
+in KEK or DB. The shim will use MOK in addition to firmware enrolled
+keys to verify the image.
+
+The chainloader module could be used to load other UEFI bootloaders,
+such as xen.efi, and could be signed by any of MOK, KEK or DB.
+
+Based on https://build.opensuse.org/package/view_file/openSUSE:Factory/grub2/grub2-secureboot-chainloader.patch
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+
+Also:
+
+commit cd7a8984d4fda905877b5bfe466339100156b3bc
+Author: Raymund Will <rw@suse.com>
+Date:   Fri Apr 10 01:45:02 2015 -0400
+
+Use device part of chainloader target, if present.
+
+Otherwise chainloading is restricted to '$root', which might not even
+be readable by EFI!
+
+v1. use grub_file_get_device_name() to get device name
+
+Signed-off-by: Michael Chang <mchang@suse.com>
+Signed-off-by: Peter Jones <pjones@redhat.com>
+
+Also:
+
+commit 0872a2310a0eeac4ecfe9e1b49dd2d72ab373039
+Author: Peter Jones <pjones@redhat.com>
+Date:   Fri Jun 10 14:06:15 2016 -0400
+
+Rework even more of efi chainload so non-sb cases work right.
+
+This ensures that if shim protocol is not loaded, or is loaded but shim
+is disabled, we will fall back to a correct load method for the efi
+chain loader.
+
+Here's what I tested with this version:
+
+results                             expected    actual
+------------------------------------------------------------
+sb + enabled + shim + fedora        success     success
+sb + enabled + shim + win           success     success
+sb + enabled + grub + fedora        fail        fail
+sb + enabled + grub + win           fail        fail
+
+sb + mokdisabled + shim + fedora    success     success
+sb + mokdisabled + shim + win       success     success
+sb + mokdisabled + grub + fedora    fail        fail
+sb + mokdisabled + grub + win       fail        fail
+
+sb disabled + shim + fedora         success     success*
+sb disabled + shim + win            success     success*
+sb disabled + grub + fedora         success     success
+sb disabled + grub + win            success     success
+
+nosb + shim + fedora                success     success*
+nosb + shim + win                   success     success*
+nosb + grub + fedora                success     success
+nosb + grub + win                   success     success
+
+* for some reason shim protocol is being installed in these cases, and I
+  can't see why, but I think it may be this firmware build returning an
+  erroneous value.  But this effectively falls back to the mokdisabled
+  behavior, which works correctly, and the presence of the "grub" (i.e.
+  no shim) tests effectively tests the desired behavior here.
+
+Resolves: rhbz#1344512
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+
+Also:
+
+commit ff7b1cb7f69487870211aeb69ff4f54470fbcb58
+Author: Laszlo Ersek <lersek@redhat.com>
+Date:   Mon Nov 21 15:34:00 2016 +0100
+
+efi/chainloader: fix wrong sanity check in relocate_coff()
+
+In relocate_coff(), the relocation entries are parsed from the original
+image (not the section-wise copied image). The original image is
+pointed-to by the "orig" pointer. The current check
+
+  (void *)reloc_end < data
+
+compares the addresses of independent memory allocations. "data" is a typo
+here, it should be "orig".
+
+Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1347291
+Signed-off-by: Laszlo Ersek <lersek@redhat.com>
+Tested-by: Bogdan Costescu <bcostescu@gmail.com>
+Tested-by: Juan Orti <j.orti.alcaine@gmail.com>
+
+Also:
+
+commit ab4ba9997ad4832449e54d930fa2aac6a160d0e9
+Author: Laszlo Ersek <lersek@redhat.com>
+Date:   Wed Nov 23 06:27:09 2016 +0100
+
+efi/chainloader: truncate overlong relocation section
+
+The UEFI Windows 7 boot loader ("EFI/Microsoft/Boot/bootmgfw.efi", SHA1
+31b410e029bba87d2068c65a80b88882f9f8ea25) has inconsistent headers.
+
+Compare:
+
+> The Data Directory
+> ...
+> Entry 5 00000000000d9000 00000574 Base Relocation Directory [.reloc]
+
+Versus:
+
+> Sections:
+> Idx Name      Size      VMA               LMA               File off ...
+> ...
+>  10 .reloc    00000e22  00000000100d9000  00000000100d9000  000a1800 ...
+
+That is, the size reported by the RelocDir entry (0x574) is smaller than
+the virtual size of the .reloc section (0xe22).
+
+Quoting the grub2 debug log for the same:
+
+> chainloader.c:595: reloc_dir: 0xd9000 reloc_size: 0x00000574
+> chainloader.c:603: reloc_base: 0x7d208000 reloc_base_end: 0x7d208573
+> ...
+> chainloader.c:620: Section 10 ".reloc" at 0x7d208000..0x7d208e21
+> chainloader.c:661:  section is not reloc section?
+> chainloader.c:663:  rds: 0x00001000, vs: 00000e22
+> chainloader.c:664:  base: 0x7d208000 end: 0x7d208e21
+> chainloader.c:666:  reloc_base: 0x7d208000 reloc_base_end: 0x7d208573
+> chainloader.c:671:  Section characteristics are 42000040
+> chainloader.c:673:  Section virtual size: 00000e22
+> chainloader.c:675:  Section raw_data size: 00001000
+> chainloader.c:678:  Discarding section
+
+After hexdumping "bootmgfw.efi" and manually walking its relocation blocks
+(yes, really), I determined that the (smaller) RelocDir value is correct.
+The remaining area that extends up to the .reloc section size (== 0xe22 -
+0x574 == 0x8ae bytes) exists as zero padding in the file.
+
+This zero padding shouldn't be passed to relocate_coff() for parsing. In
+order to cope with it, split the handling of .reloc sections into the
+following branches:
+
+- original case (equal size): original behavior (--> relocation
+  attempted),
+
+- overlong .reloc section (longer than reported by RelocDir): truncate the
+  section to the RelocDir size for the purposes of relocate_coff(), and
+  attempt relocation,
+
+- .reloc section is too short, or other checks fail: original behavior
+  (--> relocation not attempted).
+
+Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1347291
+Signed-off-by: Laszlo Ersek <lersek@redhat.com>
+---
+ grub-core/kern/efi/efi.c           |  14 +-
+ grub-core/loader/arm64/linux.c     |   4 +-
+ grub-core/loader/efi/chainloader.c | 816 +++++++++++++++++++++++++++++++++----
+ grub-core/loader/efi/linux.c       |  25 +-
+ grub-core/loader/i386/efi/linux.c  |  17 +-
+ include/grub/efi/linux.h           |   2 +-
+ include/grub/efi/pe32.h            |  52 ++-
+ 7 files changed, 840 insertions(+), 90 deletions(-)
+
+diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
+index 35b8f670602..4a2259aa1c7 100644
+--- a/grub-core/kern/efi/efi.c
++++ b/grub-core/kern/efi/efi.c
+@@ -296,14 +296,20 @@ grub_efi_secure_boot (void)
+   grub_efi_boolean_t ret = 0;
+ 
+   secure_boot = grub_efi_get_variable("SecureBoot", &efi_var_guid, &datasize);
+-
+   if (datasize != 1 || !secure_boot)
+-    goto out;
++    {
++      grub_dprintf ("secureboot", "No SecureBoot variable\n");
++      goto out;
++    }
++  grub_dprintf ("secureboot", "SecureBoot: %d\n", *secure_boot);
+ 
+   setup_mode = grub_efi_get_variable("SetupMode", &efi_var_guid, &datasize);
+-
+   if (datasize != 1 || !setup_mode)
+-    goto out;
++    {
++      grub_dprintf ("secureboot", "No SetupMode variable\n");
++      goto out;
++    }
++  grub_dprintf ("secureboot", "SetupMode: %d\n", *setup_mode);
+ 
+   if (*secure_boot && !*setup_mode)
+     ret = 1;
+diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
+index a312c668685..04994d5c67d 100644
+--- a/grub-core/loader/arm64/linux.c
++++ b/grub-core/loader/arm64/linux.c
+@@ -284,6 +284,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   struct linux_arch_kernel_header lh;
+   struct grub_armxx_linux_pe_header *pe;
+   grub_err_t err;
++  int rc;
+ 
+   grub_dl_ref (my_mod);
+ 
+@@ -328,7 +329,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 
+   grub_dprintf ("linux", "kernel @ %p\n", kernel_addr);
+ 
+-  if (!grub_linuxefi_secure_validate (kernel_addr, kernel_size))
++  rc = grub_linuxefi_secure_validate (kernel_addr, kernel_size);
++  if (rc < 0)
+     {
+       grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), argv[0]);
+       goto fail;
+diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
+index 2bd80f4db3d..b54cf6986fc 100644
+--- a/grub-core/loader/efi/chainloader.c
++++ b/grub-core/loader/efi/chainloader.c
+@@ -32,6 +32,8 @@
+ #include <grub/efi/api.h>
+ #include <grub/efi/efi.h>
+ #include <grub/efi/disk.h>
++#include <grub/efi/pe32.h>
++#include <grub/efi/linux.h>
+ #include <grub/command.h>
+ #include <grub/i18n.h>
+ #include <grub/net.h>
+@@ -46,9 +48,14 @@ static grub_dl_t my_mod;
+ 
+ static grub_efi_physical_address_t address;
+ static grub_efi_uintn_t pages;
++static grub_ssize_t fsize;
+ static grub_efi_device_path_t *file_path;
+ static grub_efi_handle_t image_handle;
+ static grub_efi_char16_t *cmdline;
++static grub_ssize_t cmdline_len;
++static grub_efi_handle_t dev_handle;
++
++static grub_efi_status_t (*entry_point) (grub_efi_handle_t image_handle, grub_efi_system_table_t *system_table);
+ 
+ static grub_err_t
+ grub_chainloader_unload (void)
+@@ -63,6 +70,7 @@ grub_chainloader_unload (void)
+   grub_free (cmdline);
+   cmdline = 0;
+   file_path = 0;
++  dev_handle = 0;
+ 
+   grub_dl_unref (my_mod);
+   return GRUB_ERR_NONE;
+@@ -213,20 +221,690 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename)
+   return file_path;
+ }
+ 
++#define SHIM_LOCK_GUID \
++  { 0x605dab50, 0xe046, 0x4300, { 0xab,0xb6,0x3d,0xd8,0x10,0xdd,0x8b,0x23 } }
++
++typedef union
++{
++  struct grub_pe32_header_32 pe32;
++  struct grub_pe32_header_64 pe32plus;
++} grub_pe_header_t;
++
++struct pe_coff_loader_image_context
++{
++  grub_efi_uint64_t image_address;
++  grub_efi_uint64_t image_size;
++  grub_efi_uint64_t entry_point;
++  grub_efi_uintn_t size_of_headers;
++  grub_efi_uint16_t image_type;
++  grub_efi_uint16_t number_of_sections;
++  grub_efi_uint32_t section_alignment;
++  struct grub_pe32_section_table *first_section;
++  struct grub_pe32_data_directory *reloc_dir;
++  struct grub_pe32_data_directory *sec_dir;
++  grub_efi_uint64_t number_of_rva_and_sizes;
++  grub_pe_header_t *pe_hdr;
++};
++
++typedef struct pe_coff_loader_image_context pe_coff_loader_image_context_t;
++
++struct grub_efi_shim_lock
++{
++  grub_efi_status_t (*verify)(void *buffer,
++                              grub_efi_uint32_t size);
++  grub_efi_status_t (*hash)(void *data,
++                            grub_efi_int32_t datasize,
++                            pe_coff_loader_image_context_t *context,
++                            grub_efi_uint8_t *sha256hash,
++                            grub_efi_uint8_t *sha1hash);
++  grub_efi_status_t (*context)(void *data,
++                               grub_efi_uint32_t size,
++                               pe_coff_loader_image_context_t *context);
++};
++
++typedef struct grub_efi_shim_lock grub_efi_shim_lock_t;
++
++static grub_efi_boolean_t
++read_header (void *data, grub_efi_uint32_t size,
++	     pe_coff_loader_image_context_t *context)
++{
++  grub_efi_guid_t guid = SHIM_LOCK_GUID;
++  grub_efi_shim_lock_t *shim_lock;
++  grub_efi_status_t status;
++
++  shim_lock = grub_efi_locate_protocol (&guid, NULL);
++  if (!shim_lock)
++    {
++      grub_dprintf ("chain", "no shim lock protocol");
++      return 0;
++    }
++
++  status = shim_lock->context (data, size, context);
++
++  if (status == GRUB_EFI_SUCCESS)
++    {
++      grub_dprintf ("chain", "context success\n");
++      return 1;
++    }
++
++  switch (status)
++    {
++      case GRUB_EFI_UNSUPPORTED:
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "context error unsupported");
++      break;
++      case GRUB_EFI_INVALID_PARAMETER:
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "context error invalid parameter");
++      break;
++      default:
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "context error code");
++      break;
++    }
++
++  return -1;
++}
++
++static void*
++image_address (void *image, grub_efi_uint64_t sz, grub_efi_uint64_t adr)
++{
++  if (adr > sz)
++    return NULL;
++
++  return ((grub_uint8_t*)image + adr);
++}
++
++static int
++image_is_64_bit (grub_pe_header_t *pe_hdr)
++{
++  /* .Magic is the same offset in all cases */
++  if (pe_hdr->pe32plus.optional_header.magic == GRUB_PE32_PE64_MAGIC)
++    return 1;
++  return 0;
++}
++
++static const grub_uint16_t machine_type __attribute__((__unused__)) =
++#if defined(__x86_64__)
++  GRUB_PE32_MACHINE_X86_64;
++#elif defined(__aarch64__)
++  GRUB_PE32_MACHINE_ARM64;
++#elif defined(__arm__)
++  GRUB_PE32_MACHINE_ARMTHUMB_MIXED;
++#elif defined(__i386__) || defined(__i486__) || defined(__i686__)
++  GRUB_PE32_MACHINE_I386;
++#elif defined(__ia64__)
++  GRUB_PE32_MACHINE_IA64;
++#else
++#error this architecture is not supported by grub2
++#endif
++
++static grub_efi_status_t
++relocate_coff (pe_coff_loader_image_context_t *context,
++	       struct grub_pe32_section_table *section,
++	       void *orig, void *data)
++{
++  struct grub_pe32_data_directory *reloc_base, *reloc_base_end;
++  grub_efi_uint64_t adjust;
++  struct grub_pe32_fixup_block *reloc, *reloc_end;
++  char *fixup, *fixup_base, *fixup_data = NULL;
++  grub_efi_uint16_t *fixup_16;
++  grub_efi_uint32_t *fixup_32;
++  grub_efi_uint64_t *fixup_64;
++  grub_efi_uint64_t size = context->image_size;
++  void *image_end = (char *)orig + size;
++  int n = 0;
++
++  if (image_is_64_bit (context->pe_hdr))
++    context->pe_hdr->pe32plus.optional_header.image_base =
++      (grub_uint64_t)(unsigned long)data;
++  else
++    context->pe_hdr->pe32.optional_header.image_base =
++      (grub_uint32_t)(unsigned long)data;
++
++  /* Alright, so here's how this works:
++   *
++   * context->reloc_dir gives us two things:
++   * - the VA the table of base relocation blocks are (maybe) to be
++   *   mapped at (reloc_dir->rva)
++   * - the virtual size (reloc_dir->size)
++   *
++   * The .reloc section (section here) gives us some other things:
++   * - the name! kind of. (section->name)
++   * - the virtual size (section->virtual_size), which should be the same
++   *   as RelocDir->Size
++   * - the virtual address (section->virtual_address)
++   * - the file section size (section->raw_data_size), which is
++   *   a multiple of optional_header->file_alignment.  Only useful for image
++   *   validation, not really useful for iteration bounds.
++   * - the file address (section->raw_data_offset)
++   * - a bunch of stuff we don't use that's 0 in our binaries usually
++   * - Flags (section->characteristics)
++   *
++   * and then the thing that's actually at the file address is an array
++   * of struct grub_pe32_fixup_block structs with some values packed behind
++   * them.  The block_size field of this structure includes the
++   * structure itself, and adding it to that structure's address will
++   * yield the next entry in the array.
++   */
++
++  reloc_base = image_address (orig, size, section->raw_data_offset);
++  reloc_base_end = image_address (orig, size, section->raw_data_offset
++				  + section->virtual_size);
++
++  grub_dprintf ("chain", "relocate_coff(): reloc_base %p reloc_base_end %p\n",
++		reloc_base, reloc_base_end);
++
++  if (!reloc_base && !reloc_base_end)
++    return GRUB_EFI_SUCCESS;
++
++  if (!reloc_base || !reloc_base_end)
++    {
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc table overflows binary");
++      return GRUB_EFI_UNSUPPORTED;
++    }
++
++  adjust = (grub_uint64_t)(grub_efi_uintn_t)data - context->image_address;
++  if (adjust == 0)
++    return GRUB_EFI_SUCCESS;
++
++  while (reloc_base < reloc_base_end)
++    {
++      grub_uint16_t *entry;
++      reloc = (struct grub_pe32_fixup_block *)((char*)reloc_base);
++
++      if ((reloc_base->size == 0) ||
++	  (reloc_base->size > context->reloc_dir->size))
++	{
++	  grub_error (GRUB_ERR_BAD_ARGUMENT,
++		      "Reloc %d block size %d is invalid\n", n,
++		      reloc_base->size);
++	  return GRUB_EFI_UNSUPPORTED;
++	}
++
++      entry = &reloc->entries[0];
++      reloc_end = (struct grub_pe32_fixup_block *)
++	((char *)reloc_base + reloc_base->size);
++
++      if ((void *)reloc_end < orig || (void *)reloc_end > image_end)
++        {
++          grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc entry %d overflows binary",
++		      n);
++          return GRUB_EFI_UNSUPPORTED;
++        }
++
++      fixup_base = image_address(data, size, reloc_base->rva);
++
++      if (!fixup_base)
++        {
++          grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc %d Invalid fixupbase", n);
++          return GRUB_EFI_UNSUPPORTED;
++        }
++
++      while ((void *)entry < (void *)reloc_end)
++        {
++          fixup = fixup_base + (*entry & 0xFFF);
++          switch ((*entry) >> 12)
++            {
++              case GRUB_PE32_REL_BASED_ABSOLUTE:
++                break;
++              case GRUB_PE32_REL_BASED_HIGH:
++                fixup_16 = (grub_uint16_t *)fixup;
++                *fixup_16 = (grub_uint16_t)
++		  (*fixup_16 + ((grub_uint16_t)((grub_uint32_t)adjust >> 16)));
++                if (fixup_data != NULL)
++                  {
++                    *(grub_uint16_t *) fixup_data = *fixup_16;
++                    fixup_data = fixup_data + sizeof (grub_uint16_t);
++                  }
++                break;
++              case GRUB_PE32_REL_BASED_LOW:
++                fixup_16 = (grub_uint16_t *)fixup;
++                *fixup_16 = (grub_uint16_t) (*fixup_16 + (grub_uint16_t)adjust);
++                if (fixup_data != NULL)
++                  {
++                    *(grub_uint16_t *) fixup_data = *fixup_16;
++                    fixup_data = fixup_data + sizeof (grub_uint16_t);
++                  }
++                break;
++              case GRUB_PE32_REL_BASED_HIGHLOW:
++                fixup_32 = (grub_uint32_t *)fixup;
++                *fixup_32 = *fixup_32 + (grub_uint32_t)adjust;
++                if (fixup_data != NULL)
++                  {
++                    fixup_data = (char *)ALIGN_UP ((grub_addr_t)fixup_data, sizeof (grub_uint32_t));
++                    *(grub_uint32_t *) fixup_data = *fixup_32;
++                    fixup_data += sizeof (grub_uint32_t);
++                  }
++                break;
++              case GRUB_PE32_REL_BASED_DIR64:
++                fixup_64 = (grub_uint64_t *)fixup;
++                *fixup_64 = *fixup_64 + (grub_uint64_t)adjust;
++                if (fixup_data != NULL)
++                  {
++                    fixup_data = (char *)ALIGN_UP ((grub_addr_t)fixup_data, sizeof (grub_uint64_t));
++                    *(grub_uint64_t *) fixup_data = *fixup_64;
++                    fixup_data += sizeof (grub_uint64_t);
++                  }
++                break;
++              default:
++                grub_error (GRUB_ERR_BAD_ARGUMENT,
++			    "Reloc %d unknown relocation type %d",
++			    n, (*entry) >> 12);
++                return GRUB_EFI_UNSUPPORTED;
++            }
++          entry += 1;
++        }
++      reloc_base = (struct grub_pe32_data_directory *)reloc_end;
++      n++;
++    }
++
++  return GRUB_EFI_SUCCESS;
++}
++
++static grub_efi_device_path_t *
++grub_efi_get_media_file_path (grub_efi_device_path_t *dp)
++{
++  while (1)
++    {
++      grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
++      grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
++
++      if (type == GRUB_EFI_END_DEVICE_PATH_TYPE)
++        break;
++      else if (type == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
++            && subtype == GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE)
++      return dp;
++
++      dp = GRUB_EFI_NEXT_DEVICE_PATH (dp);
++    }
++
++    return NULL;
++}
++
++static grub_efi_boolean_t
++handle_image (void *data, grub_efi_uint32_t datasize)
++{
++  grub_efi_boot_services_t *b;
++  grub_efi_loaded_image_t *li, li_bak;
++  grub_efi_status_t efi_status;
++  char *buffer = NULL;
++  char *buffer_aligned = NULL;
++  grub_efi_uint32_t i;
++  struct grub_pe32_section_table *section;
++  char *base, *end;
++  pe_coff_loader_image_context_t context;
++  grub_uint32_t section_alignment;
++  grub_uint32_t buffer_size;
++  int found_entry_point = 0;
++  int rc;
++
++  b = grub_efi_system_table->boot_services;
++
++  rc = read_header (data, datasize, &context);
++  if (rc < 0)
++    {
++      grub_dprintf ("chain", "Failed to read header\n");
++      goto error_exit;
++    }
++  else if (rc == 0)
++    {
++      grub_dprintf ("chain", "Secure Boot is not enabled\n");
++      return 0;
++    }
++  else
++    {
++      grub_dprintf ("chain", "Header read without error\n");
++    }
++
++  /*
++   * The spec says, uselessly, of SectionAlignment:
++   * =====
++   * The alignment (in bytes) of sections when they are loaded into
++   * memory. It must be greater than or equal to FileAlignment. The
++   * default is the page size for the architecture.
++   * =====
++   * Which doesn't tell you whose responsibility it is to enforce the
++   * "default", or when.  It implies that the value in the field must
++   * be > FileAlignment (also poorly defined), but it appears visual
++   * studio will happily write 512 for FileAlignment (its default) and
++   * 0 for SectionAlignment, intending to imply PAGE_SIZE.
++   *
++   * We only support one page size, so if it's zero, nerf it to 4096.
++   */
++  section_alignment = context.section_alignment;
++  if (section_alignment == 0)
++    section_alignment = 4096;
++
++  buffer_size = context.image_size + section_alignment;
++  grub_dprintf ("chain", "image size is %08"PRIxGRUB_UINT64_T", datasize is %08x\n",
++	       context.image_size, datasize);
++
++  efi_status = efi_call_3 (b->allocate_pool, GRUB_EFI_LOADER_DATA,
++			   buffer_size, &buffer);
++
++  if (efi_status != GRUB_EFI_SUCCESS)
++    {
++      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
++      goto error_exit;
++    }
++
++  buffer_aligned = (char *)ALIGN_UP ((grub_addr_t)buffer, section_alignment);
++  if (!buffer_aligned)
++    {
++      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
++      goto error_exit;
++    }
++
++  grub_memcpy (buffer_aligned, data, context.size_of_headers);
++
++  entry_point = image_address (buffer_aligned, context.image_size,
++			       context.entry_point);
++
++  grub_dprintf ("chain", "entry_point: %p\n", entry_point);
++  if (!entry_point)
++    {
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid entry point");
++      goto error_exit;
++    }
++
++  char *reloc_base, *reloc_base_end;
++  grub_dprintf ("chain", "reloc_dir: %p reloc_size: 0x%08x\n",
++		(void *)(unsigned long)context.reloc_dir->rva,
++		context.reloc_dir->size);
++  reloc_base = image_address (buffer_aligned, context.image_size,
++			      context.reloc_dir->rva);
++  /* RelocBaseEnd here is the address of the last byte of the table */
++  reloc_base_end = image_address (buffer_aligned, context.image_size,
++				  context.reloc_dir->rva
++				  + context.reloc_dir->size - 1);
++  grub_dprintf ("chain", "reloc_base: %p reloc_base_end: %p\n",
++		reloc_base, reloc_base_end);
++
++  struct grub_pe32_section_table *reloc_section = NULL, fake_reloc_section;
++
++  section = context.first_section;
++  for (i = 0; i < context.number_of_sections; i++, section++)
++    {
++      char name[9];
++
++      base = image_address (buffer_aligned, context.image_size,
++			    section->virtual_address);
++      end = image_address (buffer_aligned, context.image_size,
++			   section->virtual_address + section->virtual_size -1);
++
++      grub_strncpy(name, section->name, 9);
++      name[8] = '\0';
++      grub_dprintf ("chain", "Section %d \"%s\" at %p..%p\n", i,
++		   name, base, end);
++
++      if (end < base)
++	{
++	  grub_dprintf ("chain", " base is %p but end is %p... bad.\n",
++		       base, end);
++	  grub_error (GRUB_ERR_BAD_ARGUMENT,
++		      "Image has invalid negative size");
++	  goto error_exit;
++	}
++
++      if (section->virtual_address <= context.entry_point &&
++	  (section->virtual_address + section->raw_data_size - 1)
++	  > context.entry_point)
++	{
++	  found_entry_point++;
++	  grub_dprintf ("chain", " section contains entry point\n");
++	}
++
++      /* We do want to process .reloc, but it's often marked
++       * discardable, so we don't want to memcpy it. */
++      if (grub_memcmp (section->name, ".reloc\0\0", 8) == 0)
++	{
++	  if (reloc_section)
++	    {
++	      grub_error (GRUB_ERR_BAD_ARGUMENT,
++			  "Image has multiple relocation sections");
++	      goto error_exit;
++	    }
++
++	  /* If it has nonzero sizes, and our bounds check
++	   * made sense, and the VA and size match RelocDir's
++	   * versions, then we believe in this section table. */
++	  if (section->raw_data_size && section->virtual_size &&
++	      base && end && reloc_base == base)
++	    {
++	      if (reloc_base_end == end)
++		{
++		  grub_dprintf ("chain", " section is relocation section\n");
++		  reloc_section = section;
++		}
++	      else if (reloc_base_end && reloc_base_end < end)
++	        {
++		  /* Bogus virtual size in the reloc section -- RelocDir
++		   * reported a smaller Base Relocation Directory. Decrease
++		   * the section's virtual size so that it equal RelocDir's
++		   * idea, but only for the purposes of relocate_coff(). */
++		  grub_dprintf ("chain",
++				" section is (overlong) relocation section\n");
++		  grub_memcpy (&fake_reloc_section, section, sizeof *section);
++		  fake_reloc_section.virtual_size -= (end - reloc_base_end);
++		  reloc_section = &fake_reloc_section;
++		}
++	    }
++
++	  if (!reloc_section)
++	    {
++	      grub_dprintf ("chain", " section is not reloc section?\n");
++	      grub_dprintf ("chain", " rds: 0x%08x, vs: %08x\n",
++			    section->raw_data_size, section->virtual_size);
++	      grub_dprintf ("chain", " base: %p end: %p\n", base, end);
++	      grub_dprintf ("chain", " reloc_base: %p reloc_base_end: %p\n",
++			    reloc_base, reloc_base_end);
++	    }
++	}
++
++      grub_dprintf ("chain", " Section characteristics are %08x\n",
++		   section->characteristics);
++      grub_dprintf ("chain", " Section virtual size: %08x\n",
++		   section->virtual_size);
++      grub_dprintf ("chain", " Section raw_data size: %08x\n",
++		   section->raw_data_size);
++      if (section->characteristics & GRUB_PE32_SCN_MEM_DISCARDABLE)
++	{
++	  grub_dprintf ("chain", " Discarding section\n");
++	  continue;
++	}
++
++      if (!base || !end)
++        {
++	  grub_dprintf ("chain", " section is invalid\n");
++          grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid section size");
++          goto error_exit;
++        }
++
++      if (section->characteristics & GRUB_PE32_SCN_CNT_UNINITIALIZED_DATA)
++	{
++	  if (section->raw_data_size != 0)
++	    grub_dprintf ("chain", " UNINITIALIZED_DATA section has data?\n");
++	}
++      else if (section->virtual_address < context.size_of_headers ||
++	       section->raw_data_offset < context.size_of_headers)
++	{
++	  grub_error (GRUB_ERR_BAD_ARGUMENT,
++		      "Section %d is inside image headers", i);
++	  goto error_exit;
++	}
++
++      if (section->raw_data_size > 0)
++	{
++	  grub_dprintf ("chain", " copying 0x%08x bytes to %p\n",
++			section->raw_data_size, base);
++	  grub_memcpy (base,
++		       (grub_efi_uint8_t*)data + section->raw_data_offset,
++		       section->raw_data_size);
++	}
++
++      if (section->raw_data_size < section->virtual_size)
++	{
++	  grub_dprintf ("chain", " padding with 0x%08x bytes at %p\n",
++			section->virtual_size - section->raw_data_size,
++			base + section->raw_data_size);
++	  grub_memset (base + section->raw_data_size, 0,
++		       section->virtual_size - section->raw_data_size);
++	}
++
++      grub_dprintf ("chain", " finished section %s\n", name);
++    }
++
++  /* 5 == EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC */
++  if (context.number_of_rva_and_sizes <= 5)
++    {
++      grub_dprintf ("chain", "image has no relocation entry\n");
++      goto error_exit;
++    }
++
++  if (context.reloc_dir->size && reloc_section)
++    {
++      /* run the relocation fixups */
++      efi_status = relocate_coff (&context, reloc_section, data,
++				  buffer_aligned);
++
++      if (efi_status != GRUB_EFI_SUCCESS)
++	{
++	  grub_error (GRUB_ERR_BAD_ARGUMENT, "relocation failed");
++	  goto error_exit;
++	}
++    }
++
++  if (!found_entry_point)
++    {
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "entry point is not within sections");
++      goto error_exit;
++    }
++  if (found_entry_point > 1)
++    {
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "%d sections contain entry point",
++		  found_entry_point);
++      goto error_exit;
++    }
++
++  li = grub_efi_get_loaded_image (grub_efi_image_handle);
++  if (!li)
++    {
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "no loaded image available");
++      goto error_exit;
++    }
++
++  grub_memcpy (&li_bak, li, sizeof (grub_efi_loaded_image_t));
++  li->image_base = buffer_aligned;
++  li->image_size = context.image_size;
++  li->load_options = cmdline;
++  li->load_options_size = cmdline_len;
++  li->file_path = grub_efi_get_media_file_path (file_path);
++  li->device_handle = dev_handle;
++  if (!li->file_path)
++    {
++      grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching file path found");
++      goto error_exit;
++    }
++
++  grub_dprintf ("chain", "booting via entry point\n");
++  efi_status = efi_call_2 (entry_point, grub_efi_image_handle,
++			   grub_efi_system_table);
++
++  grub_dprintf ("chain", "entry_point returned %ld\n", efi_status);
++  grub_memcpy (li, &li_bak, sizeof (grub_efi_loaded_image_t));
++  efi_status = efi_call_1 (b->free_pool, buffer);
++
++  return 1;
++
++error_exit:
++  grub_dprintf ("chain", "error_exit: grub_errno: %d\n", grub_errno);
++  if (buffer)
++      efi_call_1 (b->free_pool, buffer);
++
++  return 0;
++}
++
++static grub_err_t
++grub_secureboot_chainloader_unload (void)
++{
++  grub_efi_boot_services_t *b;
++
++  b = grub_efi_system_table->boot_services;
++  efi_call_2 (b->free_pages, address, pages);
++  grub_free (file_path);
++  grub_free (cmdline);
++  cmdline = 0;
++  file_path = 0;
++  dev_handle = 0;
++
++  grub_dl_unref (my_mod);
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_load_and_start_image(void *boot_image)
++{
++  grub_efi_boot_services_t *b;
++  grub_efi_status_t status;
++  grub_efi_loaded_image_t *loaded_image;
++
++  b = grub_efi_system_table->boot_services;
++
++  status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path,
++		       boot_image, fsize, &image_handle);
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      if (status == GRUB_EFI_OUT_OF_RESOURCES)
++	grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of resources");
++      else
++	grub_error (GRUB_ERR_BAD_OS, "cannot load image");
++      return -1;
++    }
++
++  /* LoadImage does not set a device handler when the image is
++     loaded from memory, so it is necessary to set it explicitly here.
++     This is a mess.  */
++  loaded_image = grub_efi_get_loaded_image (image_handle);
++  if (! loaded_image)
++    {
++      grub_error (GRUB_ERR_BAD_OS, "no loaded image available");
++      return -1;
++    }
++  loaded_image->device_handle = dev_handle;
++
++  if (cmdline)
++    {
++      loaded_image->load_options = cmdline;
++      loaded_image->load_options_size = cmdline_len;
++    }
++
++  return 0;
++}
++
++static grub_err_t
++grub_secureboot_chainloader_boot (void)
++{
++  int rc;
++  rc = handle_image ((void *)(unsigned long)address, fsize);
++  if (rc == 0)
++    {
++      grub_load_and_start_image((void *)(unsigned long)address);
++    }
++
++  grub_loader_unset ();
++  return grub_errno;
++}
++
+ static grub_err_t
+ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+ 		      int argc, char *argv[])
+ {
+   grub_file_t file = 0;
+-  grub_ssize_t size;
+   grub_efi_status_t status;
+   grub_efi_boot_services_t *b;
+   grub_device_t dev = 0;
+   grub_efi_device_path_t *dp = 0;
+-  grub_efi_loaded_image_t *loaded_image;
+   char *filename;
+   void *boot_image = 0;
+-  grub_efi_handle_t dev_handle = 0;
++  int rc;
+ 
+   if (argc == 0)
+     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+@@ -238,15 +916,45 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+   address = 0;
+   image_handle = 0;
+   file_path = 0;
++  dev_handle = 0;
+ 
+   b = grub_efi_system_table->boot_services;
+ 
++  if (argc > 1)
++    {
++      int i;
++      grub_efi_char16_t *p16;
++
++      for (i = 1, cmdline_len = 0; i < argc; i++)
++        cmdline_len += grub_strlen (argv[i]) + 1;
++
++      cmdline_len *= sizeof (grub_efi_char16_t);
++      cmdline = p16 = grub_malloc (cmdline_len);
++      if (! cmdline)
++        goto fail;
++
++      for (i = 1; i < argc; i++)
++        {
++          char *p8;
++
++          p8 = argv[i];
++          while (*p8)
++            *(p16++) = *(p8++);
++
++          *(p16++) = ' ';
++        }
++      *(--p16) = 0;
++    }
++
+   file = grub_file_open (filename, GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE);
+   if (! file)
+     goto fail;
+ 
+-  /* Get the root device's device path.  */
+-  dev = grub_device_open (0);
++  /* Get the device path from filename. */
++  char *devname = grub_file_get_device_name (filename);
++  dev = grub_device_open (devname);
++  if (devname)
++    grub_free (devname);
+   if (! dev)
+     goto fail;
+ 
+@@ -283,17 +991,14 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+   if (! file_path)
+     goto fail;
+ 
+-  grub_printf ("file path: ");
+-  grub_efi_print_device_path (file_path);
+-
+-  size = grub_file_size (file);
+-  if (!size)
++  fsize = grub_file_size (file);
++  if (!fsize)
+     {
+       grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ 		  filename);
+       goto fail;
+     }
+-  pages = (((grub_efi_uintn_t) size + ((1 << 12) - 1)) >> 12);
++  pages = (((grub_efi_uintn_t) fsize + ((1 << 12) - 1)) >> 12);
+ 
+   status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_ANY_PAGES,
+ 			      GRUB_EFI_LOADER_CODE,
+@@ -307,7 +1012,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+     }
+ 
+   boot_image = (void *) ((grub_addr_t) address);
+-  if (grub_file_read (file, boot_image, size) != size)
++  if (grub_file_read (file, boot_image, fsize) != fsize)
+     {
+       if (grub_errno == GRUB_ERR_NONE)
+ 	grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+@@ -317,7 +1022,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+     }
+ 
+ #if defined (__i386__) || defined (__x86_64__)
+-  if (size >= (grub_ssize_t) sizeof (struct grub_macho_fat_header))
++  if (fsize >= (grub_ssize_t) sizeof (struct grub_macho_fat_header))
+     {
+       struct grub_macho_fat_header *head = boot_image;
+       if (head->magic
+@@ -326,6 +1031,14 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+ 	  grub_uint32_t i;
+ 	  struct grub_macho_fat_arch *archs
+ 	    = (struct grub_macho_fat_arch *) (head + 1);
++
++	  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
++	    {
++	      grub_error (GRUB_ERR_BAD_OS,
++			  "MACHO binaries are forbidden with Secure Boot");
++	      goto fail;
++	    }
++
+ 	  for (i = 0; i < grub_cpu_to_le32 (head->nfat_arch); i++)
+ 	    {
+ 	      if (GRUB_MACHO_CPUTYPE_IS_HOST_CURRENT (archs[i].cputype))
+@@ -340,79 +1053,39 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+ 	      > ~grub_cpu_to_le32 (archs[i].size)
+ 	      || grub_cpu_to_le32 (archs[i].offset)
+ 	      + grub_cpu_to_le32 (archs[i].size)
+-	      > (grub_size_t) size)
++	      > (grub_size_t) fsize)
+ 	    {
+ 	      grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ 			  filename);
+ 	      goto fail;
+ 	    }
+ 	  boot_image = (char *) boot_image + grub_cpu_to_le32 (archs[i].offset);
+-	  size = grub_cpu_to_le32 (archs[i].size);
++	  fsize = grub_cpu_to_le32 (archs[i].size);
+ 	}
+     }
+ #endif
+ 
+-  status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path,
+-		       boot_image, size,
+-		       &image_handle);
+-  if (status != GRUB_EFI_SUCCESS)
++  rc = grub_linuxefi_secure_validate((void *)(unsigned long)address, fsize);
++  grub_dprintf ("chain", "linuxefi_secure_validate: %d\n", rc);
++  if (rc > 0)
+     {
+-      if (status == GRUB_EFI_OUT_OF_RESOURCES)
+-	grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of resources");
+-      else
+-	grub_error (GRUB_ERR_BAD_OS, "cannot load image");
+-
+-      goto fail;
++      grub_file_close (file);
++      grub_device_close (dev);
++      grub_loader_set (grub_secureboot_chainloader_boot,
++		       grub_secureboot_chainloader_unload, 0);
++      return 0;
+     }
+-
+-  /* LoadImage does not set a device handler when the image is
+-     loaded from memory, so it is necessary to set it explicitly here.
+-     This is a mess.  */
+-  loaded_image = grub_efi_get_loaded_image (image_handle);
+-  if (! loaded_image)
++  else if (rc == 0)
+     {
+-      grub_error (GRUB_ERR_BAD_OS, "no loaded image available");
+-      goto fail;
+-    }
+-  loaded_image->device_handle = dev_handle;
+-
+-  if (argc > 1)
+-    {
+-      int i, len;
+-      grub_efi_char16_t *p16;
+-
+-      for (i = 1, len = 0; i < argc; i++)
+-        len += grub_strlen (argv[i]) + 1;
+-
+-      len *= sizeof (grub_efi_char16_t);
+-      cmdline = p16 = grub_malloc (len);
+-      if (! cmdline)
+-        goto fail;
++      grub_load_and_start_image(boot_image);
++      grub_file_close (file);
++      grub_device_close (dev);
++      grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
+ 
+-      for (i = 1; i < argc; i++)
+-        {
+-          char *p8;
+-
+-          p8 = argv[i];
+-          while (*p8)
+-            *(p16++) = *(p8++);
+-
+-          *(p16++) = ' ';
+-        }
+-      *(--p16) = 0;
+-
+-      loaded_image->load_options = cmdline;
+-      loaded_image->load_options_size = len;
++      return 0;
+     }
+ 
+-  grub_file_close (file);
+-  grub_device_close (dev);
+-
+-  grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
+-  return 0;
+-
+- fail:
+-
++fail:
+   if (dev)
+     grub_device_close (dev);
+ 
+@@ -424,6 +1097,9 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+   if (address)
+     efi_call_2 (b->free_pages, address, pages);
+ 
++  if (cmdline)
++    grub_free (cmdline);
++
+   grub_dl_unref (my_mod);
+ 
+   return grub_errno;
+diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c
+index c24202a5dd1..c8ecce6dfd0 100644
+--- a/grub-core/loader/efi/linux.c
++++ b/grub-core/loader/efi/linux.c
+@@ -33,21 +33,34 @@ struct grub_efi_shim_lock
+ };
+ typedef struct grub_efi_shim_lock grub_efi_shim_lock_t;
+ 
+-grub_efi_boolean_t
++int
+ grub_linuxefi_secure_validate (void *data, grub_uint32_t size)
+ {
+   grub_efi_guid_t guid = SHIM_LOCK_GUID;
+   grub_efi_shim_lock_t *shim_lock;
++  grub_efi_status_t status;
+ 
+   shim_lock = grub_efi_locate_protocol(&guid, NULL);
+-
++  grub_dprintf ("secureboot", "shim_lock: %p\n", shim_lock);
+   if (!shim_lock)
+-    return 1;
++    {
++      grub_dprintf ("secureboot", "shim not available\n");
++      return 0;
++    }
+ 
+-  if (shim_lock->verify(data, size) == GRUB_EFI_SUCCESS)
+-    return 1;
++  grub_dprintf ("secureboot", "Asking shim to verify kernel signature\n");
++  status = shim_lock->verify (data, size);
++  grub_dprintf ("secureboot", "shim_lock->verify(): %ld\n", (long int)status);
++  if (status == GRUB_EFI_SUCCESS)
++    {
++      grub_dprintf ("secureboot", "Kernel signature verification passed\n");
++      return 1;
++    }
+ 
+-  return 0;
++  grub_dprintf ("secureboot", "Kernel signature verification failed (0x%lx)\n",
++		(unsigned long) status);
++
++  return -1;
+ }
+ 
+ #pragma GCC diagnostic push
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index bb2616a8092..6b24cbb9483 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -117,6 +117,8 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+       goto fail;
+     }
+ 
++  grub_dprintf ("linux", "initrd_mem = %lx\n", (unsigned long) initrd_mem);
++
+   params->ramdisk_size = size;
+   params->ramdisk_image = (grub_uint32_t)(grub_addr_t) initrd_mem;
+ 
+@@ -159,6 +161,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   struct linux_i386_kernel_header lh;
+   grub_ssize_t len, start, filelen;
+   void *kernel = NULL;
++  int rc;
+ 
+   grub_dl_ref (my_mod);
+ 
+@@ -184,11 +187,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 
+   if (grub_file_read (file, kernel, filelen) != filelen)
+     {
+-      grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"), argv[0]);
++      grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"),
++		  argv[0]);
+       goto fail;
+     }
+ 
+-  if (! grub_linuxefi_secure_validate (kernel, filelen))
++  rc = grub_linuxefi_secure_validate (kernel, filelen);
++  if (rc < 0)
+     {
+       grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"),
+ 		  argv[0]);
+@@ -203,6 +208,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+       goto fail;
+     }
+ 
++  grub_dprintf ("linux", "params = %lx\n", (unsigned long) params);
++
+   grub_memset (params, 0, 16384);
+ 
+   grub_memcpy (&lh, kernel, sizeof (lh));
+@@ -241,6 +248,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+       goto fail;
+     }
+ 
++  grub_dprintf ("linux", "linux_cmdline = %lx\n",
++		(unsigned long)linux_cmdline);
++
+   grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
+   grub_create_loader_cmdline (argc, argv,
+                               linux_cmdline + sizeof (LINUX_IMAGE) - 1,
+@@ -275,9 +285,10 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   grub_memcpy (params, &lh, 2 * 512);
+ 
+   params->type_of_loader = 0x21;
++  grub_dprintf("linux", "kernel_mem: %p handover_offset: %08x\n",
++	       kernel_mem, handover_offset);
+ 
+  fail:
+-
+   if (file)
+     grub_file_close (file);
+ 
+diff --git a/include/grub/efi/linux.h b/include/grub/efi/linux.h
+index d9ede36773b..0033d9305a9 100644
+--- a/include/grub/efi/linux.h
++++ b/include/grub/efi/linux.h
+@@ -22,7 +22,7 @@
+ #include <grub/err.h>
+ #include <grub/symbol.h>
+ 
+-grub_efi_boolean_t
++int
+ EXPORT_FUNC(grub_linuxefi_secure_validate) (void *data, grub_uint32_t size);
+ grub_err_t
+ EXPORT_FUNC(grub_efi_linux_boot) (void *kernel_address, grub_off_t offset,
+diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h
+index 0ed8781f037..a43adf27464 100644
+--- a/include/grub/efi/pe32.h
++++ b/include/grub/efi/pe32.h
+@@ -223,7 +223,11 @@ struct grub_pe64_optional_header
+ struct grub_pe32_section_table
+ {
+   char name[8];
+-  grub_uint32_t virtual_size;
++  union
++    {
++      grub_uint32_t physical_address;
++      grub_uint32_t virtual_size;
++    };
+   grub_uint32_t virtual_address;
+   grub_uint32_t raw_data_size;
+   grub_uint32_t raw_data_offset;
+@@ -234,12 +238,18 @@ struct grub_pe32_section_table
+   grub_uint32_t characteristics;
+ };
+ 
++#define GRUB_PE32_SCN_TYPE_NO_PAD		0x00000008
+ #define GRUB_PE32_SCN_CNT_CODE			0x00000020
+ #define GRUB_PE32_SCN_CNT_INITIALIZED_DATA	0x00000040
+-#define GRUB_PE32_SCN_MEM_DISCARDABLE		0x02000000
+-#define GRUB_PE32_SCN_MEM_EXECUTE		0x20000000
+-#define GRUB_PE32_SCN_MEM_READ			0x40000000
+-#define GRUB_PE32_SCN_MEM_WRITE			0x80000000
++#define GRUB_PE32_SCN_CNT_UNINITIALIZED_DATA	0x00000080
++#define GRUB_PE32_SCN_LNK_OTHER			0x00000100
++#define GRUB_PE32_SCN_LNK_INFO			0x00000200
++#define GRUB_PE32_SCN_LNK_REMOVE		0x00000800
++#define GRUB_PE32_SCN_LNK_COMDAT		0x00001000
++#define GRUB_PE32_SCN_GPREL			0x00008000
++#define GRUB_PE32_SCN_MEM_16BIT			0x00020000
++#define GRUB_PE32_SCN_MEM_LOCKED		0x00040000
++#define GRUB_PE32_SCN_MEM_PRELOAD		0x00080000
+ 
+ #define GRUB_PE32_SCN_ALIGN_1BYTES		0x00100000
+ #define GRUB_PE32_SCN_ALIGN_2BYTES		0x00200000
+@@ -248,10 +258,28 @@ struct grub_pe32_section_table
+ #define GRUB_PE32_SCN_ALIGN_16BYTES		0x00500000
+ #define GRUB_PE32_SCN_ALIGN_32BYTES		0x00600000
+ #define GRUB_PE32_SCN_ALIGN_64BYTES		0x00700000
++#define GRUB_PE32_SCN_ALIGN_128BYTES		0x00800000
++#define GRUB_PE32_SCN_ALIGN_256BYTES		0x00900000
++#define GRUB_PE32_SCN_ALIGN_512BYTES		0x00A00000
++#define GRUB_PE32_SCN_ALIGN_1024BYTES		0x00B00000
++#define GRUB_PE32_SCN_ALIGN_2048BYTES		0x00C00000
++#define GRUB_PE32_SCN_ALIGN_4096BYTES		0x00D00000
++#define GRUB_PE32_SCN_ALIGN_8192BYTES		0x00E00000
+ 
+ #define GRUB_PE32_SCN_ALIGN_SHIFT		20
+ #define GRUB_PE32_SCN_ALIGN_MASK		7
+ 
++#define GRUB_PE32_SCN_LNK_NRELOC_OVFL		0x01000000
++#define GRUB_PE32_SCN_MEM_DISCARDABLE		0x02000000
++#define GRUB_PE32_SCN_MEM_NOT_CACHED		0x04000000
++#define GRUB_PE32_SCN_MEM_NOT_PAGED		0x08000000
++#define GRUB_PE32_SCN_MEM_SHARED		0x10000000
++#define GRUB_PE32_SCN_MEM_EXECUTE		0x20000000
++#define GRUB_PE32_SCN_MEM_READ			0x40000000
++#define GRUB_PE32_SCN_MEM_WRITE			0x80000000
++
++
++
+ #define GRUB_PE32_SIGNATURE_SIZE 4
+ 
+ struct grub_pe32_header
+@@ -274,6 +302,20 @@ struct grub_pe32_header
+ #endif
+ };
+ 
++struct grub_pe32_header_32
++{
++  char signature[GRUB_PE32_SIGNATURE_SIZE];
++  struct grub_pe32_coff_header coff_header;
++  struct grub_pe32_optional_header optional_header;
++};
++
++struct grub_pe32_header_64
++{
++  char signature[GRUB_PE32_SIGNATURE_SIZE];
++  struct grub_pe32_coff_header coff_header;
++  struct grub_pe64_optional_header optional_header;
++};
++
+ struct grub_pe32_fixup_block
+ {
+   grub_uint32_t page_rva;
diff --git a/SOURCES/0008-Make-any-of-the-loaders-that-link-in-efi-mode-honor-.patch b/SOURCES/0008-Make-any-of-the-loaders-that-link-in-efi-mode-honor-.patch
new file mode 100644
index 0000000..3182fe9
--- /dev/null
+++ b/SOURCES/0008-Make-any-of-the-loaders-that-link-in-efi-mode-honor-.patch
@@ -0,0 +1,390 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 6 Oct 2015 16:09:25 -0400
+Subject: [PATCH] Make any of the loaders that link in efi mode honor secure
+ boot.
+
+And in this case "honor" means "even if somebody does link this in, they
+won't register commands if SB is enabled."
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/commands/iorw.c          |  7 +++++++
+ grub-core/commands/memrw.c         |  7 +++++++
+ grub-core/kern/dl.c                |  3 ++-
+ grub-core/kern/efi/efi.c           | 34 ----------------------------------
+ grub-core/loader/efi/appleloader.c |  7 +++++++
+ grub-core/loader/efi/chainloader.c |  1 +
+ grub-core/loader/i386/bsd.c        |  7 +++++++
+ grub-core/loader/i386/linux.c      |  7 +++++++
+ grub-core/loader/i386/pc/linux.c   |  7 +++++++
+ grub-core/loader/multiboot.c       |  7 +++++++
+ grub-core/loader/xnu.c             |  7 +++++++
+ include/grub/efi/efi.h             |  1 -
+ include/grub/ia64/linux.h          |  0
+ include/grub/mips/linux.h          |  0
+ include/grub/powerpc/linux.h       |  0
+ include/grub/sparc64/linux.h       |  0
+ 16 files changed, 59 insertions(+), 36 deletions(-)
+ create mode 100644 include/grub/ia64/linux.h
+ create mode 100644 include/grub/mips/linux.h
+ create mode 100644 include/grub/powerpc/linux.h
+ create mode 100644 include/grub/sparc64/linux.h
+
+diff --git a/grub-core/commands/iorw.c b/grub-core/commands/iorw.c
+index 584baec8f91..7b2999b14b5 100644
+--- a/grub-core/commands/iorw.c
++++ b/grub-core/commands/iorw.c
+@@ -24,6 +24,7 @@
+ #include <grub/cpu/io.h>
+ #include <grub/i18n.h>
+ #include <grub/lockdown.h>
++#include <grub/efi/sb.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -119,6 +120,9 @@ grub_cmd_write (grub_command_t cmd, int argc, char **argv)
+ 
+ GRUB_MOD_INIT(memrw)
+ {
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
++    return;
++
+   cmd_read_byte =
+     grub_register_extcmd ("inb", grub_cmd_read, 0,
+ 			  N_("PORT"), N_("Read 8-bit value from PORT."),
+@@ -147,6 +151,9 @@ GRUB_MOD_INIT(memrw)
+ 
+ GRUB_MOD_FINI(memrw)
+ {
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
++    return;
++
+   grub_unregister_extcmd (cmd_read_byte);
+   grub_unregister_extcmd (cmd_read_word);
+   grub_unregister_extcmd (cmd_read_dword);
+diff --git a/grub-core/commands/memrw.c b/grub-core/commands/memrw.c
+index d401a6db0ef..39cf3a06dbd 100644
+--- a/grub-core/commands/memrw.c
++++ b/grub-core/commands/memrw.c
+@@ -23,6 +23,7 @@
+ #include <grub/env.h>
+ #include <grub/i18n.h>
+ #include <grub/lockdown.h>
++#include <grub/efi/sb.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -121,6 +122,9 @@ grub_cmd_write (grub_command_t cmd, int argc, char **argv)
+ 
+ GRUB_MOD_INIT(memrw)
+ {
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
++    return;
++
+   cmd_read_byte =
+     grub_register_extcmd ("read_byte", grub_cmd_read, 0,
+ 			  N_("ADDR"), N_("Read 8-bit value from ADDR."),
+@@ -149,6 +153,9 @@ GRUB_MOD_INIT(memrw)
+ 
+ GRUB_MOD_FINI(memrw)
+ {
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
++    return;
++
+   grub_unregister_extcmd (cmd_read_byte);
+   grub_unregister_extcmd (cmd_read_word);
+   grub_unregister_extcmd (cmd_read_dword);
+diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
+index b7149370950..7afb9e6f724 100644
+--- a/grub-core/kern/dl.c
++++ b/grub-core/kern/dl.c
+@@ -32,6 +32,7 @@
+ #include <grub/env.h>
+ #include <grub/cache.h>
+ #include <grub/i18n.h>
++#include <grub/efi/sb.h>
+ 
+ /* Platforms where modules are in a readonly area of memory.  */
+ #if defined(GRUB_MACHINE_QEMU)
+@@ -704,7 +705,7 @@ grub_dl_load_file (const char *filename)
+   grub_dl_t mod = 0;
+ 
+ #ifdef GRUB_MACHINE_EFI
+-  if (grub_efi_secure_boot ())
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
+     {
+ #if 0
+       /* This is an error, but grub2-mkconfig still generates a pile of
+diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
+index 4a2259aa1c7..8cff7be0289 100644
+--- a/grub-core/kern/efi/efi.c
++++ b/grub-core/kern/efi/efi.c
+@@ -286,40 +286,6 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid,
+   return grub_efi_get_variable_with_attributes (var, guid, datasize_out, data_out, NULL);
+ }
+ 
+-grub_efi_boolean_t
+-grub_efi_secure_boot (void)
+-{
+-  grub_efi_guid_t efi_var_guid = GRUB_EFI_GLOBAL_VARIABLE_GUID;
+-  grub_size_t datasize;
+-  char *secure_boot = NULL;
+-  char *setup_mode = NULL;
+-  grub_efi_boolean_t ret = 0;
+-
+-  secure_boot = grub_efi_get_variable("SecureBoot", &efi_var_guid, &datasize);
+-  if (datasize != 1 || !secure_boot)
+-    {
+-      grub_dprintf ("secureboot", "No SecureBoot variable\n");
+-      goto out;
+-    }
+-  grub_dprintf ("secureboot", "SecureBoot: %d\n", *secure_boot);
+-
+-  setup_mode = grub_efi_get_variable("SetupMode", &efi_var_guid, &datasize);
+-  if (datasize != 1 || !setup_mode)
+-    {
+-      grub_dprintf ("secureboot", "No SetupMode variable\n");
+-      goto out;
+-    }
+-  grub_dprintf ("secureboot", "SetupMode: %d\n", *setup_mode);
+-
+-  if (*secure_boot && !*setup_mode)
+-    ret = 1;
+-
+- out:
+-  grub_free (secure_boot);
+-  grub_free (setup_mode);
+-  return ret;
+-}
+-
+ #pragma GCC diagnostic ignored "-Wcast-align"
+ 
+ /* Search the mods section from the PE32/PE32+ image. This code uses
+diff --git a/grub-core/loader/efi/appleloader.c b/grub-core/loader/efi/appleloader.c
+index 74888c463ba..585f2b57385 100644
+--- a/grub-core/loader/efi/appleloader.c
++++ b/grub-core/loader/efi/appleloader.c
+@@ -24,6 +24,7 @@
+ #include <grub/misc.h>
+ #include <grub/efi/api.h>
+ #include <grub/efi/efi.h>
++#include <grub/efi/sb.h>
+ #include <grub/command.h>
+ #include <grub/i18n.h>
+ 
+@@ -227,6 +228,9 @@ static grub_command_t cmd;
+ 
+ GRUB_MOD_INIT(appleloader)
+ {
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
++    return;
++
+   cmd = grub_register_command ("appleloader", grub_cmd_appleloader,
+ 			       N_("[OPTS]"),
+ 			       /* TRANSLATORS: This command is used on EFI to
+@@ -238,5 +242,8 @@ GRUB_MOD_INIT(appleloader)
+ 
+ GRUB_MOD_FINI(appleloader)
+ {
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
++    return;
++
+   grub_unregister_command (cmd);
+ }
+diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
+index b54cf6986fc..3ff305b1d32 100644
+--- a/grub-core/loader/efi/chainloader.c
++++ b/grub-core/loader/efi/chainloader.c
+@@ -34,6 +34,7 @@
+ #include <grub/efi/disk.h>
+ #include <grub/efi/pe32.h>
+ #include <grub/efi/linux.h>
++#include <grub/efi/sb.h>
+ #include <grub/command.h>
+ #include <grub/i18n.h>
+ #include <grub/net.h>
+diff --git a/grub-core/loader/i386/bsd.c b/grub-core/loader/i386/bsd.c
+index 5f3290ce17b..54befc26626 100644
+--- a/grub-core/loader/i386/bsd.c
++++ b/grub-core/loader/i386/bsd.c
+@@ -40,6 +40,7 @@
+ #ifdef GRUB_MACHINE_PCBIOS
+ #include <grub/machine/int.h>
+ #endif
++#include <grub/efi/sb.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -2137,6 +2138,9 @@ static grub_command_t cmd_netbsd_module_elf, cmd_openbsd_ramdisk;
+ 
+ GRUB_MOD_INIT (bsd)
+ {
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
++    return;
++
+   /* Net and OpenBSD kernels are often compressed.  */
+   grub_dl_load ("gzio");
+ 
+@@ -2176,6 +2180,9 @@ GRUB_MOD_INIT (bsd)
+ 
+ GRUB_MOD_FINI (bsd)
+ {
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
++    return;
++
+   grub_unregister_extcmd (cmd_freebsd);
+   grub_unregister_extcmd (cmd_openbsd);
+   grub_unregister_extcmd (cmd_netbsd);
+diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c
+index dccf3bb3005..4aeb0e4b9a6 100644
+--- a/grub-core/loader/i386/linux.c
++++ b/grub-core/loader/i386/linux.c
+@@ -37,6 +37,7 @@
+ #include <grub/linux.h>
+ #include <grub/machine/kernel.h>
+ #include <grub/safemath.h>
++#include <grub/efi/sb.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -1138,6 +1139,9 @@ static grub_command_t cmd_linux, cmd_initrd;
+ 
+ GRUB_MOD_INIT(linux)
+ {
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
++    return;
++
+   cmd_linux = grub_register_command ("linux", grub_cmd_linux,
+ 				     0, N_("Load Linux."));
+   cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
+@@ -1147,6 +1151,9 @@ GRUB_MOD_INIT(linux)
+ 
+ GRUB_MOD_FINI(linux)
+ {
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
++    return;
++
+   grub_unregister_command (cmd_linux);
+   grub_unregister_command (cmd_initrd);
+ }
+diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c
+index 4b1750e360e..e3fa1221e81 100644
+--- a/grub-core/loader/i386/pc/linux.c
++++ b/grub-core/loader/i386/pc/linux.c
+@@ -36,6 +36,7 @@
+ #include <grub/lib/cmdline.h>
+ #include <grub/linux.h>
+ #include <grub/safemath.h>
++#include <grub/efi/sb.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -487,6 +488,9 @@ static grub_command_t cmd_linux, cmd_linux16, cmd_initrd, cmd_initrd16;
+ 
+ GRUB_MOD_INIT(linux16)
+ {
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
++    return;
++
+   cmd_linux =
+     grub_register_command ("linux", grub_cmd_linux,
+ 			   0, N_("Load Linux."));
+@@ -504,6 +508,9 @@ GRUB_MOD_INIT(linux16)
+ 
+ GRUB_MOD_FINI(linux16)
+ {
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
++    return;
++
+   grub_unregister_command (cmd_linux);
+   grub_unregister_command (cmd_linux16);
+   grub_unregister_command (cmd_initrd);
+diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c
+index facb13f3d36..47e481f4576 100644
+--- a/grub-core/loader/multiboot.c
++++ b/grub-core/loader/multiboot.c
+@@ -50,6 +50,7 @@
+ #include <grub/video.h>
+ #include <grub/memory.h>
+ #include <grub/i18n.h>
++#include <grub/efi/sb.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -444,6 +445,9 @@ static grub_command_t cmd_multiboot, cmd_module;
+ 
+ GRUB_MOD_INIT(multiboot)
+ {
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
++    return;
++
+   cmd_multiboot =
+ #ifdef GRUB_USE_MULTIBOOT2
+     grub_register_command ("multiboot2", grub_cmd_multiboot,
+@@ -464,6 +468,9 @@ GRUB_MOD_INIT(multiboot)
+ 
+ GRUB_MOD_FINI(multiboot)
+ {
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
++    return;
++
+   grub_unregister_command (cmd_multiboot);
+   grub_unregister_command (cmd_module);
+ }
+diff --git a/grub-core/loader/xnu.c b/grub-core/loader/xnu.c
+index 1c0cf6a430a..baa54e652ab 100644
+--- a/grub-core/loader/xnu.c
++++ b/grub-core/loader/xnu.c
+@@ -35,6 +35,7 @@
+ #include <grub/i18n.h>
+ #include <grub/verify.h>
+ #include <grub/safemath.h>
++#include <grub/efi/sb.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -1497,6 +1498,9 @@ static grub_extcmd_t cmd_splash;
+ 
+ GRUB_MOD_INIT(xnu)
+ {
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
++    return;
++
+   cmd_kernel = grub_register_command ("xnu_kernel", grub_cmd_xnu_kernel, 0,
+ 				      N_("Load XNU image."));
+   cmd_kernel64 = grub_register_command ("xnu_kernel64", grub_cmd_xnu_kernel64,
+@@ -1540,6 +1544,9 @@ GRUB_MOD_INIT(xnu)
+ 
+ GRUB_MOD_FINI(xnu)
+ {
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
++    return;
++
+ #ifndef GRUB_MACHINE_EMU
+   grub_unregister_command (cmd_resume);
+ #endif
+diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
+index 6295df85f3f..585fa6662b6 100644
+--- a/include/grub/efi/efi.h
++++ b/include/grub/efi/efi.h
+@@ -91,7 +91,6 @@ EXPORT_FUNC (grub_efi_set_variable) (const char *var,
+ 				     const grub_efi_guid_t *guid,
+ 				     void *data,
+ 				     grub_size_t datasize);
+-grub_efi_boolean_t EXPORT_FUNC (grub_efi_secure_boot) (void);
+ int
+ EXPORT_FUNC (grub_efi_compare_device_paths) (const grub_efi_device_path_t *dp1,
+ 					     const grub_efi_device_path_t *dp2);
+diff --git a/include/grub/ia64/linux.h b/include/grub/ia64/linux.h
+new file mode 100644
+index 00000000000..e69de29bb2d
+diff --git a/include/grub/mips/linux.h b/include/grub/mips/linux.h
+new file mode 100644
+index 00000000000..e69de29bb2d
+diff --git a/include/grub/powerpc/linux.h b/include/grub/powerpc/linux.h
+new file mode 100644
+index 00000000000..e69de29bb2d
+diff --git a/include/grub/sparc64/linux.h b/include/grub/sparc64/linux.h
+new file mode 100644
+index 00000000000..e69de29bb2d
diff --git a/SOURCES/0009-Handle-multi-arch-64-on-32-boot-in-linuxefi-loader.patch b/SOURCES/0009-Handle-multi-arch-64-on-32-boot-in-linuxefi-loader.patch
new file mode 100644
index 0000000..f3aae38
--- /dev/null
+++ b/SOURCES/0009-Handle-multi-arch-64-on-32-boot-in-linuxefi-loader.patch
@@ -0,0 +1,264 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 8 Jul 2019 12:32:37 +0200
+Subject: [PATCH] Handle multi-arch (64-on-32) boot in linuxefi loader.
+
+Allow booting 64-bit kernels on 32-bit EFI on x86.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/loader/efi/linux.c      |   9 +++-
+ grub-core/loader/i386/efi/linux.c | 110 ++++++++++++++++++++++++++------------
+ include/grub/i386/linux.h         |   7 ++-
+ 3 files changed, 89 insertions(+), 37 deletions(-)
+
+diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c
+index c8ecce6dfd0..0622dfa48d4 100644
+--- a/grub-core/loader/efi/linux.c
++++ b/grub-core/loader/efi/linux.c
+@@ -69,12 +69,17 @@ grub_linuxefi_secure_validate (void *data, grub_uint32_t size)
+ typedef void (*handover_func) (void *, grub_efi_system_table_t *, void *);
+ 
+ grub_err_t
+-grub_efi_linux_boot (void *kernel_addr, grub_off_t offset,
++grub_efi_linux_boot (void *kernel_addr, grub_off_t handover_offset,
+ 		     void *kernel_params)
+ {
+   handover_func hf;
++  int offset = 0;
+ 
+-  hf = (handover_func)((char *)kernel_addr + offset);
++#ifdef __x86_64__
++  offset = 512;
++#endif
++
++  hf = (handover_func)((char *)kernel_addr + handover_offset + offset);
+   hf (grub_efi_image_handle, grub_efi_system_table, kernel_params);
+ 
+   return GRUB_ERR_BUG;
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index 6b24cbb9483..3017d0f3e52 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -44,14 +44,10 @@ static char *linux_cmdline;
+ static grub_err_t
+ grub_linuxefi_boot (void)
+ {
+-  int offset = 0;
+-
+-#ifdef __x86_64__
+-  offset = 512;
+-#endif
+   asm volatile ("cli");
+ 
+-  return grub_efi_linux_boot ((char *)kernel_mem, handover_offset + offset,
++  return grub_efi_linux_boot ((char *)kernel_mem,
++			      handover_offset,
+ 			      params);
+ }
+ 
+@@ -153,14 +149,20 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+   return grub_errno;
+ }
+ 
++#define MIN(a, b) \
++  ({ typeof (a) _a = (a);  \
++     typeof (b) _b = (b); \
++     _a < _b ? _a : _b; })
++
+ static grub_err_t
+ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 		int argc, char *argv[])
+ {
+   grub_file_t file = 0;
+-  struct linux_i386_kernel_header lh;
+-  grub_ssize_t len, start, filelen;
++  struct linux_i386_kernel_header *lh = NULL;
++  grub_ssize_t start, filelen;
+   void *kernel = NULL;
++  int setup_header_end_offset;
+   int rc;
+ 
+   grub_dl_ref (my_mod);
+@@ -200,48 +202,79 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+       goto fail;
+     }
+ 
+-  params = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(16384));
+-
++  params = grub_efi_allocate_pages_max (0x3fffffff,
++					BYTES_TO_PAGES(sizeof(*params)));
+   if (! params)
+     {
+       grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters");
+       goto fail;
+     }
+ 
+-  grub_dprintf ("linux", "params = %lx\n", (unsigned long) params);
++  grub_dprintf ("linux", "params = %p\n", params);
+ 
+-  grub_memset (params, 0, 16384);
++  grub_memset (params, 0, sizeof(*params));
+ 
+-  grub_memcpy (&lh, kernel, sizeof (lh));
+-
+-  if (lh.boot_flag != grub_cpu_to_le16 (0xaa55))
++  setup_header_end_offset = *((grub_uint8_t *)kernel + 0x201);
++  grub_dprintf ("linux", "copying %lu bytes from %p to %p\n",
++		MIN((grub_size_t)0x202+setup_header_end_offset,
++		    sizeof (*params)) - 0x1f1,
++		(grub_uint8_t *)kernel + 0x1f1,
++		(grub_uint8_t *)params + 0x1f1);
++  grub_memcpy ((grub_uint8_t *)params + 0x1f1,
++	       (grub_uint8_t *)kernel + 0x1f1,
++		MIN((grub_size_t)0x202+setup_header_end_offset,sizeof (*params)) - 0x1f1);
++  lh = (struct linux_i386_kernel_header *)params;
++  grub_dprintf ("linux", "lh is at %p\n", lh);
++  grub_dprintf ("linux", "checking lh->boot_flag\n");
++  if (lh->boot_flag != grub_cpu_to_le16 (0xaa55))
+     {
+       grub_error (GRUB_ERR_BAD_OS, N_("invalid magic number"));
+       goto fail;
+     }
+ 
+-  if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
++  grub_dprintf ("linux", "checking lh->setup_sects\n");
++  if (lh->setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
+     {
+       grub_error (GRUB_ERR_BAD_OS, N_("too many setup sectors"));
+       goto fail;
+     }
+ 
+-  if (lh.version < grub_cpu_to_le16 (0x020b))
++  grub_dprintf ("linux", "checking lh->version\n");
++  if (lh->version < grub_cpu_to_le16 (0x020b))
+     {
+       grub_error (GRUB_ERR_BAD_OS, N_("kernel too old"));
+       goto fail;
+     }
+ 
+-  if (!lh.handover_offset)
++  grub_dprintf ("linux", "checking lh->handover_offset\n");
++  if (!lh->handover_offset)
+     {
+       grub_error (GRUB_ERR_BAD_OS, N_("kernel doesn't support EFI handover"));
+       goto fail;
+     }
+ 
++#if defined(__x86_64__) || defined(__aarch64__)
++  grub_dprintf ("linux", "checking lh->xloadflags\n");
++  if (!(lh->xloadflags & LINUX_XLF_KERNEL_64))
++    {
++      grub_error (GRUB_ERR_BAD_OS, N_("kernel doesn't support 64-bit CPUs"));
++      goto fail;
++    }
++#endif
++
++#if defined(__i386__)
++  if ((lh->xloadflags & LINUX_XLF_KERNEL_64) &&
++      !(lh->xloadflags & LINUX_XLF_EFI_HANDOVER_32))
++    {
++      grub_error (GRUB_ERR_BAD_OS,
++		  N_("kernel doesn't support 32-bit handover"));
++      goto fail;
++    }
++#endif
++
+   grub_dprintf ("linux", "setting up cmdline\n");
+   linux_cmdline = grub_efi_allocate_pages_max(0x3fffffff,
+-					 BYTES_TO_PAGES(lh.cmdline_size + 1));
+-
++					 BYTES_TO_PAGES(lh->cmdline_size + 1));
+   if (!linux_cmdline)
+     {
+       grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate cmdline"));
+@@ -254,22 +287,24 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
+   grub_create_loader_cmdline (argc, argv,
+                               linux_cmdline + sizeof (LINUX_IMAGE) - 1,
+-			      lh.cmdline_size - (sizeof (LINUX_IMAGE) - 1),
++			      lh->cmdline_size - (sizeof (LINUX_IMAGE) - 1),
+ 			      GRUB_VERIFY_KERNEL_CMDLINE);
+ 
+-  lh.cmd_line_ptr = (grub_uint32_t)(grub_addr_t)linux_cmdline;
++  grub_dprintf ("linux", "cmdline:%s\n", linux_cmdline);
++  grub_dprintf ("linux", "setting lh->cmd_line_ptr\n");
++  lh->cmd_line_ptr = (grub_uint32_t)(grub_addr_t)linux_cmdline;
+ 
+-  handover_offset = lh.handover_offset;
++  grub_dprintf ("linux", "computing handover offset\n");
++  handover_offset = lh->handover_offset;
+ 
+-  start = (lh.setup_sects + 1) * 512;
+-  len = grub_file_size(file) - start;
++  start = (lh->setup_sects + 1) * 512;
+ 
+-  kernel_mem = grub_efi_allocate_pages_max(lh.pref_address,
+-					   BYTES_TO_PAGES(lh.init_size));
++  kernel_mem = grub_efi_allocate_pages_max(lh->pref_address,
++					   BYTES_TO_PAGES(lh->init_size));
+ 
+   if (!kernel_mem)
+     kernel_mem = grub_efi_allocate_pages_max(0x3fffffff,
+-					     BYTES_TO_PAGES(lh.init_size));
++					     BYTES_TO_PAGES(lh->init_size));
+ 
+   if (!kernel_mem)
+     {
+@@ -277,14 +312,21 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+       goto fail;
+     }
+ 
+-  grub_memcpy (kernel_mem, (char *)kernel + start, len);
++  grub_dprintf ("linux", "kernel_mem = %lx\n", (unsigned long) kernel_mem);
++
+   grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0);
+   loaded=1;
++  grub_dprintf ("linux", "setting lh->code32_start to %p\n", kernel_mem);
++  lh->code32_start = (grub_uint32_t)(grub_addr_t) kernel_mem;
+ 
+-  lh.code32_start = (grub_uint32_t)(grub_uint64_t) kernel_mem;
+-  grub_memcpy (params, &lh, 2 * 512);
++  grub_memcpy (kernel_mem, (char *)kernel + start, filelen - start);
+ 
+-  params->type_of_loader = 0x21;
++  grub_dprintf ("linux", "setting lh->type_of_loader\n");
++  lh->type_of_loader = 0x6;
++
++  grub_dprintf ("linux", "setting lh->ext_loader_{type,ver}\n");
++  params->ext_loader_type = 0;
++  params->ext_loader_ver = 2;
+   grub_dprintf("linux", "kernel_mem: %p handover_offset: %08x\n",
+ 	       kernel_mem, handover_offset);
+ 
+@@ -301,10 +343,10 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+       loaded = 0;
+     }
+ 
+-  if (linux_cmdline && !loaded)
++  if (linux_cmdline && lh && !loaded)
+     grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)
+ 			 linux_cmdline,
+-			 BYTES_TO_PAGES(lh.cmdline_size + 1));
++			 BYTES_TO_PAGES(lh->cmdline_size + 1));
+ 
+   if (kernel_mem && !loaded)
+     grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)kernel_mem,
+diff --git a/include/grub/i386/linux.h b/include/grub/i386/linux.h
+index eddf9251d9a..25ef52c04eb 100644
+--- a/include/grub/i386/linux.h
++++ b/include/grub/i386/linux.h
+@@ -138,7 +138,12 @@ struct linux_i386_kernel_header
+   grub_uint32_t kernel_alignment;
+   grub_uint8_t relocatable;
+   grub_uint8_t min_alignment;
+-  grub_uint8_t pad[2];
++#define LINUX_XLF_KERNEL_64                   (1<<0)
++#define LINUX_XLF_CAN_BE_LOADED_ABOVE_4G      (1<<1)
++#define LINUX_XLF_EFI_HANDOVER_32             (1<<2)
++#define LINUX_XLF_EFI_HANDOVER_64             (1<<3)
++#define LINUX_XLF_EFI_KEXEC                   (1<<4)
++  grub_uint16_t xloadflags;
+   grub_uint32_t cmdline_size;
+   grub_uint32_t hardware_subarch;
+   grub_uint64_t hardware_subarch_data;
diff --git a/SOURCES/0010-re-write-.gitignore.patch b/SOURCES/0010-re-write-.gitignore.patch
new file mode 100644
index 0000000..74cb0da
--- /dev/null
+++ b/SOURCES/0010-re-write-.gitignore.patch
@@ -0,0 +1,248 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 8 Jul 2019 12:55:29 +0200
+Subject: [PATCH] re-write .gitignore
+
+---
+ .gitignore                        | 152 ++++++++++++++++++++++++++++++++++++++
+ docs/.gitignore                   |   5 ++
+ grub-core/.gitignore              |  16 ++++
+ grub-core/lib/.gitignore          |   1 +
+ include/grub/gcrypt/.gitignore    |   2 +
+ po/.gitignore                     |   5 ++
+ util/bash-completion.d/.gitignore |   2 +
+ 7 files changed, 183 insertions(+)
+ create mode 100644 docs/.gitignore
+ create mode 100644 grub-core/.gitignore
+ create mode 100644 grub-core/lib/.gitignore
+ create mode 100644 include/grub/gcrypt/.gitignore
+ create mode 100644 po/.gitignore
+ create mode 100644 util/bash-completion.d/.gitignore
+
+diff --git a/.gitignore b/.gitignore
+index f6a1bd05175..594d0134d33 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -275,3 +275,155 @@ widthspec.bin
+ /xfs_test
+ /xzcompress_test
+ /zfs_test
++=======
++# things ./autogen.sh will create
++/Makefile.utilgcry.def
++/ABOUT-NLS
++/aclocal.m4
++/autom4te.cache
++/build-aux
++/configure
++/gnulib
++/grub-core/lib/gnulib/
++/Makefile
++
++# things very common editors create that we never want
++*~
++.*.sw?
++*.patch
++
++# stuff you're likely to make while building test trees
++grub.cfg
++/build*/
++
++# built objects across the whole tree
++Makefile.in
++*.a
++*.am
++*.efi
++*.exec
++*.image
++*.img
++*.info
++*.lst
++*.marker
++/m4
++*.mod
++*.module
++*.o
++*.pf2
++*.yy.[ch]
++.deps/
++.deps-core/
++.deps-util/
++.dirstamp
++
++# next are things you get if you do ./configure in the topdir (for e.g.
++# "make dist" invocation.
++/config-util.h
++/config.h
++/include/grub/cpu
++/include/grub/machine
++/INSTALL
++/INSTALL.grub
++/po/Makefile.in.in
++/po/Makevars
++/po/Makevars.template
++/po/POTFILES
++/po/Rules-quot
++/stamp-h
++/stamp-h1
++bootstrap.log
++config.log
++config.status
++
++# stuff "make dist" creates
++ChangeLog
++grub-*.tar
++grub-*.tar.*
++
++# stuff "make" creates
++/[[:digit:]][[:digit:]]_?*
++/ascii.h
++/build-grub-gen-asciih
++/build-grub-gen-widthspec
++/build-grub-mkfont
++/config-util.h.in
++/garbage-gen
++/grub*-bios-setup
++/grub*-bios-setup.8
++/grub*-editenv
++/grub*-editenv.1
++/grub*-file
++/grub*-file.1
++/grub*-fs-tester
++/grub*-fstest
++/grub*-fstest.1
++/grub*-get-kernel-settings
++/grub*-get-kernel-settings.3
++/grub*-glue-efi
++/grub*-glue-efi.1
++/grub*-install
++/grub*-install.8
++/grub*-kbdcomp
++/grub*-kbdcomp.1
++/grub*-macbless
++/grub*-macbless.8
++/grub*-menulst2cfg
++/grub*-menulst2cfg.1
++/grub*-mount
++/grub*-mount.1
++/grub*-mkconfig
++/grub*-mkconfig.8
++/grub*-mkconfig_lib
++/grub*-mkfont
++/grub*-mkfont.1
++/grub*-mkimage
++/grub*-mkimage.1
++/grub*-mklayout
++/grub*-mklayout.1
++/grub*-mknetdir
++/grub*-mknetdir.1
++/grub*-mkpasswd-pbkdf2
++/grub*-mkpasswd-pbkdf2.1
++/grub*-mkrelpath
++/grub*-mkrelpath.1
++/grub*-mkrescue
++/grub*-mkrescue.1
++/grub*-mkstandalone
++/grub*-mkstandalone.1
++/grub*-ofpathname
++/grub*-ofpathname.8
++/grub*-probe
++/grub*-probe.8
++/grub*-reboot
++/grub*-reboot.8
++/grub*-render-label
++/grub*-render-label.1
++/grub*-rpm-sort
++/grub*-rpm-sort.8
++/grub*-script-check
++/grub*-script-check.1
++/grub*-set-bootflag
++/grub*-set-bootflag.1
++/grub*-set-default
++/grub*-set-default.8
++/grub*-set-password
++/grub*-set-password.8
++/grub*-shell
++/grub*-shell-tester
++/grub*-sparc64-setup
++/grub*-sparc64-setup.8
++/grub*-syslinux2cfg
++/grub*-syslinux2cfg.1
++/grub*-switch-to-blscfg
++/grub*-switch-to-blscfg.8
++/grub_fstest.pp
++/grub_fstest_init.c
++/grub_fstest_init.lst
++/grub_script.tab.[ch]
++/libgrub.pp
++/libgrub_a_init.c
++/libgrub_a_init.lst
++/stamp-h.in
++/widthspec.h
+diff --git a/docs/.gitignore b/docs/.gitignore
+new file mode 100644
+index 00000000000..e1d849ef95b
+--- /dev/null
++++ b/docs/.gitignore
+@@ -0,0 +1,5 @@
++/*.in
++/Makefile
++/stamp-1
++/stamp-vti
++/version*.texi
+diff --git a/grub-core/.gitignore b/grub-core/.gitignore
+new file mode 100644
+index 00000000000..2acce281159
+--- /dev/null
++++ b/grub-core/.gitignore
+@@ -0,0 +1,16 @@
++/*.lst
++/Makefile
++/Makefile.gcry.def
++/unidata.c
++/build-grub-module-verifier
++/gdb_grub
++/genmod.sh
++/gensyminfo.sh
++/gentrigtables
++/gmodule.pl
++/grub_script.tab.[ch]
++/modinfo.sh
++/rs_decoder.h
++/symlist.c
++/symlist.h
++/trigtables.c
+diff --git a/grub-core/lib/.gitignore b/grub-core/lib/.gitignore
+new file mode 100644
+index 00000000000..68154591404
+--- /dev/null
++++ b/grub-core/lib/.gitignore
+@@ -0,0 +1 @@
++/libgcrypt-grub/
+diff --git a/include/grub/gcrypt/.gitignore b/include/grub/gcrypt/.gitignore
+new file mode 100644
+index 00000000000..8fbf5646246
+--- /dev/null
++++ b/include/grub/gcrypt/.gitignore
+@@ -0,0 +1,2 @@
++g10lib.h
++gcrypt.h
+diff --git a/po/.gitignore b/po/.gitignore
+new file mode 100644
+index 00000000000..f507e7741e3
+--- /dev/null
++++ b/po/.gitignore
+@@ -0,0 +1,5 @@
++/Makefile
++/POTFILES*.in
++/grub.pot
++/remove-potcdate.sed
++/stamp-po
+diff --git a/util/bash-completion.d/.gitignore b/util/bash-completion.d/.gitignore
+new file mode 100644
+index 00000000000..6813a527ad3
+--- /dev/null
++++ b/util/bash-completion.d/.gitignore
+@@ -0,0 +1,2 @@
++Makefile
++grub
diff --git a/SOURCES/0011-IBM-client-architecture-CAS-reboot-support.patch b/SOURCES/0011-IBM-client-architecture-CAS-reboot-support.patch
new file mode 100644
index 0000000..eaaf9a4
--- /dev/null
+++ b/SOURCES/0011-IBM-client-architecture-CAS-reboot-support.patch
@@ -0,0 +1,172 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Paulo Flabiano Smorigo <pfsmorigo@br.ibm.com>
+Date: Thu, 20 Sep 2012 18:07:39 -0300
+Subject: [PATCH] IBM client architecture (CAS) reboot support
+
+This is an implementation of IBM client architecture (CAS) reboot for GRUB.
+
+There are cases where the POWER firmware must reboot in order to support
+specific features requested by a kernel. The kernel calls
+ibm,client-architecture-support and it may either return or reboot with the new
+feature set. eg:
+
+Calling ibm,client-architecture-support.../
+Elapsed time since release of system processors: 70959 mins 50 secs
+Welcome to GRUB!
+
+Instead of return to the GRUB menu, it will check if the flag for CAS reboot is
+set. If so, grub will automatically boot the last booted kernel using the same
+parameters
+---
+ grub-core/kern/ieee1275/openfw.c | 63 ++++++++++++++++++++++++++++++++++++++++
+ grub-core/normal/main.c          | 19 ++++++++++++
+ grub-core/script/execute.c       |  7 +++++
+ include/grub/ieee1275/ieee1275.h |  2 ++
+ 4 files changed, 91 insertions(+)
+
+diff --git a/grub-core/kern/ieee1275/openfw.c b/grub-core/kern/ieee1275/openfw.c
+index 4d493ab7661..3a6689abb11 100644
+--- a/grub-core/kern/ieee1275/openfw.c
++++ b/grub-core/kern/ieee1275/openfw.c
+@@ -591,3 +591,66 @@ grub_ieee1275_get_boot_dev (void)
+ 
+   return bootpath;
+ }
++
++/* Check if it's a CAS reboot. If so, set the script to be executed.  */
++int
++grub_ieee1275_cas_reboot (char *script)
++{
++  grub_uint32_t ibm_ca_support_reboot;
++  grub_uint32_t ibm_fw_nbr_reboots;
++  char property_value[10];
++  grub_ssize_t actual;
++  grub_ieee1275_ihandle_t options;
++
++  if (grub_ieee1275_finddevice ("/options", &options) < 0)
++    return -1;
++
++  /* Check two properties, one is enough to get cas reboot value */
++  ibm_ca_support_reboot = 0;
++  if (grub_ieee1275_get_integer_property (grub_ieee1275_chosen,
++                                          "ibm,client-architecture-support-reboot",
++                                          &ibm_ca_support_reboot,
++                                          sizeof (ibm_ca_support_reboot),
++                                          &actual) >= 0)
++    grub_dprintf("ieee1275", "ibm,client-architecture-support-reboot: %u\n",
++                 ibm_ca_support_reboot);
++
++  ibm_fw_nbr_reboots = 0;
++  if (grub_ieee1275_get_property (options, "ibm,fw-nbr-reboots",
++                                  property_value, sizeof (property_value),
++                                  &actual) >= 0)
++    {
++      property_value[sizeof (property_value) - 1] = 0;
++      ibm_fw_nbr_reboots = (grub_uint8_t) grub_strtoul (property_value, 0, 10);
++      grub_dprintf("ieee1275", "ibm,fw-nbr-reboots: %u\n", ibm_fw_nbr_reboots);
++    }
++
++  if (ibm_ca_support_reboot || ibm_fw_nbr_reboots)
++    {
++      if (! grub_ieee1275_get_property_length (options, "boot-last-label", &actual))
++        {
++          if (actual > 1024)
++            script = grub_realloc (script, actual + 1);
++          grub_ieee1275_get_property (options, "boot-last-label", script, actual,
++                                      &actual);
++          return 0;
++        }
++    }
++
++  grub_ieee1275_set_boot_last_label ("");
++
++  return -1;
++}
++
++int grub_ieee1275_set_boot_last_label (const char *text)
++{
++  grub_ieee1275_ihandle_t options;
++  grub_ssize_t actual;
++
++  grub_dprintf("ieee1275", "set boot_last_label (size: %u)\n", grub_strlen(text));
++  if (! grub_ieee1275_finddevice ("/options", &options) &&
++      options != (grub_ieee1275_ihandle_t) -1)
++    grub_ieee1275_set_property (options, "boot-last-label", text,
++                                grub_strlen (text), &actual);
++  return 0;
++}
+diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
+index c4ebe9e22ad..70614de1565 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -34,6 +34,9 @@
+ #include <grub/charset.h>
+ #include <grub/script_sh.h>
+ #include <grub/bufio.h>
++#ifdef GRUB_MACHINE_IEEE1275
++#include <grub/ieee1275/ieee1275.h>
++#endif
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -276,6 +279,22 @@ grub_normal_execute (const char *config, int nested, int batch)
+     {
+       menu = read_config_file (config);
+ 
++#ifdef GRUB_MACHINE_IEEE1275
++      int boot;
++      boot = 0;
++      char *script;
++      script = grub_malloc (1024);
++      if (! grub_ieee1275_cas_reboot (script))
++        {
++          char *dummy[1] = { NULL };
++          if (! grub_script_execute_sourcecode (script))
++            boot = 1;
++        }
++      grub_free (script);
++      if (boot)
++        grub_command_execute ("boot", 0, 0);
++#endif
++
+       /* Ignore any error.  */
+       grub_errno = GRUB_ERR_NONE;
+     }
+diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c
+index 25158407dd8..ad80399246a 100644
+--- a/grub-core/script/execute.c
++++ b/grub-core/script/execute.c
+@@ -28,6 +28,9 @@
+ #include <grub/extcmd.h>
+ #include <grub/i18n.h>
+ #include <grub/verify.h>
++#ifdef GRUB_MACHINE_IEEE1275
++#include <grub/ieee1275/ieee1275.h>
++#endif
+ 
+ /* Max digits for a char is 3 (0xFF is 255), similarly for an int it
+    is sizeof (int) * 3, and one extra for a possible -ve sign.  */
+@@ -883,6 +886,10 @@ grub_script_execute_sourcecode (const char *source)
+   grub_err_t ret = 0;
+   struct grub_script *parsed_script;
+ 
++#ifdef GRUB_MACHINE_IEEE1275
++  grub_ieee1275_set_boot_last_label (source);
++#endif
++
+   while (source)
+     {
+       char *line;
+diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h
+index 73e2f464475..0a599607f31 100644
+--- a/include/grub/ieee1275/ieee1275.h
++++ b/include/grub/ieee1275/ieee1275.h
+@@ -254,6 +254,8 @@ int EXPORT_FUNC(grub_ieee1275_devalias_next) (struct grub_ieee1275_devalias *ali
+ void EXPORT_FUNC(grub_ieee1275_children_peer) (struct grub_ieee1275_devalias *alias);
+ void EXPORT_FUNC(grub_ieee1275_children_first) (const char *devpath,
+ 						struct grub_ieee1275_devalias *alias);
++int EXPORT_FUNC(grub_ieee1275_cas_reboot) (char *script);
++int EXPORT_FUNC(grub_ieee1275_set_boot_last_label) (const char *text);
+ 
+ char *EXPORT_FUNC(grub_ieee1275_get_boot_dev) (void);
+ 
diff --git a/SOURCES/0012-for-ppc-reset-console-display-attr-when-clear-screen.patch b/SOURCES/0012-for-ppc-reset-console-display-attr-when-clear-screen.patch
new file mode 100644
index 0000000..c8bb9d3
--- /dev/null
+++ b/SOURCES/0012-for-ppc-reset-console-display-attr-when-clear-screen.patch
@@ -0,0 +1,29 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Paulo Flabiano Smorigo <pfsmorigo@br.ibm.com>
+Date: Wed, 24 Apr 2013 10:51:48 -0300
+Subject: [PATCH] for ppc, reset console display attr when clear screen
+
+v2: Also use \x0c instead of a literal ^L to make future patches less
+awkward.
+
+This should fix this bugzilla:
+https://bugzilla.redhat.com/show_bug.cgi?id=908519
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/term/terminfo.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/term/terminfo.c b/grub-core/term/terminfo.c
+index 85ecf06b4df..05c88dcf49e 100644
+--- a/grub-core/term/terminfo.c
++++ b/grub-core/term/terminfo.c
+@@ -151,7 +151,7 @@ grub_terminfo_set_current (struct grub_term_output *term,
+       /* Clear the screen.  Using serial console, screen(1) only recognizes the
+        * ANSI escape sequence.  Using video console, Apple Open Firmware
+        * (version 3.1.1) only recognizes the literal ^L.  So use both.  */
+-      data->cls               = grub_strdup ("\e[2J");
++      data->cls               = grub_strdup ("\x0c\e[2J\e[m");
+       data->reverse_video_on  = grub_strdup ("\e[7m");
+       data->reverse_video_off = grub_strdup ("\e[m");
+       if (grub_strcmp ("ieee1275", str) == 0)
diff --git a/SOURCES/0013-Disable-GRUB-video-support-for-IBM-power-machines.patch b/SOURCES/0013-Disable-GRUB-video-support-for-IBM-power-machines.patch
new file mode 100644
index 0000000..430f600
--- /dev/null
+++ b/SOURCES/0013-Disable-GRUB-video-support-for-IBM-power-machines.patch
@@ -0,0 +1,62 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Paulo Flabiano Smorigo <pfsmorigo@br.ibm.com>
+Date: Tue, 11 Jun 2013 15:14:05 -0300
+Subject: [PATCH] Disable GRUB video support for IBM power machines
+
+Should fix the problem in bugzilla:
+https://bugzilla.redhat.com/show_bug.cgi?id=973205
+---
+ grub-core/kern/ieee1275/cmain.c  | 5 ++++-
+ grub-core/video/ieee1275.c       | 9 ++++++---
+ include/grub/ieee1275/ieee1275.h | 2 ++
+ 3 files changed, 12 insertions(+), 4 deletions(-)
+
+diff --git a/grub-core/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c
+index 20cbbd761ec..04df9d2c667 100644
+--- a/grub-core/kern/ieee1275/cmain.c
++++ b/grub-core/kern/ieee1275/cmain.c
+@@ -90,7 +90,10 @@ grub_ieee1275_find_options (void)
+   }
+ 
+   if (rc >= 0 && grub_strncmp (tmp, "IBM", 3) == 0)
+-    grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_TREE_SCANNING_FOR_DISKS);
++    {
++      grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_TREE_SCANNING_FOR_DISKS);
++      grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT);
++    }
+ 
+   /* Old Macs have no key repeat, newer ones have fully working one.
+      The ones inbetween when repeated key generates an escaoe sequence
+diff --git a/grub-core/video/ieee1275.c b/grub-core/video/ieee1275.c
+index 17a3dbbb575..b8e4b3feb32 100644
+--- a/grub-core/video/ieee1275.c
++++ b/grub-core/video/ieee1275.c
+@@ -352,9 +352,12 @@ static struct grub_video_adapter grub_video_ieee1275_adapter =
+ 
+ GRUB_MOD_INIT(ieee1275_fb)
+ {
+-  find_display ();
+-  if (display)
+-    grub_video_register (&grub_video_ieee1275_adapter);
++  if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT))
++    {
++      find_display ();
++      if (display)
++        grub_video_register (&grub_video_ieee1275_adapter);
++    }
+ }
+ 
+ GRUB_MOD_FINI(ieee1275_fb)
+diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h
+index 0a599607f31..b5a1d49bbc3 100644
+--- a/include/grub/ieee1275/ieee1275.h
++++ b/include/grub/ieee1275/ieee1275.h
+@@ -148,6 +148,8 @@ enum grub_ieee1275_flag
+   GRUB_IEEE1275_FLAG_CURSORONOFF_ANSI_BROKEN,
+ 
+   GRUB_IEEE1275_FLAG_RAW_DEVNAMES,
++  
++  GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT
+ };
+ 
+ extern int EXPORT_FUNC(grub_ieee1275_test_flag) (enum grub_ieee1275_flag flag);
diff --git a/SOURCES/0014-Move-bash-completion-script-922997.patch b/SOURCES/0014-Move-bash-completion-script-922997.patch
new file mode 100644
index 0000000..6bf0b4b
--- /dev/null
+++ b/SOURCES/0014-Move-bash-completion-script-922997.patch
@@ -0,0 +1,52 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Wed, 3 Apr 2013 14:35:34 -0400
+Subject: [PATCH] Move bash completion script (#922997)
+
+Apparently these go in a new place now.
+---
+ configure.ac                       | 11 +++++++++++
+ util/bash-completion.d/Makefile.am |  1 -
+ 2 files changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/configure.ac b/configure.ac
+index 7517fc49d98..8331f95b645 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -314,6 +314,14 @@ AC_SUBST(grubdirname)
+ AC_DEFINE_UNQUOTED(GRUB_DIR_NAME, "$grubdirname",
+     [Default grub directory name])
+ 
++PKG_PROG_PKG_CONFIG
++AS_IF([$($PKG_CONFIG --exists bash-completion)], [
++	bashcompletiondir=$($PKG_CONFIG --variable=completionsdir bash-completion)
++] , [
++	bashcompletiondir=${datadir}/bash-completion/completions
++])
++AC_SUBST(bashcompletiondir)
++
+ #
+ # Checks for build programs.
+ #
+@@ -525,6 +533,9 @@ HOST_CFLAGS="$HOST_CFLAGS $grub_cv_cc_w_extra_flags"
+ # Check for target programs.
+ #
+ 
++# This makes sure pkg.m4 is available.
++m4_pattern_forbid([^_?PKG_[A-Z_]+$],[*** pkg.m4 missing, please install pkg-config])
++
+ # Find tools for the target.
+ if test "x$target_alias" != x && test "x$host_alias" != "x$target_alias"; then
+   tmp_ac_tool_prefix="$ac_tool_prefix"
+diff --git a/util/bash-completion.d/Makefile.am b/util/bash-completion.d/Makefile.am
+index 136287cf1bf..61108f05429 100644
+--- a/util/bash-completion.d/Makefile.am
++++ b/util/bash-completion.d/Makefile.am
+@@ -6,7 +6,6 @@ EXTRA_DIST = $(bash_completion_source)
+ 
+ CLEANFILES = $(bash_completion_script) config.log
+ 
+-bashcompletiondir = $(sysconfdir)/bash_completion.d
+ bashcompletion_DATA = $(bash_completion_script)
+ 
+ $(bash_completion_script): $(bash_completion_source) $(top_builddir)/config.status
diff --git a/SOURCES/0015-Allow-fallback-to-include-entries-by-title-not-just-.patch b/SOURCES/0015-Allow-fallback-to-include-entries-by-title-not-just-.patch
new file mode 100644
index 0000000..f2a4a82
--- /dev/null
+++ b/SOURCES/0015-Allow-fallback-to-include-entries-by-title-not-just-.patch
@@ -0,0 +1,140 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Fri, 5 Sep 2014 10:07:04 -0400
+Subject: [PATCH] Allow "fallback" to include entries by title, not just
+ number.
+
+Resolves: rhbz#1026084
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/normal/menu.c | 85 +++++++++++++++++++++++++++++++++----------------
+ 1 file changed, 58 insertions(+), 27 deletions(-)
+
+diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c
+index 8397886fa05..d7a222e681b 100644
+--- a/grub-core/normal/menu.c
++++ b/grub-core/normal/menu.c
+@@ -163,15 +163,40 @@ grub_menu_set_timeout (int timeout)
+     }
+ }
+ 
++static int
++menuentry_eq (const char *id, const char *spec)
++{
++  const char *ptr1, *ptr2;
++  ptr1 = id;
++  ptr2 = spec;
++  while (1)
++    {
++      if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0)
++	return ptr2 - spec;
++      if (*ptr2 == '>' && ptr2[1] != '>')
++	return 0;
++      if (*ptr2 == '>')
++	ptr2++;
++      if (*ptr1 != *ptr2)
++	return 0;
++      if (*ptr1 == 0)
++	return ptr1 - id;
++      ptr1++;
++      ptr2++;
++    }
++  return 0;
++}
++
+ /* Get the first entry number from the value of the environment variable NAME,
+    which is a space-separated list of non-negative integers.  The entry number
+    which is returned is stripped from the value of NAME.  If no entry number
+    can be found, -1 is returned.  */
+ static int
+-get_and_remove_first_entry_number (const char *name)
++get_and_remove_first_entry_number (grub_menu_t menu, const char *name)
+ {
+   const char *val, *tail;
+   int entry;
++  int sz = 0;
+ 
+   val = grub_env_get (name);
+   if (! val)
+@@ -181,9 +206,39 @@ get_and_remove_first_entry_number (const char *name)
+ 
+   entry = (int) grub_strtoul (val, &tail, 0);
+ 
++  if (grub_errno == GRUB_ERR_BAD_NUMBER)
++    {
++      /* See if the variable matches the title of a menu entry.  */
++      grub_menu_entry_t e = menu->entry_list;
++      int i;
++
++      for (i = 0; e; i++)
++	{
++	  sz = menuentry_eq (e->title, val);
++	  if (sz < 1)
++	    sz = menuentry_eq (e->id, val);
++
++	  if (sz >= 1)
++	    {
++	      entry = i;
++	      break;
++	    }
++	  e = e->next;
++	}
++
++      if (sz > 0)
++	grub_errno = GRUB_ERR_NONE;
++
++      if (! e)
++	entry = -1;
++    }
++
+   if (grub_errno == GRUB_ERR_NONE)
+     {
+-      /* Skip whitespace to find the next digit.  */
++      if (sz > 0)
++	tail += sz;
++
++      /* Skip whitespace to find the next entry.  */
+       while (*tail && grub_isspace (*tail))
+ 	tail++;
+       grub_env_set (name, tail);
+@@ -346,7 +401,7 @@ grub_menu_execute_with_fallback (grub_menu_t menu,
+   grub_menu_execute_entry (entry, 1);
+ 
+   /* Deal with fallback entries.  */
+-  while ((fallback_entry = get_and_remove_first_entry_number ("fallback"))
++  while ((fallback_entry = get_and_remove_first_entry_number (menu, "fallback"))
+ 	 >= 0)
+     {
+       grub_print_error ();
+@@ -464,30 +519,6 @@ grub_menu_register_viewer (struct grub_menu_viewer *viewer)
+   viewers = viewer;
+ }
+ 
+-static int
+-menuentry_eq (const char *id, const char *spec)
+-{
+-  const char *ptr1, *ptr2;
+-  ptr1 = id;
+-  ptr2 = spec;
+-  while (1)
+-    {
+-      if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0)
+-	return 1;
+-      if (*ptr2 == '>' && ptr2[1] != '>')
+-	return 0;
+-      if (*ptr2 == '>')
+-	ptr2++;
+-      if (*ptr1 != *ptr2)
+-	return 0;
+-      if (*ptr1 == 0)
+-	return 1;
+-      ptr1++;
+-      ptr2++;
+-    }
+-}
+-
+-
+ /* Get the entry number from the variable NAME.  */
+ static int
+ get_entry_number (grub_menu_t menu, const char *name)
diff --git a/SOURCES/0016-Make-exit-take-a-return-code.patch b/SOURCES/0016-Make-exit-take-a-return-code.patch
new file mode 100644
index 0000000..39d7057
--- /dev/null
+++ b/SOURCES/0016-Make-exit-take-a-return-code.patch
@@ -0,0 +1,267 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Wed, 26 Feb 2014 21:49:12 -0500
+Subject: [PATCH] Make "exit" take a return code.
+
+This adds "exit" with a return code.  With this patch, any "exit"
+command /may/ include a return code, and on platforms that support
+returning with an exit status, we will do so.  By default we return the
+same exit status we did before this patch.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/commands/minicmd.c         | 20 ++++++++++++++++----
+ grub-core/kern/efi/efi.c             |  9 +++++++--
+ grub-core/kern/emu/main.c            |  2 +-
+ grub-core/kern/emu/misc.c            |  5 +++--
+ grub-core/kern/i386/coreboot/init.c  |  2 +-
+ grub-core/kern/i386/qemu/init.c      |  2 +-
+ grub-core/kern/ieee1275/init.c       |  2 +-
+ grub-core/kern/mips/arc/init.c       |  2 +-
+ grub-core/kern/mips/loongson/init.c  |  2 +-
+ grub-core/kern/mips/qemu_mips/init.c |  2 +-
+ grub-core/kern/misc.c                | 11 ++++++++++-
+ grub-core/kern/uboot/init.c          |  6 +++---
+ grub-core/kern/xen/init.c            |  2 +-
+ include/grub/misc.h                  |  2 +-
+ 14 files changed, 48 insertions(+), 21 deletions(-)
+
+diff --git a/grub-core/commands/minicmd.c b/grub-core/commands/minicmd.c
+index fa498931ed2..2bd3ac76f2d 100644
+--- a/grub-core/commands/minicmd.c
++++ b/grub-core/commands/minicmd.c
+@@ -182,12 +182,24 @@ grub_mini_cmd_lsmod (struct grub_command *cmd __attribute__ ((unused)),
+ }
+ 
+ /* exit */
+-static grub_err_t __attribute__ ((noreturn))
++static grub_err_t
+ grub_mini_cmd_exit (struct grub_command *cmd __attribute__ ((unused)),
+-		    int argc __attribute__ ((unused)),
+-		    char *argv[] __attribute__ ((unused)))
++		    int argc, char *argv[])
+ {
+-  grub_exit ();
++  int retval = -1;
++  unsigned long n;
++
++  if (argc < 0 || argc > 1)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
++
++  if (argc == 1)
++    {
++      n = grub_strtoul (argv[0], 0, 10);
++      if (n != ~0UL)
++	retval = n;
++    }
++
++  grub_exit (retval);
+   /* Not reached.  */
+ }
+ 
+diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
+index 8cff7be0289..05d8237a9b2 100644
+--- a/grub-core/kern/efi/efi.c
++++ b/grub-core/kern/efi/efi.c
+@@ -165,11 +165,16 @@ grub_reboot (void)
+ }
+ 
+ void
+-grub_exit (void)
++grub_exit (int retval)
+ {
++  int rc = GRUB_EFI_LOAD_ERROR;
++
++  if (retval == 0)
++    rc = GRUB_EFI_SUCCESS;
++
+   grub_machine_fini (GRUB_LOADER_FLAG_NORETURN);
+   efi_call_4 (grub_efi_system_table->boot_services->exit,
+-              grub_efi_image_handle, GRUB_EFI_SUCCESS, 0, 0);
++              grub_efi_image_handle, rc, 0, 0);
+   for (;;) ;
+ }
+ 
+diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c
+index 425bb960347..55ea5a11ccd 100644
+--- a/grub-core/kern/emu/main.c
++++ b/grub-core/kern/emu/main.c
+@@ -67,7 +67,7 @@ grub_reboot (void)
+ }
+ 
+ void
+-grub_exit (void)
++grub_exit (int retval __attribute__((unused)))
+ {
+   grub_reboot ();
+ }
+diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c
+index dfd8a8ec488..0ff13bcaf8c 100644
+--- a/grub-core/kern/emu/misc.c
++++ b/grub-core/kern/emu/misc.c
+@@ -151,9 +151,10 @@ xasprintf (const char *fmt, ...)
+ 
+ #if !defined (GRUB_MACHINE_EMU) || defined (GRUB_UTIL)
+ void
+-grub_exit (void)
++__attribute__ ((noreturn))
++grub_exit (int rc)
+ {
+-  exit (1);
++  exit (rc < 0 ? 1 : rc);
+ }
+ #endif
+ 
+diff --git a/grub-core/kern/i386/coreboot/init.c b/grub-core/kern/i386/coreboot/init.c
+index 3314f027fec..36f9134b7b7 100644
+--- a/grub-core/kern/i386/coreboot/init.c
++++ b/grub-core/kern/i386/coreboot/init.c
+@@ -41,7 +41,7 @@ extern grub_uint8_t _end[];
+ extern grub_uint8_t _edata[];
+ 
+ void  __attribute__ ((noreturn))
+-grub_exit (void)
++grub_exit (int rc __attribute__((unused)))
+ {
+   /* We can't use grub_fatal() in this function.  This would create an infinite
+      loop, since grub_fatal() calls grub_abort() which in turn calls grub_exit().  */
+diff --git a/grub-core/kern/i386/qemu/init.c b/grub-core/kern/i386/qemu/init.c
+index 271b6fbfabd..9fafe98f015 100644
+--- a/grub-core/kern/i386/qemu/init.c
++++ b/grub-core/kern/i386/qemu/init.c
+@@ -42,7 +42,7 @@ extern grub_uint8_t _end[];
+ extern grub_uint8_t _edata[];
+ 
+ void  __attribute__ ((noreturn))
+-grub_exit (void)
++grub_exit (int rc __attribute__((unused)))
+ {
+   /* We can't use grub_fatal() in this function.  This would create an infinite
+      loop, since grub_fatal() calls grub_abort() which in turn calls grub_exit().  */
+diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
+index d483e35eed2..e71d1584164 100644
+--- a/grub-core/kern/ieee1275/init.c
++++ b/grub-core/kern/ieee1275/init.c
+@@ -71,7 +71,7 @@ grub_addr_t grub_ieee1275_original_stack;
+ #endif
+ 
+ void
+-grub_exit (void)
++grub_exit (int rc __attribute__((unused)))
+ {
+   grub_ieee1275_exit ();
+ }
+diff --git a/grub-core/kern/mips/arc/init.c b/grub-core/kern/mips/arc/init.c
+index 2ed3ff3191e..5c40c34078d 100644
+--- a/grub-core/kern/mips/arc/init.c
++++ b/grub-core/kern/mips/arc/init.c
+@@ -276,7 +276,7 @@ grub_halt (void)
+ }
+ 
+ void
+-grub_exit (void)
++grub_exit (int rc __attribute__((unused)))
+ {
+   GRUB_ARC_FIRMWARE_VECTOR->exit ();
+ 
+diff --git a/grub-core/kern/mips/loongson/init.c b/grub-core/kern/mips/loongson/init.c
+index 7b96531b983..dff598ca7b0 100644
+--- a/grub-core/kern/mips/loongson/init.c
++++ b/grub-core/kern/mips/loongson/init.c
+@@ -304,7 +304,7 @@ grub_halt (void)
+ }
+ 
+ void
+-grub_exit (void)
++grub_exit (int rc __attribute__((unused)))
+ {
+   grub_halt ();
+ }
+diff --git a/grub-core/kern/mips/qemu_mips/init.c b/grub-core/kern/mips/qemu_mips/init.c
+index be88b77d22d..8b6c55ffc01 100644
+--- a/grub-core/kern/mips/qemu_mips/init.c
++++ b/grub-core/kern/mips/qemu_mips/init.c
+@@ -75,7 +75,7 @@ grub_machine_fini (int flags __attribute__ ((unused)))
+ }
+ 
+ void
+-grub_exit (void)
++grub_exit (int rc __attribute__((unused)))
+ {
+   grub_halt ();
+ }
+diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
+index 3af336ee227..63b586d09cb 100644
+--- a/grub-core/kern/misc.c
++++ b/grub-core/kern/misc.c
+@@ -1209,9 +1209,18 @@ grub_abort (void)
+       grub_getkey ();
+     }
+ 
+-  grub_exit ();
++  grub_exit (1);
+ }
+ 
++#if defined (__clang__) && !defined (GRUB_UTIL)
++/* clang emits references to abort().  */
++void __attribute__ ((noreturn))
++abort (void)
++{
++  grub_abort ();
++}
++#endif
++
+ void
+ grub_fatal (const char *fmt, ...)
+ {
+diff --git a/grub-core/kern/uboot/init.c b/grub-core/kern/uboot/init.c
+index 3e338645c57..be2a5be1d07 100644
+--- a/grub-core/kern/uboot/init.c
++++ b/grub-core/kern/uboot/init.c
+@@ -39,9 +39,9 @@ extern grub_size_t grub_total_module_size;
+ static unsigned long timer_start;
+ 
+ void
+-grub_exit (void)
++grub_exit (int rc)
+ {
+-  grub_uboot_return (0);
++  grub_uboot_return (rc < 0 ? 1 : rc);
+ }
+ 
+ static grub_uint64_t
+@@ -78,7 +78,7 @@ grub_machine_init (void)
+   if (!ver)
+     {
+       /* Don't even have a console to log errors to... */
+-      grub_exit ();
++      grub_exit (-1);
+     }
+   else if (ver > API_SIG_VERSION)
+     {
+diff --git a/grub-core/kern/xen/init.c b/grub-core/kern/xen/init.c
+index 782ca72952a..708b060f324 100644
+--- a/grub-core/kern/xen/init.c
++++ b/grub-core/kern/xen/init.c
+@@ -584,7 +584,7 @@ grub_machine_init (void)
+ }
+ 
+ void
+-grub_exit (void)
++grub_exit (int rc __attribute__((unused)))
+ {
+   struct sched_shutdown arg;
+ 
+diff --git a/include/grub/misc.h b/include/grub/misc.h
+index 7d2b5519690..fd18e6320b8 100644
+--- a/include/grub/misc.h
++++ b/include/grub/misc.h
+@@ -353,7 +353,7 @@ int EXPORT_FUNC(grub_vsnprintf) (char *str, grub_size_t n, const char *fmt,
+ char *EXPORT_FUNC(grub_xasprintf) (const char *fmt, ...)
+      __attribute__ ((format (GNU_PRINTF, 1, 2))) WARN_UNUSED_RESULT;
+ char *EXPORT_FUNC(grub_xvasprintf) (const char *fmt, va_list args) WARN_UNUSED_RESULT;
+-void EXPORT_FUNC(grub_exit) (void) __attribute__ ((noreturn));
++void EXPORT_FUNC(grub_exit) (int rc) __attribute__ ((noreturn));
+ grub_uint64_t EXPORT_FUNC(grub_divmod64) (grub_uint64_t n,
+ 					  grub_uint64_t d,
+ 					  grub_uint64_t *r);
diff --git a/SOURCES/0017-Make-efi-machines-load-an-env-block-from-a-variable.patch b/SOURCES/0017-Make-efi-machines-load-an-env-block-from-a-variable.patch
new file mode 100644
index 0000000..d061461
--- /dev/null
+++ b/SOURCES/0017-Make-efi-machines-load-an-env-block-from-a-variable.patch
@@ -0,0 +1,89 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 7 Dec 2015 14:20:49 -0500
+Subject: [PATCH] Make efi machines load an env block from a variable
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/Makefile.core.def |  1 +
+ grub-core/kern/efi/init.c   | 36 +++++++++++++++++++++++++++++++++++-
+ 2 files changed, 36 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 45d3edaa4dc..c865a08b027 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -207,6 +207,7 @@ kernel = {
+   efi = kern/efi/acpi.c;
+   efi = kern/efi/sb.c;
+   efi = kern/lockdown.c;
++  efi = lib/envblk.c;
+   i386_coreboot = kern/i386/pc/acpi.c;
+   i386_multiboot = kern/i386/pc/acpi.c;
+   i386_coreboot = kern/acpi.c;
+diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c
+index 7facacf09c7..6d39bd3ad29 100644
+--- a/grub-core/kern/efi/init.c
++++ b/grub-core/kern/efi/init.c
+@@ -27,8 +27,11 @@
+ #include <grub/env.h>
+ #include <grub/mm.h>
+ #include <grub/kernel.h>
++
+ #include <grub/stack_protector.h>
+ 
++#include <grub/lib/envblk.h>
++
+ #ifdef GRUB_STACK_PROTECTOR
+ 
+ static grub_efi_guid_t rng_protocol_guid = GRUB_EFI_RNG_PROTOCOL_GUID;
+@@ -82,6 +85,36 @@ stack_protector_init (void)
+ 
+ grub_addr_t grub_modbase;
+ 
++#define GRUB_EFI_GRUB_VARIABLE_GUID \
++  { 0x91376aff, 0xcba6, 0x42be, \
++    { 0x94, 0x9d, 0x06, 0xfd, 0xe8, 0x11, 0x28, 0xe8 } \
++  }
++
++/* Helper for grub_efi_env_init */
++static int
++set_var (const char *name, const char *value,
++	 void *whitelist __attribute__((__unused__)))
++{
++  grub_env_set (name, value);
++  return 0;
++}
++
++static void
++grub_efi_env_init (void)
++{
++  grub_efi_guid_t efi_grub_guid = GRUB_EFI_GRUB_VARIABLE_GUID;
++  struct grub_envblk envblk_s = { NULL, 0 };
++  grub_envblk_t envblk = &envblk_s;
++
++  grub_efi_get_variable ("GRUB_ENV", &efi_grub_guid, &envblk_s.size,
++                         (void **) &envblk_s.buf);
++  if (!envblk_s.buf || envblk_s.size < 1)
++    return;
++
++  grub_envblk_iterate (envblk, NULL, set_var);
++  grub_free (envblk_s.buf);
++}
++
+ void
+ grub_efi_init (void)
+ {
+@@ -108,10 +141,11 @@ grub_efi_init (void)
+   efi_call_4 (grub_efi_system_table->boot_services->set_watchdog_timer,
+ 	      0, 0, 0, NULL);
+ 
++  grub_efi_env_init ();
+   grub_efidisk_init ();
+ }
+ 
+-void (*grub_efi_net_config) (grub_efi_handle_t hnd, 
++void (*grub_efi_net_config) (grub_efi_handle_t hnd,
+ 			     char **device,
+ 			     char **path);
+ 
diff --git a/SOURCES/0018-Migrate-PPC-from-Yaboot-to-Grub2.patch b/SOURCES/0018-Migrate-PPC-from-Yaboot-to-Grub2.patch
new file mode 100644
index 0000000..ee8c7ab
--- /dev/null
+++ b/SOURCES/0018-Migrate-PPC-from-Yaboot-to-Grub2.patch
@@ -0,0 +1,151 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Mark Hamzy <hamzy@us.ibm.com>
+Date: Wed, 28 Mar 2012 14:46:41 -0500
+Subject: [PATCH] Migrate PPC from Yaboot to Grub2
+
+Add configuration support for serial terminal consoles.  This will set the
+maximum screen size so that text is not overwritten.
+---
+ Makefile.util.def              |   7 +++
+ util/grub.d/20_ppc_terminfo.in | 114 +++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 121 insertions(+)
+ create mode 100644 util/grub.d/20_ppc_terminfo.in
+
+diff --git a/Makefile.util.def b/Makefile.util.def
+index f8b356cc1fa..2c9b283a230 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -508,6 +508,13 @@ script = {
+   condition = COND_HOST_LINUX;
+ };
+ 
++script = {
++  name = '20_ppc_terminfo';
++  common = util/grub.d/20_ppc_terminfo.in;
++  installdir = grubconf;
++  condition = COND_HOST_LINUX;
++};
++
+ script = {
+   name = '30_os-prober';
+   common = util/grub.d/30_os-prober.in;
+diff --git a/util/grub.d/20_ppc_terminfo.in b/util/grub.d/20_ppc_terminfo.in
+new file mode 100644
+index 00000000000..10d66586820
+--- /dev/null
++++ b/util/grub.d/20_ppc_terminfo.in
+@@ -0,0 +1,114 @@
++#! /bin/sh
++set -e
++
++# grub-mkconfig helper script.
++# Copyright (C) 2006,2007,2008,2009,2010  Free Software Foundation, Inc.
++#
++# GRUB is free software: you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation, either version 3 of the License, or
++# (at your option) any later version.
++#
++# GRUB is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++
++prefix=@prefix@
++exec_prefix=@exec_prefix@
++bindir=@bindir@
++libdir=@libdir@
++. "@datadir@/@PACKAGE@/grub-mkconfig_lib"
++
++export TEXTDOMAIN=@PACKAGE@
++export TEXTDOMAINDIR=@localedir@
++
++X=80
++Y=24
++TERMINAL=ofconsole
++
++argument () {
++  opt=$1
++  shift
++
++  if test $# -eq 0; then
++      echo "$0: option requires an argument -- '$opt'" 1>&2
++      exit 1
++  fi
++  echo $1
++}
++
++check_terminfo () {
++
++  while test $# -gt 0
++  do
++    option=$1
++    shift
++
++    case "$option" in
++    terminfo | TERMINFO)
++        ;;
++
++    -g)
++        NEWXY=`argument $option "$@"`
++        NEWX=`echo $NEWXY | cut -d x -f 1`
++        NEWY=`echo $NEWXY | cut -d x -f 2`
++
++        if [ ${NEWX} -ge 80 ] ; then
++          X=${NEWX}
++        else
++          echo "Warning: ${NEWX} is less than the minimum size of 80"
++        fi
++
++        if [ ${NEWY} -ge 24 ] ; then
++          Y=${NEWY}
++        else
++          echo "Warning: ${NEWY} is less than the minimum size of 24"
++        fi
++
++        shift
++        ;;
++
++    *)
++#       # accept console or ofconsole
++#       if [ "$option" != "console" -a "$option" != "ofconsole" ] ; then
++#         echo "Error: GRUB_TERMINFO unknown console: $option"
++#         exit 1
++#       fi
++#       # perfer console
++#       TERMINAL=console
++        # accept ofconsole
++        if [ "$option" != "ofconsole" ] ; then
++          echo "Error: GRUB_TERMINFO unknown console: $option"
++          exit 1
++        fi
++        # perfer console
++        TERMINAL=ofconsole
++        ;;
++    esac
++
++  done
++
++}
++
++if ! uname -m | grep -q ppc ; then
++  exit 0
++fi
++
++if [ "x${GRUB_TERMINFO}" != "x" ] ; then
++  F1=`echo ${GRUB_TERMINFO} | cut -d " " -f 1`
++
++  if [ "${F1}" != "terminfo" ] ; then
++    echo "Error: GRUB_TERMINFO is set to \"${GRUB_TERMINFO}\" The first word should be terminfo."
++    exit 1
++  fi
++
++  check_terminfo ${GRUB_TERMINFO}
++fi
++
++cat << EOF
++  terminfo -g ${X}x${Y} ${TERMINAL}
++EOF
diff --git a/SOURCES/0019-Add-fw_path-variable-revised.patch b/SOURCES/0019-Add-fw_path-variable-revised.patch
new file mode 100644
index 0000000..df9ef83
--- /dev/null
+++ b/SOURCES/0019-Add-fw_path-variable-revised.patch
@@ -0,0 +1,78 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Paulo Flabiano Smorigo <pfsmorigo@br.ibm.com>
+Date: Wed, 19 Sep 2012 21:22:55 -0300
+Subject: [PATCH] Add fw_path variable (revised)
+
+This patch makes grub look for its config file on efi where the app was
+found. It was originally written by Matthew Garrett, and adapted to fix the
+"No modules are loaded on grub2 network boot" issue:
+
+https://bugzilla.redhat.com/show_bug.cgi?id=857936
+---
+ grub-core/kern/main.c   | 13 ++++++-------
+ grub-core/normal/main.c | 25 ++++++++++++++++++++++++-
+ 2 files changed, 30 insertions(+), 8 deletions(-)
+
+diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c
+index 73967e2f5b0..d1de9fa6873 100644
+--- a/grub-core/kern/main.c
++++ b/grub-core/kern/main.c
+@@ -128,16 +128,15 @@ grub_set_prefix_and_root (void)
+ 
+   grub_machine_get_bootlocation (&fwdevice, &fwpath);
+ 
+-  if (fwdevice)
++  if (fwdevice && fwpath)
+     {
+-      char *cmdpath;
++      char *fw_path;
+ 
+-      cmdpath = grub_xasprintf ("(%s)%s", fwdevice, fwpath ? : "");
+-      if (cmdpath)
++      fw_path = grub_xasprintf ("(%s)/%s", fwdevice, fwpath);
++      if (fw_path)
+ 	{
+-	  grub_env_set ("cmdpath", cmdpath);
+-	  grub_env_export ("cmdpath");
+-	  grub_free (cmdpath);
++	  grub_env_set ("fw_path", fw_path);
++	  grub_free (fw_path);
+ 	}
+     }
+ 
+diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
+index 70614de1565..62571e6dfcc 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -339,7 +339,30 @@ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)),
+       /* Guess the config filename. It is necessary to make CONFIG static,
+ 	 so that it won't get broken by longjmp.  */
+       char *config;
+-      const char *prefix;
++      const char *prefix, *fw_path;
++
++      fw_path = grub_env_get ("fw_path");
++      if (fw_path)
++	{
++	  config = grub_xasprintf ("%s/grub.cfg", fw_path);
++	  if (config)
++	    {
++	      grub_file_t file;
++
++	      file = grub_file_open (config, GRUB_FILE_TYPE_CONFIG);
++	      if (file)
++		{
++		  grub_file_close (file);
++		  grub_enter_normal_mode (config);
++		}
++              else
++                {
++                  /*  Ignore all errors.  */
++                  grub_errno = 0;
++                }
++	      grub_free (config);
++	    }
++	}
+ 
+       prefix = grub_env_get ("prefix");
+       if (prefix)
diff --git a/SOURCES/0020-Pass-x-hex-hex-straight-through-unmolested.patch b/SOURCES/0020-Pass-x-hex-hex-straight-through-unmolested.patch
new file mode 100644
index 0000000..b81abb5
--- /dev/null
+++ b/SOURCES/0020-Pass-x-hex-hex-straight-through-unmolested.patch
@@ -0,0 +1,173 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 1 Oct 2012 13:24:37 -0400
+Subject: [PATCH] Pass "\x[[:hex:]][[:hex:]]" straight through unmolested.
+
+Don't munge raw spaces when we're doing our cmdline escaping (#923374)
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/commands/wildcard.c | 16 +++++++++++++++-
+ grub-core/lib/cmdline.c       | 25 +++++++++++++++++++++++--
+ grub-core/script/execute.c    | 43 +++++++++++++++++++++++++++++++++++++------
+ 3 files changed, 75 insertions(+), 9 deletions(-)
+
+diff --git a/grub-core/commands/wildcard.c b/grub-core/commands/wildcard.c
+index cc3290311f0..8f67a4be7f0 100644
+--- a/grub-core/commands/wildcard.c
++++ b/grub-core/commands/wildcard.c
+@@ -488,6 +488,12 @@ check_file (const char *dir, const char *basename)
+   return ctx.found;
+ }
+ 
++static int
++is_hex(char c)
++{
++  return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
++}
++
+ static void
+ unescape (char *out, const char *in, const char *end)
+ {
+@@ -496,7 +502,15 @@ unescape (char *out, const char *in, const char *end)
+ 
+   for (optr = out, iptr = in; iptr < end;)
+     {
+-      if (*iptr == '\\' && iptr + 1 < end)
++      if (*iptr == '\\' && iptr + 3 < end && iptr[1] == 'x' && is_hex(iptr[2]) && is_hex(iptr[3]))
++	{
++	  *optr++ = *iptr++;
++	  *optr++ = *iptr++;
++	  *optr++ = *iptr++;
++	  *optr++ = *iptr++;
++	  continue;
++	}
++      else if (*iptr == '\\' && iptr + 1 < end)
+ 	{
+ 	  *optr++ = iptr[1];
+ 	  iptr += 2;
+diff --git a/grub-core/lib/cmdline.c b/grub-core/lib/cmdline.c
+index ed0b149dca5..8e2294d8ff6 100644
+--- a/grub-core/lib/cmdline.c
++++ b/grub-core/lib/cmdline.c
+@@ -20,6 +20,12 @@
+ #include <grub/lib/cmdline.h>
+ #include <grub/misc.h>
+ 
++static int
++is_hex(char c)
++{
++  return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
++}
++
+ static unsigned int check_arg (char *c, int *has_space)
+ {
+   int space = 0;
+@@ -27,7 +33,13 @@ static unsigned int check_arg (char *c, int *has_space)
+ 
+   while (*c)
+     {
+-      if (*c == '\\' || *c == '\'' || *c == '"')
++      if (*c == '\\' && *(c+1) == 'x' && is_hex(*(c+2)) && is_hex(*(c+3)))
++	{
++	  size += 4;
++	  c += 4;
++	  continue;
++	}
++      else if (*c == '\\' || *c == '\'' || *c == '"')
+ 	size++;
+       else if (*c == ' ')
+ 	space = 1;
+@@ -86,7 +98,16 @@ grub_create_loader_cmdline (int argc, char *argv[], char *buf,
+ 
+       while (*c)
+ 	{
+-	  if (*c == '\\' || *c == '\'' || *c == '"')
++	  if (*c == '\\' && *(c+1) == 'x' &&
++		   is_hex(*(c+2)) && is_hex(*(c+3)))
++	    {
++	      *buf++ = *c++;
++	      *buf++ = *c++;
++	      *buf++ = *c++;
++	      *buf++ = *c++;
++	      continue;
++	    }
++	  else if (*c == '\\' || *c == '\'' || *c == '"')
+ 	    *buf++ = '\\';
+ 
+ 	  *buf++ = *c;
+diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c
+index ad80399246a..0c6dd9c5201 100644
+--- a/grub-core/script/execute.c
++++ b/grub-core/script/execute.c
+@@ -56,6 +56,12 @@ static struct grub_script_scope *scope = 0;
+ /* Wildcard translator for GRUB script.  */
+ struct grub_script_wildcard_translator *grub_wildcard_translator;
+ 
++static int
++is_hex(char c)
++{
++  return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
++}
++
+ static char*
+ wildcard_escape (const char *s)
+ {
+@@ -72,7 +78,15 @@ wildcard_escape (const char *s)
+   i = 0;
+   while ((ch = *s++))
+     {
+-      if (ch == '*' || ch == '\\' || ch == '?')
++      if (ch == '\\' && s[0] == 'x' && is_hex(s[1]) && is_hex(s[2]))
++	{
++	  p[i++] = ch;
++	  p[i++] = *s++;
++	  p[i++] = *s++;
++	  p[i++] = *s++;
++	  continue;
++	}
++      else if (ch == '*' || ch == '\\' || ch == '?')
+ 	p[i++] = '\\';
+       p[i++] = ch;
+     }
+@@ -96,7 +110,14 @@ wildcard_unescape (const char *s)
+   i = 0;
+   while ((ch = *s++))
+     {
+-      if (ch == '\\')
++      if (ch == '\\' && s[0] == 'x' && is_hex(s[1]) && is_hex(s[2]))
++	{
++	  p[i++] = '\\';
++	  p[i++] = *s++;
++	  p[i++] = *s++;
++	  p[i++] = *s++;
++	}
++      else if (ch == '\\')
+ 	p[i++] = *s++;
+       else
+ 	p[i++] = ch;
+@@ -398,10 +419,20 @@ parse_string (const char *str,
+     switch (*ptr)
+       {
+       case '\\':
+-	escaped = !escaped;
+-	if (!escaped && put)
+-	  *(put++) = '\\';
+-	ptr++;
++	if (!escaped && put && *(ptr+1) == 'x' && is_hex(*(ptr+2)) && is_hex(*(ptr+3)))
++	  {
++	    *(put++) = *ptr++;
++	    *(put++) = *ptr++;
++	    *(put++) = *ptr++;
++	    *(put++) = *ptr++;
++	  }
++	else
++	  {
++	    escaped = !escaped;
++	    if (!escaped && put)
++	      *(put++) = '\\';
++	    ptr++;
++	  }
+ 	break;
+       case '$':
+ 	if (escaped)
diff --git a/SOURCES/0021-blscfg-add-blscfg-module-to-parse-Boot-Loader-Specif.patch b/SOURCES/0021-blscfg-add-blscfg-module-to-parse-Boot-Loader-Specif.patch
new file mode 100644
index 0000000..da65e18
--- /dev/null
+++ b/SOURCES/0021-blscfg-add-blscfg-module-to-parse-Boot-Loader-Specif.patch
@@ -0,0 +1,1609 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 22 Jan 2013 06:31:38 +0100
+Subject: [PATCH] blscfg: add blscfg module to parse Boot Loader Specification
+ snippets
+
+The BootLoaderSpec (BLS) defines a scheme where different bootloaders can
+share a format for boot items and a configuration directory that accepts
+these common configurations as drop-in files.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+[wjt: some cleanups and fixes]
+Signed-off-by: Will Thompson <wjt@endlessm.com>
+---
+ grub-core/Makefile.core.def    |   11 +
+ grub-core/commands/blscfg.c    | 1177 ++++++++++++++++++++++++++++++++++++++++
+ grub-core/commands/legacycfg.c |    5 +-
+ grub-core/commands/loadenv.c   |   77 +--
+ grub-core/commands/menuentry.c |   20 +-
+ grub-core/normal/main.c        |    6 +
+ grub-core/commands/loadenv.h   |   93 ++++
+ include/grub/compiler.h        |    2 +
+ include/grub/menu.h            |   13 +
+ include/grub/normal.h          |    2 +-
+ 10 files changed, 1324 insertions(+), 82 deletions(-)
+ create mode 100644 grub-core/commands/blscfg.c
+ create mode 100644 grub-core/commands/loadenv.h
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index c865a08b027..c15e91943b9 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -814,6 +814,16 @@ module = {
+   common = commands/blocklist.c;
+ };
+ 
++module = {
++  name = blscfg;
++  common = commands/blscfg.c;
++  common = commands/loadenv.h;
++  enable = powerpc_ieee1275;
++  enable = efi;
++  enable = i386_pc;
++  enable = emu;
++};
++
+ module = {
+   name = boot;
+   common = commands/boot.c;
+@@ -980,6 +990,7 @@ module = {
+ module = {
+   name = loadenv;
+   common = commands/loadenv.c;
++  common = commands/loadenv.h;
+   common = lib/envblk.c;
+ };
+ 
+diff --git a/grub-core/commands/blscfg.c b/grub-core/commands/blscfg.c
+new file mode 100644
+index 00000000000..e907a6a5d28
+--- /dev/null
++++ b/grub-core/commands/blscfg.c
+@@ -0,0 +1,1177 @@
++/*-*- Mode: C; c-basic-offset: 2; indent-tabs-mode: t -*-*/
++
++/* bls.c - implementation of the boot loader spec */
++
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/list.h>
++#include <grub/types.h>
++#include <grub/misc.h>
++#include <grub/mm.h>
++#include <grub/err.h>
++#include <grub/dl.h>
++#include <grub/extcmd.h>
++#include <grub/i18n.h>
++#include <grub/fs.h>
++#include <grub/env.h>
++#include <grub/file.h>
++#include <grub/normal.h>
++#include <grub/lib/envblk.h>
++
++#include <stdbool.h>
++
++GRUB_MOD_LICENSE ("GPLv3+");
++
++#include "loadenv.h"
++
++#define GRUB_BLS_CONFIG_PATH "/loader/entries/"
++#ifdef GRUB_MACHINE_EMU
++#define GRUB_BOOT_DEVICE "/boot"
++#else
++#define GRUB_BOOT_DEVICE "($root)"
++#endif
++
++struct keyval
++{
++  const char *key;
++  char *val;
++};
++
++static struct bls_entry *entries = NULL;
++
++#define FOR_BLS_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
++
++static int bls_add_keyval(struct bls_entry *entry, char *key, char *val)
++{
++  char *k, *v;
++  struct keyval **kvs, *kv;
++  int new_n = entry->nkeyvals + 1;
++
++  kvs = grub_realloc (entry->keyvals, new_n * sizeof (struct keyval *));
++  if (!kvs)
++    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++		       "couldn't find space for BLS entry");
++  entry->keyvals = kvs;
++
++  kv = grub_malloc (sizeof (struct keyval));
++  if (!kv)
++    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++		       "couldn't find space for BLS entry");
++
++  k = grub_strdup (key);
++  if (!k)
++    {
++      grub_free (kv);
++      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++			 "couldn't find space for BLS entry");
++    }
++
++  v = grub_strdup (val);
++  if (!v)
++    {
++      grub_free (k);
++      grub_free (kv);
++      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++			 "couldn't find space for BLS entry");
++    }
++
++  kv->key = k;
++  kv->val = v;
++
++  entry->keyvals[entry->nkeyvals] = kv;
++  grub_dprintf("blscfg", "new keyval at %p:%s:%s\n", entry->keyvals[entry->nkeyvals], k, v);
++  entry->nkeyvals = new_n;
++
++  return 0;
++}
++
++/* Find they value of the key named by keyname.  If there are allowed to be
++ * more than one, pass a pointer to an int set to -1 the first time, and pass
++ * the same pointer through each time after, and it'll return them in sorted
++ * order as defined in the BLS fragment file */
++static char *bls_get_val(struct bls_entry *entry, const char *keyname, int *last)
++{
++  int idx, start = 0;
++  struct keyval *kv = NULL;
++
++  if (last)
++    start = *last + 1;
++
++  for (idx = start; idx < entry->nkeyvals; idx++) {
++    kv = entry->keyvals[idx];
++
++    if (!grub_strcmp (keyname, kv->key))
++      break;
++  }
++
++  if (idx == entry->nkeyvals) {
++    if (last)
++      *last = -1;
++    return NULL;
++  }
++
++  if (last)
++    *last = idx;
++
++  return kv->val;
++}
++
++#define goto_return(x) ({ ret = (x); goto finish; })
++
++/* compare alpha and numeric segments of two versions */
++/* return 1: a is newer than b */
++/*        0: a and b are the same version */
++/*       -1: b is newer than a */
++static int vercmp(const char * a, const char * b)
++{
++    char oldch1, oldch2;
++    char *abuf, *bbuf;
++    char *str1, *str2;
++    char * one, * two;
++    int rc;
++    int isnum;
++    int ret = 0;
++
++    grub_dprintf("blscfg", "%s comparing %s and %s\n", __func__, a, b);
++    if (!grub_strcmp(a, b))
++	    return 0;
++
++    abuf = grub_malloc(grub_strlen(a) + 1);
++    bbuf = grub_malloc(grub_strlen(b) + 1);
++    str1 = abuf;
++    str2 = bbuf;
++    grub_strcpy(str1, a);
++    grub_strcpy(str2, b);
++
++    one = str1;
++    two = str2;
++
++    /* loop through each version segment of str1 and str2 and compare them */
++    while (*one || *two) {
++	while (*one && !grub_isalnum(*one) && *one != '~' && *one != '+') one++;
++	while (*two && !grub_isalnum(*two) && *two != '~' && *two != '+') two++;
++
++	/* handle the tilde separator, it sorts before everything else */
++	if (*one == '~' || *two == '~') {
++	    if (*one != '~') goto_return (1);
++	    if (*two != '~') goto_return (-1);
++	    one++;
++	    two++;
++	    continue;
++	}
++
++	/*
++	 * Handle plus separator. Concept is the same as tilde,
++	 * except that if one of the strings ends (base version),
++	 * the other is considered as higher version.
++	 */
++	if (*one == '+' || *two == '+') {
++	    if (!*one) return -1;
++	    if (!*two) return 1;
++	    if (*one != '+') goto_return (1);
++	    if (*two != '+') goto_return (-1);
++	    one++;
++	    two++;
++	    continue;
++	}
++
++	/* If we ran to the end of either, we are finished with the loop */
++	if (!(*one && *two)) break;
++
++	str1 = one;
++	str2 = two;
++
++	/* grab first completely alpha or completely numeric segment */
++	/* leave one and two pointing to the start of the alpha or numeric */
++	/* segment and walk str1 and str2 to end of segment */
++	if (grub_isdigit(*str1)) {
++	    while (*str1 && grub_isdigit(*str1)) str1++;
++	    while (*str2 && grub_isdigit(*str2)) str2++;
++	    isnum = 1;
++	} else {
++	    while (*str1 && grub_isalpha(*str1)) str1++;
++	    while (*str2 && grub_isalpha(*str2)) str2++;
++	    isnum = 0;
++	}
++
++	/* save character at the end of the alpha or numeric segment */
++	/* so that they can be restored after the comparison */
++	oldch1 = *str1;
++	*str1 = '\0';
++	oldch2 = *str2;
++	*str2 = '\0';
++
++	/* this cannot happen, as we previously tested to make sure that */
++	/* the first string has a non-null segment */
++	if (one == str1) goto_return(-1);	/* arbitrary */
++
++	/* take care of the case where the two version segments are */
++	/* different types: one numeric, the other alpha (i.e. empty) */
++	/* numeric segments are always newer than alpha segments */
++	/* XXX See patch #60884 (and details) from bugzilla #50977. */
++	if (two == str2) goto_return (isnum ? 1 : -1);
++
++	if (isnum) {
++	    grub_size_t onelen, twolen;
++	    /* this used to be done by converting the digit segments */
++	    /* to ints using atoi() - it's changed because long  */
++	    /* digit segments can overflow an int - this should fix that. */
++
++	    /* throw away any leading zeros - it's a number, right? */
++	    while (*one == '0') one++;
++	    while (*two == '0') two++;
++
++	    /* whichever number has more digits wins */
++	    onelen = grub_strlen(one);
++	    twolen = grub_strlen(two);
++	    if (onelen > twolen) goto_return (1);
++	    if (twolen > onelen) goto_return (-1);
++	}
++
++	/* grub_strcmp will return which one is greater - even if the two */
++	/* segments are alpha or if they are numeric.  don't return  */
++	/* if they are equal because there might be more segments to */
++	/* compare */
++	rc = grub_strcmp(one, two);
++	if (rc) goto_return (rc < 1 ? -1 : 1);
++
++	/* restore character that was replaced by null above */
++	*str1 = oldch1;
++	one = str1;
++	*str2 = oldch2;
++	two = str2;
++    }
++
++    /* this catches the case where all numeric and alpha segments have */
++    /* compared identically but the segment sepparating characters were */
++    /* different */
++    if ((!*one) && (!*two)) goto_return (0);
++
++    /* whichever version still has characters left over wins */
++    if (!*one) goto_return (-1); else goto_return (1);
++
++finish:
++    grub_free (abuf);
++    grub_free (bbuf);
++    return ret;
++}
++
++/* returns name/version/release */
++/* NULL string pointer returned if nothing found */
++static void
++split_package_string (char *package_string, char **name,
++                     char **version, char **release)
++{
++  char *package_version, *package_release;
++
++  /* Release */
++  package_release = grub_strrchr (package_string, '-');
++
++  if (package_release != NULL)
++      *package_release++ = '\0';
++
++  *release = package_release;
++
++  if (name == NULL)
++    {
++      *version = package_string;
++    }
++  else
++    {
++      /* Version */
++      package_version = grub_strrchr(package_string, '-');
++
++      if (package_version != NULL)
++	*package_version++ = '\0';
++
++      *version = package_version;
++      /* Name */
++      *name = package_string;
++    }
++
++  /* Bubble up non-null values from release to name */
++  if (name != NULL && *name == NULL)
++    {
++      *name = (*version == NULL ? *release : *version);
++      *version = *release;
++      *release = NULL;
++    }
++  if (*version == NULL)
++    {
++      *version = *release;
++      *release = NULL;
++    }
++}
++
++static int
++split_cmp(char *nvr0, char *nvr1, int has_name)
++{
++  int ret = 0;
++  char *name0, *version0, *release0;
++  char *name1, *version1, *release1;
++
++  split_package_string(nvr0, has_name ? &name0 : NULL, &version0, &release0);
++  split_package_string(nvr1, has_name ? &name1 : NULL, &version1, &release1);
++
++  if (has_name)
++    {
++      ret = vercmp(name0 == NULL ? "" : name0,
++		   name1 == NULL ? "" : name1);
++      if (ret != 0)
++	return ret;
++    }
++
++  ret = vercmp(version0 == NULL ? "" : version0,
++	       version1 == NULL ? "" : version1);
++  if (ret != 0)
++    return ret;
++
++  ret = vercmp(release0 == NULL ? "" : release0,
++	       release1 == NULL ? "" : release1);
++  return ret;
++}
++
++/* return 1: e0 is newer than e1 */
++/*        0: e0 and e1 are the same version */
++/*       -1: e1 is newer than e0 */
++static int bls_cmp(const struct bls_entry *e0, const struct bls_entry *e1)
++{
++  char *id0, *id1;
++  int r;
++
++  id0 = grub_strdup(e0->filename);
++  id1 = grub_strdup(e1->filename);
++
++  r = split_cmp(id0, id1, 1);
++
++  grub_free(id0);
++  grub_free(id1);
++
++  return r;
++}
++
++static void list_add_tail(struct bls_entry *head, struct bls_entry *item)
++{
++  item->next = head;
++  if (head->prev)
++    head->prev->next = item;
++  item->prev = head->prev;
++  head->prev = item;
++}
++
++static int bls_add_entry(struct bls_entry *entry)
++{
++  struct bls_entry *e, *last = NULL;
++  int rc;
++
++  if (!entries) {
++    grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename);
++    entries = entry;
++    return 0;
++  }
++
++  FOR_BLS_ENTRIES(e) {
++    rc = bls_cmp(entry, e);
++
++    if (!rc)
++      return GRUB_ERR_BAD_ARGUMENT;
++
++    if (rc == 1) {
++      grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename);
++      list_add_tail (e, entry);
++      if (e == entries) {
++	entries = entry;
++	entry->prev = NULL;
++      }
++      return 0;
++    }
++    last = e;
++  }
++
++  if (last) {
++    grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename);
++    last->next = entry;
++    entry->prev = last;
++  }
++
++  return 0;
++}
++
++struct read_entry_info {
++  const char *devid;
++  const char *dirname;
++  grub_file_t file;
++};
++
++static int read_entry (
++    const char *filename,
++    const struct grub_dirhook_info *dirhook_info UNUSED,
++    void *data)
++{
++  grub_size_t m = 0, n, clip = 0;
++  int rc = 0;
++  char *p = NULL;
++  grub_file_t f = NULL;
++  struct bls_entry *entry;
++  struct read_entry_info *info = (struct read_entry_info *)data;
++
++  grub_dprintf ("blscfg", "filename: \"%s\"\n", filename);
++
++  n = grub_strlen (filename);
++
++  if (info->file)
++    {
++      f = info->file;
++    }
++  else
++    {
++      if (filename[0] == '.')
++	return 0;
++
++      if (n <= 5)
++	return 0;
++
++      if (grub_strcmp (filename + n - 5, ".conf") != 0)
++	return 0;
++
++      p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename);
++
++      f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG);
++      if (!f)
++	goto finish;
++    }
++
++  entry = grub_zalloc (sizeof (*entry));
++  if (!entry)
++    goto finish;
++
++  if (info->file)
++    {
++      char *slash;
++
++      if (n > 5 && !grub_strcmp (filename + n - 5, ".conf") == 0)
++	clip = 5;
++
++      slash = grub_strrchr (filename, '/');
++      if (!slash)
++	slash = grub_strrchr (filename, '\\');
++
++      while (*slash == '/' || *slash == '\\')
++	slash++;
++
++      m = slash ? slash - filename : 0;
++    }
++  else
++    {
++      m = 0;
++      clip = 5;
++    }
++  n -= m;
++
++  entry->filename = grub_strndup(filename + m, n - clip);
++  if (!entry->filename)
++    goto finish;
++
++  entry->filename[n - 5] = '\0';
++
++  for (;;)
++    {
++      char *buf;
++      char *separator;
++
++      buf = grub_file_getline (f);
++      if (!buf)
++	break;
++
++      while (buf && buf[0] && (buf[0] == ' ' || buf[0] == '\t'))
++	buf++;
++      if (buf[0] == '#')
++	continue;
++
++      separator = grub_strchr (buf, ' ');
++
++      if (!separator)
++	separator = grub_strchr (buf, '\t');
++
++      if (!separator || separator[1] == '\0')
++	{
++	  grub_free (buf);
++	  break;
++	}
++
++      separator[0] = '\0';
++
++      do {
++	separator++;
++      } while (*separator == ' ' || *separator == '\t');
++
++      rc = bls_add_keyval (entry, buf, separator);
++      grub_free (buf);
++      if (rc < 0)
++	break;
++    }
++
++    if (!rc)
++      bls_add_entry(entry);
++
++finish:
++  if (p)
++    grub_free (p);
++
++  if (f)
++    grub_file_close (f);
++
++  return 0;
++}
++
++static grub_envblk_t saved_env = NULL;
++
++static int UNUSED
++save_var (const char *name, const char *value, void *whitelist UNUSED)
++{
++  const char *val = grub_env_get (name);
++  grub_dprintf("blscfg", "saving \"%s\"\n", name);
++
++  if (val)
++    grub_envblk_set (saved_env, name, value);
++
++  return 0;
++}
++
++static int UNUSED
++unset_var (const char *name, const char *value UNUSED, void *whitelist)
++{
++  grub_dprintf("blscfg", "restoring \"%s\"\n", name);
++  if (! whitelist)
++    {
++      grub_env_unset (name);
++      return 0;
++    }
++
++  if (test_whitelist_membership (name,
++				 (const grub_env_whitelist_t *) whitelist))
++    grub_env_unset (name);
++
++  return 0;
++}
++
++static char **bls_make_list (struct bls_entry *entry, const char *key, int *num)
++{
++  int last = -1;
++  char *val;
++
++  int nlist = 0;
++  char **list = NULL;
++
++  list = grub_malloc (sizeof (char *));
++  if (!list)
++    return NULL;
++  list[0] = NULL;
++
++  while (1)
++    {
++      char **new;
++
++      val = bls_get_val (entry, key, &last);
++      if (!val)
++	break;
++
++      new = grub_realloc (list, (nlist + 2) * sizeof (char *));
++      if (!new)
++	break;
++
++      list = new;
++      list[nlist++] = val;
++      list[nlist] = NULL;
++  }
++
++  if (!nlist)
++    {
++      grub_free (list);
++      return NULL;
++    }
++
++  if (num)
++    *num = nlist;
++
++  return list;
++}
++
++static char *field_append(bool is_var, char *buffer, const char *start, const char *end)
++{
++  char *tmp = grub_strndup(start, end - start + 1);
++  const char *field = tmp;
++  int term = is_var ? 2 : 1;
++
++  if (is_var) {
++    field = grub_env_get (tmp);
++    if (!field)
++      return buffer;
++  }
++
++  if (!buffer)
++    buffer = grub_zalloc (grub_strlen(field) + term);
++  else
++    buffer = grub_realloc (buffer, grub_strlen(buffer) + grub_strlen(field) + term);
++
++  if (!buffer)
++    return NULL;
++
++  tmp = buffer + grub_strlen(buffer);
++  tmp = grub_stpcpy (tmp, field);
++
++  if (is_var)
++      tmp = grub_stpcpy (tmp, " ");
++
++  return buffer;
++}
++
++static char *expand_val(const char *value)
++{
++  char *buffer = NULL;
++  const char *start = value;
++  const char *end = value;
++  bool is_var = false;
++
++  if (!value)
++    return NULL;
++
++  while (*value) {
++    if (*value == '$') {
++      if (start != end) {
++	buffer = field_append(is_var, buffer, start, end);
++	if (!buffer)
++	  return NULL;
++      }
++
++      is_var = true;
++      start = value + 1;
++    } else if (is_var) {
++      if (!grub_isalnum(*value) && *value != '_') {
++	buffer = field_append(is_var, buffer, start, end);
++	is_var = false;
++	start = value;
++	if (*start == ' ')
++	  start++;
++      }
++    }
++
++    end = value;
++    value++;
++  }
++
++  if (start != end) {
++    buffer = field_append(is_var, buffer, start, end);
++    if (!buffer)
++      return NULL;
++  }
++
++  return buffer;
++}
++
++static char **early_initrd_list (const char *initrd)
++{
++  int nlist = 0;
++  char **list = NULL;
++  char *separator;
++
++  while ((separator = grub_strchr (initrd, ' ')))
++    {
++      list = grub_realloc (list, (nlist + 2) * sizeof (char *));
++      if (!list)
++        return NULL;
++
++      list[nlist++] = grub_strndup(initrd, separator - initrd);
++      list[nlist] = NULL;
++      initrd = separator + 1;
++  }
++
++  list = grub_realloc (list, (nlist + 2) * sizeof (char *));
++  if (!list)
++    return NULL;
++
++  list[nlist++] = grub_strndup(initrd, grub_strlen(initrd));
++  list[nlist] = NULL;
++
++  return list;
++}
++
++static void create_entry (struct bls_entry *entry)
++{
++  int argc = 0;
++  const char **argv = NULL;
++
++  char *title = NULL;
++  char *clinux = NULL;
++  char *options = NULL;
++  char **initrds = NULL;
++  char *initrd = NULL;
++  const char *early_initrd = NULL;
++  char **early_initrds = NULL;
++  char *initrd_prefix = NULL;
++  char *devicetree = NULL;
++  char *dt = NULL;
++  char *id = entry->filename;
++  char *dotconf = id;
++  char *hotkey = NULL;
++
++  char *users = NULL;
++  char **classes = NULL;
++
++  char **args = NULL;
++
++  char *src = NULL;
++  int i, index;
++  bool add_dt_prefix = false;
++
++  grub_dprintf("blscfg", "%s got here\n", __func__);
++  clinux = bls_get_val (entry, "linux", NULL);
++  if (!clinux)
++    {
++      grub_dprintf ("blscfg", "Skipping file %s with no 'linux' key.\n", entry->filename);
++      goto finish;
++    }
++
++  /*
++   * strip the ".conf" off the end before we make it our "id" field.
++   */
++  do
++    {
++      dotconf = grub_strstr(dotconf, ".conf");
++    } while (dotconf != NULL && dotconf[5] != '\0');
++  if (dotconf)
++    dotconf[0] = '\0';
++
++  title = bls_get_val (entry, "title", NULL);
++  options = expand_val (bls_get_val (entry, "options", NULL));
++
++  if (!options)
++    options = expand_val (grub_env_get("default_kernelopts"));
++
++  initrds = bls_make_list (entry, "initrd", NULL);
++
++  devicetree = expand_val (bls_get_val (entry, "devicetree", NULL));
++
++  if (!devicetree)
++    {
++      devicetree = expand_val (grub_env_get("devicetree"));
++      add_dt_prefix = true;
++    }
++
++  hotkey = bls_get_val (entry, "grub_hotkey", NULL);
++  users = expand_val (bls_get_val (entry, "grub_users", NULL));
++  classes = bls_make_list (entry, "grub_class", NULL);
++  args = bls_make_list (entry, "grub_arg", &argc);
++
++  argc += 1;
++  argv = grub_malloc ((argc + 1) * sizeof (char *));
++  argv[0] = title ? title : clinux;
++  for (i = 1; i < argc; i++)
++    argv[i] = args[i-1];
++  argv[argc] = NULL;
++
++  early_initrd = grub_env_get("early_initrd");
++
++  grub_dprintf ("blscfg", "adding menu entry for \"%s\" with id \"%s\"\n",
++		title, id);
++  if (early_initrd)
++    {
++      early_initrds = early_initrd_list(early_initrd);
++      if (!early_initrds)
++      {
++	grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
++	goto finish;
++      }
++
++      if (initrds != NULL && initrds[0] != NULL)
++	{
++	  initrd_prefix = grub_strrchr (initrds[0], '/');
++	  initrd_prefix = grub_strndup(initrds[0], initrd_prefix - initrds[0] + 1);
++	}
++      else
++	{
++	  initrd_prefix = grub_strrchr (clinux, '/');
++	  initrd_prefix = grub_strndup(clinux, initrd_prefix - clinux + 1);
++	}
++
++      if (!initrd_prefix)
++	{
++	  grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
++	  goto finish;
++	}
++    }
++
++  if (early_initrds || initrds)
++    {
++      int initrd_size = sizeof ("initrd");
++      char *tmp;
++
++      for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++)
++	initrd_size += sizeof (" " GRUB_BOOT_DEVICE) \
++		       + grub_strlen(initrd_prefix)  \
++		       + grub_strlen (early_initrds[i]) + 1;
++
++      for (i = 0; initrds != NULL && initrds[i] != NULL; i++)
++	initrd_size += sizeof (" " GRUB_BOOT_DEVICE) \
++		       + grub_strlen (initrds[i]) + 1;
++      initrd_size += 1;
++
++      initrd = grub_malloc (initrd_size);
++      if (!initrd)
++	{
++	  grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
++	  goto finish;
++	}
++
++      tmp = grub_stpcpy(initrd, "initrd");
++      for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++)
++	{
++	  grub_dprintf ("blscfg", "adding early initrd %s\n", early_initrds[i]);
++	  tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE);
++	  tmp = grub_stpcpy (tmp, initrd_prefix);
++	  tmp = grub_stpcpy (tmp, early_initrds[i]);
++	  grub_free(early_initrds[i]);
++	}
++
++      for (i = 0; initrds != NULL && initrds[i] != NULL; i++)
++	{
++	  grub_dprintf ("blscfg", "adding initrd %s\n", initrds[i]);
++	  tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE);
++	  tmp = grub_stpcpy (tmp, initrds[i]);
++	}
++      tmp = grub_stpcpy (tmp, "\n");
++    }
++
++  if (devicetree)
++    {
++      char *prefix = NULL;
++      int dt_size;
++
++      if (add_dt_prefix)
++	{
++	  prefix = grub_strrchr (clinux, '/');
++	  prefix = grub_strndup(clinux, prefix - clinux + 1);
++	  if (!prefix)
++	    {
++	      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
++	      goto finish;
++	    }
++	}
++
++      dt_size = sizeof("devicetree " GRUB_BOOT_DEVICE) + grub_strlen(devicetree) + 1;
++
++      if (add_dt_prefix)
++	{
++	  dt_size += grub_strlen(prefix);
++	}
++
++      dt = grub_malloc (dt_size);
++      if (!dt)
++        {
++          grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
++        goto finish;
++        }
++      char *tmp = dt;
++      tmp = grub_stpcpy (dt, "devicetree");
++      tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE);
++      if (add_dt_prefix)
++        tmp = grub_stpcpy (tmp, prefix);
++      tmp = grub_stpcpy (tmp, devicetree);
++      tmp = grub_stpcpy (tmp, "\n");
++
++      grub_free(prefix);
++    }
++
++  grub_dprintf ("blscfg2", "devicetree %s for id:\"%s\"\n", dt, id);
++
++  const char *sdval = grub_env_get("save_default");
++  bool savedefault = ((NULL != sdval) && (grub_strcmp(sdval, "true") == 0));
++  src = grub_xasprintf ("%sload_video\n"
++			"set gfxpayload=keep\n"
++			"insmod gzio\n"
++			"linux %s%s%s%s\n"
++			"%s%s",
++			savedefault ? "savedefault\n" : "",
++			GRUB_BOOT_DEVICE, clinux, options ? " " : "", options ? options : "",
++			initrd ? initrd : "", dt ? dt : "");
++
++  grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, &index, entry);
++  grub_dprintf ("blscfg", "Added entry %d id:\"%s\"\n", index, id);
++
++finish:
++  grub_free (dt);
++  grub_free (initrd);
++  grub_free (initrd_prefix);
++  grub_free (early_initrds);
++  grub_free (devicetree);
++  grub_free (initrds);
++  grub_free (options);
++  grub_free (classes);
++  grub_free (args);
++  grub_free (argv);
++  grub_free (src);
++}
++
++struct find_entry_info {
++	const char *dirname;
++	const char *devid;
++	grub_device_t dev;
++	grub_fs_t fs;
++};
++
++/*
++ * info: the filesystem object the file is on.
++ */
++static int find_entry (struct find_entry_info *info)
++{
++  struct read_entry_info read_entry_info;
++  grub_fs_t blsdir_fs = NULL;
++  grub_device_t blsdir_dev = NULL;
++  const char *blsdir = info->dirname;
++  int fallback = 0;
++  int r = 0;
++
++  if (!blsdir) {
++    blsdir = grub_env_get ("blsdir");
++    if (!blsdir)
++      blsdir = GRUB_BLS_CONFIG_PATH;
++  }
++
++  read_entry_info.file = NULL;
++  read_entry_info.dirname = blsdir;
++
++  grub_dprintf ("blscfg", "scanning blsdir: %s\n", blsdir);
++
++  blsdir_dev = info->dev;
++  blsdir_fs = info->fs;
++  read_entry_info.devid = info->devid;
++
++read_fallback:
++  r = blsdir_fs->fs_dir (blsdir_dev, read_entry_info.dirname, read_entry,
++			 &read_entry_info);
++  if (r != 0) {
++      grub_dprintf ("blscfg", "read_entry returned error\n");
++      grub_err_t e;
++      do
++	{
++	  e = grub_error_pop();
++	} while (e);
++  }
++
++  if (r && !info->dirname && !fallback) {
++    read_entry_info.dirname = "/boot" GRUB_BLS_CONFIG_PATH;
++    grub_dprintf ("blscfg", "Entries weren't found in %s, fallback to %s\n",
++		  blsdir, read_entry_info.dirname);
++    fallback = 1;
++    goto read_fallback;
++  }
++
++  return 0;
++}
++
++static grub_err_t
++bls_load_entries (const char *path)
++{
++  grub_size_t len;
++  grub_fs_t fs;
++  grub_device_t dev;
++  static grub_err_t r;
++  const char *devid = NULL;
++  char *blsdir = NULL;
++  struct find_entry_info info = {
++      .dev = NULL,
++      .fs = NULL,
++      .dirname = NULL,
++  };
++  struct read_entry_info rei = {
++      .devid = NULL,
++      .dirname = NULL,
++  };
++
++  if (path) {
++    len = grub_strlen (path);
++    if (grub_strcmp (path + len - 5, ".conf") == 0) {
++      rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG);
++      if (!rei.file)
++	return grub_errno;
++      /*
++       * read_entry() closes the file
++       */
++      return read_entry(path, NULL, &rei);
++    } else if (path[0] == '(') {
++      devid = path + 1;
++
++      blsdir = grub_strchr (path, ')');
++      if (!blsdir)
++	return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Filepath isn't correct"));
++
++      *blsdir = '\0';
++      blsdir = blsdir + 1;
++    }
++  }
++
++  if (!devid) {
++#ifdef GRUB_MACHINE_EMU
++    devid = "host";
++#else
++    devid = grub_env_get ("root");
++#endif
++    if (!devid)
++      return grub_error (GRUB_ERR_FILE_NOT_FOUND,
++			 N_("variable `%s' isn't set"), "root");
++  }
++
++  grub_dprintf ("blscfg", "opening %s\n", devid);
++  dev = grub_device_open (devid);
++  if (!dev)
++    return grub_errno;
++
++  grub_dprintf ("blscfg", "probing fs\n");
++  fs = grub_fs_probe (dev);
++  if (!fs)
++    {
++      r = grub_errno;
++      goto finish;
++    }
++
++  info.dirname = blsdir;
++  info.devid = devid;
++  info.dev = dev;
++  info.fs = fs;
++  find_entry(&info);
++
++finish:
++  if (dev)
++    grub_device_close (dev);
++
++  return r;
++}
++
++static bool
++is_default_entry(const char *def_entry, struct bls_entry *entry, int idx)
++{
++  const char *title;
++  int def_idx;
++
++  if (!def_entry)
++    return false;
++
++  if (grub_strcmp(def_entry, entry->filename) == 0)
++    return true;
++
++  title = bls_get_val(entry, "title", NULL);
++
++  if (title && grub_strcmp(def_entry, title) == 0)
++    return true;
++
++  def_idx = (int)grub_strtol(def_entry, NULL, 0);
++  if (grub_errno == GRUB_ERR_BAD_NUMBER) {
++    grub_errno = GRUB_ERR_NONE;
++    return false;
++  }
++
++  if (def_idx == idx)
++    return true;
++
++  return false;
++}
++
++static grub_err_t
++bls_create_entries (bool show_default, bool show_non_default, char *entry_id)
++{
++  const char *def_entry = NULL;
++  struct bls_entry *entry = NULL;
++  int idx = 0;
++
++  def_entry = grub_env_get("default");
++
++  grub_dprintf ("blscfg", "%s Creating entries from bls\n", __func__);
++  FOR_BLS_ENTRIES(entry) {
++    if (entry->visible) {
++      idx++;
++      continue;
++    }
++
++    if ((show_default && is_default_entry(def_entry, entry, idx)) ||
++	(show_non_default && !is_default_entry(def_entry, entry, idx)) ||
++	(entry_id && grub_strcmp(entry_id, entry->filename) == 0)) {
++      create_entry(entry);
++      entry->visible = 1;
++    }
++    idx++;
++  }
++
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_cmd_blscfg (grub_extcmd_context_t ctxt UNUSED,
++		 int argc, char **args)
++{
++  grub_err_t r;
++  char *path = NULL;
++  char *entry_id = NULL;
++  bool show_default = true;
++  bool show_non_default = true;
++
++  if (argc == 1) {
++    if (grub_strcmp (args[0], "default") == 0) {
++      show_non_default = false;
++    } else if (grub_strcmp (args[0], "non-default") == 0) {
++      show_default = false;
++    } else if (args[0][0] == '(') {
++      path = args[0];
++    } else {
++      entry_id = args[0];
++      show_default = false;
++      show_non_default = false;
++    }
++  }
++
++  r = bls_load_entries(path);
++  if (r)
++    return r;
++
++  return bls_create_entries(show_default, show_non_default, entry_id);
++}
++
++static grub_extcmd_t cmd;
++static grub_extcmd_t oldcmd;
++
++GRUB_MOD_INIT(blscfg)
++{
++  grub_dprintf("blscfg", "%s got here\n", __func__);
++  cmd = grub_register_extcmd ("blscfg",
++			      grub_cmd_blscfg,
++			      0,
++			      NULL,
++			      N_("Import Boot Loader Specification snippets."),
++			      NULL);
++  oldcmd = grub_register_extcmd ("bls_import",
++				 grub_cmd_blscfg,
++				 0,
++				 NULL,
++				 N_("Import Boot Loader Specification snippets."),
++				 NULL);
++}
++
++GRUB_MOD_FINI(blscfg)
++{
++  grub_unregister_extcmd (cmd);
++  grub_unregister_extcmd (oldcmd);
++}
+diff --git a/grub-core/commands/legacycfg.c b/grub-core/commands/legacycfg.c
+index cc5971f4dbd..782761c31aa 100644
+--- a/grub-core/commands/legacycfg.c
++++ b/grub-core/commands/legacycfg.c
+@@ -143,7 +143,7 @@ legacy_file (const char *filename)
+ 	    args[0] = oldname;
+ 	    grub_normal_add_menu_entry (1, args, NULL, NULL, "legacy",
+ 					NULL, NULL,
+-					entrysrc, 0);
++					entrysrc, 0, NULL, NULL);
+ 	    grub_free (args);
+ 	    entrysrc[0] = 0;
+ 	    grub_free (oldname);
+@@ -205,7 +205,8 @@ legacy_file (const char *filename)
+ 	}
+       args[0] = entryname;
+       grub_normal_add_menu_entry (1, args, NULL, NULL, NULL,
+-				  NULL, NULL, entrysrc, 0);
++				  NULL, NULL, entrysrc, 0, NULL,
++				  NULL);
+       grub_free (args);
+     }
+ 
+diff --git a/grub-core/commands/loadenv.c b/grub-core/commands/loadenv.c
+index 3fd664aac33..163b9a09042 100644
+--- a/grub-core/commands/loadenv.c
++++ b/grub-core/commands/loadenv.c
+@@ -28,6 +28,8 @@
+ #include <grub/extcmd.h>
+ #include <grub/i18n.h>
+ 
++#include "loadenv.h"
++
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+ static const struct grub_arg_option options[] =
+@@ -79,81 +81,6 @@ open_envblk_file (char *filename,
+   return file;
+ }
+ 
+-static grub_envblk_t
+-read_envblk_file (grub_file_t file)
+-{
+-  grub_off_t offset = 0;
+-  char *buf;
+-  grub_size_t size = grub_file_size (file);
+-  grub_envblk_t envblk;
+-
+-  buf = grub_malloc (size);
+-  if (! buf)
+-    return 0;
+-
+-  while (size > 0)
+-    {
+-      grub_ssize_t ret;
+-
+-      ret = grub_file_read (file, buf + offset, size);
+-      if (ret <= 0)
+-        {
+-          grub_free (buf);
+-          return 0;
+-        }
+-
+-      size -= ret;
+-      offset += ret;
+-    }
+-
+-  envblk = grub_envblk_open (buf, offset);
+-  if (! envblk)
+-    {
+-      grub_free (buf);
+-      grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
+-      return 0;
+-    }
+-
+-  return envblk;
+-}
+-
+-struct grub_env_whitelist
+-{
+-  grub_size_t len;
+-  char **list;
+-};
+-typedef struct grub_env_whitelist grub_env_whitelist_t;
+-
+-static int
+-test_whitelist_membership (const char* name,
+-                           const grub_env_whitelist_t* whitelist)
+-{
+-  grub_size_t i;
+-
+-  for (i = 0; i < whitelist->len; i++)
+-    if (grub_strcmp (name, whitelist->list[i]) == 0)
+-      return 1;  /* found it */
+-
+-  return 0;  /* not found */
+-}
+-
+-/* Helper for grub_cmd_load_env.  */
+-static int
+-set_var (const char *name, const char *value, void *whitelist)
+-{
+-  if (! whitelist)
+-    {
+-      grub_env_set (name, value);
+-      return 0;
+-    }
+-
+-  if (test_whitelist_membership (name,
+-				 (const grub_env_whitelist_t *) whitelist))
+-    grub_env_set (name, value);
+-
+-  return 0;
+-}
+-
+ static grub_err_t
+ grub_cmd_load_env (grub_extcmd_context_t ctxt, int argc, char **args)
+ {
+diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c
+index 720e6d8ea3b..b194123eb67 100644
+--- a/grub-core/commands/menuentry.c
++++ b/grub-core/commands/menuentry.c
+@@ -78,7 +78,7 @@ grub_normal_add_menu_entry (int argc, const char **args,
+ 			    char **classes, const char *id,
+ 			    const char *users, const char *hotkey,
+ 			    const char *prefix, const char *sourcecode,
+-			    int submenu)
++			    int submenu, int *index, struct bls_entry *bls)
+ {
+   int menu_hotkey = 0;
+   char **menu_args = NULL;
+@@ -149,9 +149,12 @@ grub_normal_add_menu_entry (int argc, const char **args,
+   if (! menu_title)
+     goto fail;
+ 
++  grub_dprintf ("menu", "id:\"%s\"\n", id);
++  grub_dprintf ("menu", "title:\"%s\"\n", menu_title);
+   menu_id = grub_strdup (id ? : menu_title);
+   if (! menu_id)
+     goto fail;
++  grub_dprintf ("menu", "menu_id:\"%s\"\n", menu_id);
+ 
+   /* Save argc, args to pass as parameters to block arg later. */
+   menu_args = grub_calloc (argc + 1, sizeof (char *));
+@@ -170,8 +173,12 @@ grub_normal_add_menu_entry (int argc, const char **args,
+   }
+ 
+   /* Add the menu entry at the end of the list.  */
++  int ind=0;
+   while (*last)
+-    last = &(*last)->next;
++    {
++      ind++;
++      last = &(*last)->next;
++    }
+ 
+   *last = grub_zalloc (sizeof (**last));
+   if (! *last)
+@@ -188,8 +195,11 @@ grub_normal_add_menu_entry (int argc, const char **args,
+   (*last)->args = menu_args;
+   (*last)->sourcecode = menu_sourcecode;
+   (*last)->submenu = submenu;
++  (*last)->bls = bls;
+ 
+   menu->size++;
++  if (index)
++    *index = ind;
+   return GRUB_ERR_NONE;
+ 
+  fail:
+@@ -286,7 +296,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
+ 				       users,
+ 				       ctxt->state[2].arg, 0,
+ 				       ctxt->state[3].arg,
+-				       ctxt->extcmd->cmd->name[0] == 's');
++				       ctxt->extcmd->cmd->name[0] == 's',
++				       NULL, NULL);
+ 
+   src = args[argc - 1];
+   args[argc - 1] = NULL;
+@@ -303,7 +314,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
+ 				  ctxt->state[0].args, ctxt->state[4].arg,
+ 				  users,
+ 				  ctxt->state[2].arg, prefix, src + 1,
+-				  ctxt->extcmd->cmd->name[0] == 's');
++				  ctxt->extcmd->cmd->name[0] == 's', NULL,
++				  NULL);
+ 
+   src[len - 1] = ch;
+   args[argc - 1] = src;
+diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
+index 62571e6dfcc..7ca2e5400b1 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -21,6 +21,7 @@
+ #include <grub/net.h>
+ #include <grub/normal.h>
+ #include <grub/dl.h>
++#include <grub/menu.h>
+ #include <grub/misc.h>
+ #include <grub/file.h>
+ #include <grub/mm.h>
+@@ -70,6 +71,11 @@ grub_normal_free_menu (grub_menu_t menu)
+ 	  grub_free (entry->args);
+ 	}
+ 
++      if (entry->bls)
++	{
++	  entry->bls->visible = 0;
++	}
++
+       grub_free ((void *) entry->id);
+       grub_free ((void *) entry->users);
+       grub_free ((void *) entry->title);
+diff --git a/grub-core/commands/loadenv.h b/grub-core/commands/loadenv.h
+new file mode 100644
+index 00000000000..952f46121bd
+--- /dev/null
++++ b/grub-core/commands/loadenv.h
+@@ -0,0 +1,93 @@
++/* loadenv.c - command to load/save environment variable.  */
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2008,2009,2010  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++static grub_envblk_t UNUSED
++read_envblk_file (grub_file_t file)
++{
++  grub_off_t offset = 0;
++  char *buf;
++  grub_size_t size = grub_file_size (file);
++  grub_envblk_t envblk;
++
++  buf = grub_malloc (size);
++  if (! buf)
++    return 0;
++
++  while (size > 0)
++    {
++      grub_ssize_t ret;
++
++      ret = grub_file_read (file, buf + offset, size);
++      if (ret <= 0)
++        {
++          grub_free (buf);
++          return 0;
++        }
++
++      size -= ret;
++      offset += ret;
++    }
++
++  envblk = grub_envblk_open (buf, offset);
++  if (! envblk)
++    {
++      grub_free (buf);
++      grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
++      return 0;
++    }
++
++  return envblk;
++}
++
++struct grub_env_whitelist
++{
++  grub_size_t len;
++  char **list;
++};
++typedef struct grub_env_whitelist grub_env_whitelist_t;
++
++static int UNUSED
++test_whitelist_membership (const char* name,
++                           const grub_env_whitelist_t* whitelist)
++{
++  grub_size_t i;
++
++  for (i = 0; i < whitelist->len; i++)
++    if (grub_strcmp (name, whitelist->list[i]) == 0)
++      return 1;  /* found it */
++
++  return 0;  /* not found */
++}
++
++/* Helper for grub_cmd_load_env.  */
++static int UNUSED
++set_var (const char *name, const char *value, void *whitelist)
++{
++  if (! whitelist)
++    {
++      grub_env_set (name, value);
++      return 0;
++    }
++
++  if (test_whitelist_membership (name,
++				 (const grub_env_whitelist_t *) whitelist))
++    grub_env_set (name, value);
++
++  return 0;
++}
+diff --git a/include/grub/compiler.h b/include/grub/compiler.h
+index 8f3be3ae706..ebafec68957 100644
+--- a/include/grub/compiler.h
++++ b/include/grub/compiler.h
+@@ -56,4 +56,6 @@
+ #  define CLANG_PREREQ(maj,min) 0
+ #endif
+ 
++#define UNUSED __attribute__((__unused__))
++
+ #endif /* ! GRUB_COMPILER_HEADER */
+diff --git a/include/grub/menu.h b/include/grub/menu.h
+index ee2b5e91045..0acdc2aa6bf 100644
+--- a/include/grub/menu.h
++++ b/include/grub/menu.h
+@@ -20,6 +20,16 @@
+ #ifndef GRUB_MENU_HEADER
+ #define GRUB_MENU_HEADER 1
+ 
++struct bls_entry
++{
++  struct bls_entry *next;
++  struct bls_entry *prev;
++  struct keyval **keyvals;
++  int nkeyvals;
++  char *filename;
++  int visible;
++};
++
+ struct grub_menu_entry_class
+ {
+   char *name;
+@@ -60,6 +70,9 @@ struct grub_menu_entry
+ 
+   /* The next element.  */
+   struct grub_menu_entry *next;
++
++  /* BLS used to populate the entry */
++  struct bls_entry *bls;
+ };
+ typedef struct grub_menu_entry *grub_menu_entry_t;
+ 
+diff --git a/include/grub/normal.h b/include/grub/normal.h
+index 218cbabccaf..8839ad85a19 100644
+--- a/include/grub/normal.h
++++ b/include/grub/normal.h
+@@ -145,7 +145,7 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes,
+ 			    const char *id,
+ 			    const char *users, const char *hotkey,
+ 			    const char *prefix, const char *sourcecode,
+-			    int submenu);
++			    int submenu, int *index, struct bls_entry *bls);
+ 
+ grub_err_t
+ grub_normal_set_password (const char *user, const char *password);
diff --git a/SOURCES/0022-Add-devicetree-loading.patch b/SOURCES/0022-Add-devicetree-loading.patch
new file mode 100644
index 0000000..c0728a9
--- /dev/null
+++ b/SOURCES/0022-Add-devicetree-loading.patch
@@ -0,0 +1,68 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 14 Jan 2014 13:12:23 -0500
+Subject: [PATCH] Add devicetree loading
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+
+Switch to use APM Mustang device tree, for hardware testing.
+
+Signed-off-by: David A. Marlin <d.marlin@redhat.com>
+
+Use the default device tree from the grub default file
+
+instead of hardcoding a value.
+
+Signed-off-by: David A. Marlin <dmarlin@redhat.com>
+---
+ util/grub-mkconfig.in   |  3 ++-
+ util/grub.d/10_linux.in | 15 +++++++++++++++
+ 2 files changed, 17 insertions(+), 1 deletion(-)
+
+diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in
+index d3e879b8e5c..8ea2315ebc2 100644
+--- a/util/grub-mkconfig.in
++++ b/util/grub-mkconfig.in
+@@ -248,7 +248,8 @@ export GRUB_DEFAULT \
+   GRUB_ENABLE_CRYPTODISK \
+   GRUB_BADRAM \
+   GRUB_OS_PROBER_SKIP_LIST \
+-  GRUB_DISABLE_SUBMENU
++  GRUB_DISABLE_SUBMENU \
++  GRUB_DEFAULT_DTB
+ 
+ if test "x${grub_cfg}" != "x"; then
+   rm -f "${grub_cfg}.new"
+diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
+index e8b01c0d0c7..dc75a1c30bf 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -153,6 +153,13 @@ EOF
+     sed "s/^/$submenu_indentation/" << EOF
+ 	echo	'$(echo "$message" | grub_quote)'
+ 	initrd	$(echo $initrd_path)
++EOF
++  fi
++  if test -n "${fdt}" ; then
++    message="$(gettext_printf "Loading fdt ...")"
++    sed "s/^/$submenu_indentation/" << EOF
++	echo	'$(echo "$message" | grub_quote)'
++	devicetree	${rel_dirname}/${fdt}
+ EOF
+   fi
+   sed "s/^/$submenu_indentation/" << EOF
+@@ -236,6 +243,14 @@ while [ "x$list" != "x" ] ; do
+     gettext_printf "Found initrd image: %s\n" "$(echo $initrd_display)" >&2
+   fi
+ 
++  fdt=
++  for i in "dtb-${version}" "dtb-${alt_version}"; do
++    if test -f "${dirname}/${i}/${GRUB_DEFAULT_DTB}" ; then
++      fdt="${i}/${GRUB_DEFAULT_DTB}"
++      break
++    fi
++  done
++
+   config=
+   for i in "${dirname}/config-${version}" "${dirname}/config-${alt_version}" "/etc/kernels/kernel-config-${version}" ; do
+     if test -e "${i}" ; then
diff --git a/SOURCES/0023-Don-t-write-messages-to-the-screen.patch b/SOURCES/0023-Don-t-write-messages-to-the-screen.patch
new file mode 100644
index 0000000..01d0264
--- /dev/null
+++ b/SOURCES/0023-Don-t-write-messages-to-the-screen.patch
@@ -0,0 +1,176 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Jon McCann <william.jon.mccann@gmail.com>
+Date: Wed, 15 May 2013 13:30:20 -0400
+Subject: [PATCH] Don't write messages to the screen
+
+Writing messages to the screen before the menus or boot splash
+happens so quickly it looks like something is wrong and isn't
+very appealing.
+---
+ grub-core/gettext/gettext.c       | 25 +++++--------------------
+ grub-core/kern/main.c             |  5 -----
+ grub-core/boot/i386/pc/boot.S     |  3 ---
+ grub-core/boot/i386/pc/diskboot.S |  5 -----
+ util/grub.d/10_linux.in           |  7 -------
+ 5 files changed, 5 insertions(+), 40 deletions(-)
+
+diff --git a/grub-core/gettext/gettext.c b/grub-core/gettext/gettext.c
+index 4d02e62c109..84d520cd494 100644
+--- a/grub-core/gettext/gettext.c
++++ b/grub-core/gettext/gettext.c
+@@ -434,16 +434,12 @@ static char *
+ grub_gettext_env_write_lang (struct grub_env_var *var
+ 			     __attribute__ ((unused)), const char *val)
+ {
+-  grub_err_t err;
++  grub_err_t __attribute__((__unused__)) err;
+   err = grub_gettext_init_ext (&main_context, val, grub_env_get ("locale_dir"),
+ 			       grub_env_get ("prefix"));
+-  if (err)
+-    grub_print_error ();
+ 
+   err = grub_gettext_init_ext (&secondary_context, val,
+ 			       grub_env_get ("secondary_locale_dir"), 0);
+-  if (err)
+-    grub_print_error ();
+ 
+   return grub_strdup (val);
+ }
+@@ -451,23 +447,19 @@ grub_gettext_env_write_lang (struct grub_env_var *var
+ void
+ grub_gettext_reread_prefix (const char *val)
+ {
+-  grub_err_t err;
++  grub_err_t __attribute__((__unused__)) err;
+   err = grub_gettext_init_ext (&main_context, grub_env_get ("lang"), 
+ 			       grub_env_get ("locale_dir"),
+ 			       val);
+-  if (err)
+-    grub_print_error ();
+ }
+ 
+ static char *
+ read_main (struct grub_env_var *var
+ 	   __attribute__ ((unused)), const char *val)
+ {
+-  grub_err_t err;
++  grub_err_t __attribute__((__unused__)) err;
+   err = grub_gettext_init_ext (&main_context, grub_env_get ("lang"), val,
+ 			       grub_env_get ("prefix"));
+-  if (err)
+-    grub_print_error ();
+   return grub_strdup (val);
+ }
+ 
+@@ -475,12 +467,9 @@ static char *
+ read_secondary (struct grub_env_var *var
+ 		__attribute__ ((unused)), const char *val)
+ {
+-  grub_err_t err;
++  grub_err_t __attribute__((__unused__)) err;
+   err = grub_gettext_init_ext (&secondary_context, grub_env_get ("lang"), val,
+ 			       0);
+-  if (err)
+-    grub_print_error ();
+-
+   return grub_strdup (val);
+ }
+ 
+@@ -500,18 +489,14 @@ grub_cmd_translate (grub_command_t cmd __attribute__ ((unused)),
+ GRUB_MOD_INIT (gettext)
+ {
+   const char *lang;
+-  grub_err_t err;
++  grub_err_t __attribute__((__unused__)) err;
+ 
+   lang = grub_env_get ("lang");
+ 
+   err = grub_gettext_init_ext (&main_context, lang, grub_env_get ("locale_dir"),
+ 			       grub_env_get ("prefix"));
+-  if (err)
+-    grub_print_error ();
+   err = grub_gettext_init_ext (&secondary_context, lang,
+ 			       grub_env_get ("secondary_locale_dir"), 0);
+-  if (err)
+-    grub_print_error ();
+ 
+   grub_register_variable_hook ("locale_dir", NULL, read_main);
+   grub_register_variable_hook ("secondary_locale_dir", NULL, read_secondary);
+diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c
+index d1de9fa6873..48058d983ce 100644
+--- a/grub-core/kern/main.c
++++ b/grub-core/kern/main.c
+@@ -269,11 +269,6 @@ grub_main (void)
+ 
+   grub_boot_time ("After machine init.");
+ 
+-  /* Hello.  */
+-  grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
+-  grub_printf ("Welcome to GRUB!\n\n");
+-  grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
+-
+   /* Init verifiers API. */
+   grub_verifiers_init ();
+ 
+diff --git a/grub-core/boot/i386/pc/boot.S b/grub-core/boot/i386/pc/boot.S
+index 2bd0b2d2866..ea167fe1206 100644
+--- a/grub-core/boot/i386/pc/boot.S
++++ b/grub-core/boot/i386/pc/boot.S
+@@ -249,9 +249,6 @@ real_start:
+ 	/* save drive reference first thing! */
+ 	pushw	%dx
+ 
+-	/* print a notification message on the screen */
+-	MSG(notification_string)
+-
+ 	/* set %si to the disk address packet */
+ 	movw	$disk_address_packet, %si
+ 
+diff --git a/grub-core/boot/i386/pc/diskboot.S b/grub-core/boot/i386/pc/diskboot.S
+index c1addc0df29..68d31de0c4c 100644
+--- a/grub-core/boot/i386/pc/diskboot.S
++++ b/grub-core/boot/i386/pc/diskboot.S
+@@ -50,11 +50,6 @@ _start:
+ 	/* save drive reference first thing! */
+ 	pushw	%dx
+ 
+-	/* print a notification message on the screen */
+-	pushw	%si
+-	MSG(notification_string)
+-	popw	%si
+-
+ 	/* this sets up for the first run through "bootloop" */
+ 	movw	$LOCAL(firstlist), %di
+ 
+diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
+index dc75a1c30bf..ad2ac4b078d 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -138,27 +138,20 @@ linux_entry ()
+     fi
+     printf '%s\n' "${prepare_boot_cache}" | sed "s/^/$submenu_indentation/"
+   fi
+-  message="$(gettext_printf "Loading Linux %s ..." ${version})"
+   sed "s/^/$submenu_indentation/" << EOF
+-	echo	'$(echo "$message" | grub_quote)'
+ 	linux	${rel_dirname}/${basename} root=${linux_root_device_thisversion} ro ${args}
+ EOF
+   if test -n "${initrd}" ; then
+-    # TRANSLATORS: ramdisk isn't identifier. Should be translated.
+-    message="$(gettext_printf "Loading initial ramdisk ...")"
+     initrd_path=
+     for i in ${initrd}; do
+       initrd_path="${initrd_path} ${rel_dirname}/${i}"
+     done
+     sed "s/^/$submenu_indentation/" << EOF
+-	echo	'$(echo "$message" | grub_quote)'
+ 	initrd	$(echo $initrd_path)
+ EOF
+   fi
+   if test -n "${fdt}" ; then
+-    message="$(gettext_printf "Loading fdt ...")"
+     sed "s/^/$submenu_indentation/" << EOF
+-	echo	'$(echo "$message" | grub_quote)'
+ 	devicetree	${rel_dirname}/${fdt}
+ EOF
+   fi
diff --git a/SOURCES/0024-Don-t-print-GNU-GRUB-header.patch b/SOURCES/0024-Don-t-print-GNU-GRUB-header.patch
new file mode 100644
index 0000000..c903fd4
--- /dev/null
+++ b/SOURCES/0024-Don-t-print-GNU-GRUB-header.patch
@@ -0,0 +1,42 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Jon McCann <william.jon.mccann@gmail.com>
+Date: Wed, 15 May 2013 13:53:48 -0400
+Subject: [PATCH] Don't print GNU GRUB header
+
+No one cares.
+---
+ grub-core/normal/main.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
+index 7ca2e5400b1..5d5f7b539f5 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -208,15 +208,16 @@ read_config_file (const char *config)
+ /* Initialize the screen.  */
+ void
+ grub_normal_init_page (struct grub_term_output *term,
+-		       int y)
++		       int y __attribute__((__unused__)))
+ {
++  grub_term_cls (term);
++
++#if 0
+   grub_ssize_t msg_len;
+   int posx;
+   char *msg_formatted;
+   grub_uint32_t *unicode_msg;
+   grub_uint32_t *last_position;
+- 
+-  grub_term_cls (term);
+ 
+   msg_formatted = grub_xasprintf (_("GNU GRUB  version %s"), PACKAGE_VERSION);
+   if (!msg_formatted)
+@@ -241,6 +242,7 @@ grub_normal_init_page (struct grub_term_output *term,
+   grub_putcode ('\n', term);
+   grub_putcode ('\n', term);
+   grub_free (unicode_msg);
++#endif
+ }
+ 
+ static void
diff --git a/SOURCES/0025-Don-t-add-to-highlighted-row.patch b/SOURCES/0025-Don-t-add-to-highlighted-row.patch
new file mode 100644
index 0000000..b83f543
--- /dev/null
+++ b/SOURCES/0025-Don-t-add-to-highlighted-row.patch
@@ -0,0 +1,23 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Jon McCann <william.jon.mccann@gmail.com>
+Date: Wed, 15 May 2013 17:49:45 -0400
+Subject: [PATCH] Don't add '*' to highlighted row
+
+It is already highlighted.
+---
+ grub-core/normal/menu_text.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/normal/menu_text.c b/grub-core/normal/menu_text.c
+index 18240e76cea..65deafda531 100644
+--- a/grub-core/normal/menu_text.c
++++ b/grub-core/normal/menu_text.c
+@@ -242,7 +242,7 @@ print_entry (int y, int highlight, grub_menu_entry_t entry,
+       unicode_title[i] = ' ';
+ 
+   if (data->geo.num_entries > 1)
+-    grub_putcode (highlight ? '*' : ' ', data->term);
++    grub_putcode (' ', data->term);
+ 
+   grub_print_ucs4_menu (unicode_title,
+ 			unicode_title + len,
diff --git a/SOURCES/0026-Message-string-cleanups.patch b/SOURCES/0026-Message-string-cleanups.patch
new file mode 100644
index 0000000..e23ee1a
--- /dev/null
+++ b/SOURCES/0026-Message-string-cleanups.patch
@@ -0,0 +1,68 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Jon McCann <william.jon.mccann@gmail.com>
+Date: Fri, 7 Jun 2013 11:09:04 -0400
+Subject: [PATCH] Message string cleanups
+
+Make use of terminology consistent. Remove jargon.
+---
+ grub-core/normal/menu_text.c | 21 +++++++++------------
+ 1 file changed, 9 insertions(+), 12 deletions(-)
+
+diff --git a/grub-core/normal/menu_text.c b/grub-core/normal/menu_text.c
+index 65deafda531..cc5837ed2b8 100644
+--- a/grub-core/normal/menu_text.c
++++ b/grub-core/normal/menu_text.c
+@@ -157,9 +157,8 @@ print_message (int nested, int edit, struct grub_term_output *term, int dry_run)
+ 
+   if (edit)
+     {
+-      ret += grub_print_message_indented_real (_("Minimum Emacs-like screen editing is \
+-supported. TAB lists completions. Press Ctrl-x or F10 to boot, Ctrl-c or F2 for a \
+-command-line or ESC to discard edits and return to the GRUB menu."),
++      ret += grub_print_message_indented_real (_("Press Ctrl-x or F10 to start, Ctrl-c or F2 for a \
++command prompt or Escape to discard edits and return to the menu. Pressing Tab lists possible completions."),
+ 					       STANDARD_MARGIN, STANDARD_MARGIN,
+ 					       term, dry_run);
+     }
+@@ -167,8 +166,8 @@ command-line or ESC to discard edits and return to the GRUB menu."),
+     {
+       char *msg_translated;
+ 
+-      msg_translated = grub_xasprintf (_("Use the %C and %C keys to select which "
+-					 "entry is highlighted."),
++      msg_translated = grub_xasprintf (_("Use the %C and %C keys to change the "
++					 "selection."),
+ 				       GRUB_UNICODE_UPARROW,
+ 				       GRUB_UNICODE_DOWNARROW);
+       if (!msg_translated)
+@@ -181,17 +180,15 @@ command-line or ESC to discard edits and return to the GRUB menu."),
+       if (nested)
+ 	{
+ 	  ret += grub_print_message_indented_real
+-	    (_("Press enter to boot the selected OS, "
+-	       "`e' to edit the commands before booting "
+-	       "or `c' for a command-line. ESC to return previous menu."),
++	    (_("Press 'e' to edit the selected item, "
++	       "or 'c' for a command prompt. Press Escape to return to the previous menu."),
+ 	     STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run);
+ 	}
+       else
+ 	{
+ 	  ret += grub_print_message_indented_real
+-	    (_("Press enter to boot the selected OS, "
+-	       "`e' to edit the commands before booting "
+-	       "or `c' for a command-line."),
++	    (_("Press 'e' to edit the selected item, "
++	       "or 'c' for a command prompt."),
+ 	     STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run);
+ 	}	
+     }
+@@ -443,7 +440,7 @@ menu_text_print_timeout (int timeout, void *dataptr)
+       || data->timeout_msg == TIMEOUT_TERSE_NO_MARGIN)
+     msg_translated = grub_xasprintf (_("%ds"), timeout);
+   else
+-    msg_translated = grub_xasprintf (_("The highlighted entry will be executed automatically in %ds."), timeout);
++    msg_translated = grub_xasprintf (_("The selected entry will be started automatically in %ds."), timeout);
+   if (!msg_translated)
+     {
+       grub_print_error ();
diff --git a/SOURCES/0027-Fix-border-spacing-now-that-we-aren-t-displaying-it.patch b/SOURCES/0027-Fix-border-spacing-now-that-we-aren-t-displaying-it.patch
new file mode 100644
index 0000000..920ef79
--- /dev/null
+++ b/SOURCES/0027-Fix-border-spacing-now-that-we-aren-t-displaying-it.patch
@@ -0,0 +1,29 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Jon McCann <william.jon.mccann@gmail.com>
+Date: Fri, 7 Jun 2013 14:08:23 -0400
+Subject: [PATCH] Fix border spacing now that we aren't displaying it
+
+---
+ grub-core/normal/menu_text.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/normal/menu_text.c b/grub-core/normal/menu_text.c
+index cc5837ed2b8..b49835a9af7 100644
+--- a/grub-core/normal/menu_text.c
++++ b/grub-core/normal/menu_text.c
+@@ -331,12 +331,12 @@ grub_menu_init_page (int nested, int edit,
+   int empty_lines = 1;
+   int version_msg = 1;
+ 
+-  geo->border = 1;
+-  geo->first_entry_x = 1 /* margin */ + 1 /* border */;
++  geo->border = 0;
++  geo->first_entry_x = 0 /* margin */ + 0 /* border */;
+   geo->entry_width = grub_term_width (term) - 5;
+ 
+   geo->first_entry_y = 2 /* two empty lines*/
+-    + 1 /* GNU GRUB version text  */ + 1 /* top border */;
++    + 0 /* GNU GRUB version text  */ + 1 /* top border */;
+ 
+   geo->timeout_lines = 2;
+ 
diff --git a/SOURCES/0028-Use-the-correct-indentation-for-the-term-help-text.patch b/SOURCES/0028-Use-the-correct-indentation-for-the-term-help-text.patch
new file mode 100644
index 0000000..0ce3b04
--- /dev/null
+++ b/SOURCES/0028-Use-the-correct-indentation-for-the-term-help-text.patch
@@ -0,0 +1,25 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Jon McCann <william.jon.mccann@gmail.com>
+Date: Fri, 7 Jun 2013 14:08:49 -0400
+Subject: [PATCH] Use the correct indentation for the term help text
+
+That is consistent with the menu help text
+---
+ grub-core/normal/main.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
+index 5d5f7b539f5..ec1cd257397 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -440,8 +440,8 @@ grub_normal_reader_init (int nested)
+     grub_normal_init_page (term, 1);
+     grub_term_setcursor (term, 1);
+ 
+-    if (grub_term_width (term) > 3 + STANDARD_MARGIN + 20)
+-      grub_print_message_indented (msg_formatted, 3, STANDARD_MARGIN, term);
++    if (grub_term_width (term) > 2 * STANDARD_MARGIN + 20)
++      grub_print_message_indented (msg_formatted, STANDARD_MARGIN, STANDARD_MARGIN, term);
+     else
+       grub_print_message_indented (msg_formatted, 0, 0, term);
+     grub_putcode ('\n', term);
diff --git a/SOURCES/0029-Indent-menu-entries.patch b/SOURCES/0029-Indent-menu-entries.patch
new file mode 100644
index 0000000..7e588fd
--- /dev/null
+++ b/SOURCES/0029-Indent-menu-entries.patch
@@ -0,0 +1,23 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Jon McCann <william.jon.mccann@gmail.com>
+Date: Fri, 7 Jun 2013 14:30:55 -0400
+Subject: [PATCH] Indent menu entries
+
+---
+ grub-core/normal/menu_text.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/normal/menu_text.c b/grub-core/normal/menu_text.c
+index b49835a9af7..6a57376fa80 100644
+--- a/grub-core/normal/menu_text.c
++++ b/grub-core/normal/menu_text.c
+@@ -239,7 +239,8 @@ print_entry (int y, int highlight, grub_menu_entry_t entry,
+       unicode_title[i] = ' ';
+ 
+   if (data->geo.num_entries > 1)
+-    grub_putcode (' ', data->term);
++    for (i = 0; i < STANDARD_MARGIN; i++)
++      grub_putcode (' ', data->term);
+ 
+   grub_print_ucs4_menu (unicode_title,
+ 			unicode_title + len,
diff --git a/SOURCES/0030-Fix-margins.patch b/SOURCES/0030-Fix-margins.patch
new file mode 100644
index 0000000..baa10ed
--- /dev/null
+++ b/SOURCES/0030-Fix-margins.patch
@@ -0,0 +1,34 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Jon McCann <william.jon.mccann@gmail.com>
+Date: Fri, 7 Jun 2013 14:59:36 -0400
+Subject: [PATCH] Fix margins
+
+---
+ grub-core/normal/menu_text.c | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+diff --git a/grub-core/normal/menu_text.c b/grub-core/normal/menu_text.c
+index 6a57376fa80..cbd62f714cb 100644
+--- a/grub-core/normal/menu_text.c
++++ b/grub-core/normal/menu_text.c
+@@ -333,17 +333,15 @@ grub_menu_init_page (int nested, int edit,
+   int version_msg = 1;
+ 
+   geo->border = 0;
+-  geo->first_entry_x = 0 /* margin */ + 0 /* border */;
+-  geo->entry_width = grub_term_width (term) - 5;
++  geo->first_entry_x = 0; /* no margin */
++  geo->entry_width = grub_term_width (term) - 1;
+ 
+-  geo->first_entry_y = 2 /* two empty lines*/
+-    + 0 /* GNU GRUB version text  */ + 1 /* top border */;
++  geo->first_entry_y = 3; /* three empty lines*/
+ 
+   geo->timeout_lines = 2;
+ 
+   /* 3 lines for timeout message and bottom margin.  2 lines for the border.  */
+   geo->num_entries = grub_term_height (term) - geo->first_entry_y
+-    - 1 /* bottom border */
+     - 1 /* empty line before info message*/
+     - geo->timeout_lines /* timeout */
+     - 1 /* empty final line  */;
diff --git a/SOURCES/0031-Use-2-instead-of-1-for-our-right-hand-margin-so-line.patch b/SOURCES/0031-Use-2-instead-of-1-for-our-right-hand-margin-so-line.patch
new file mode 100644
index 0000000..7932180
--- /dev/null
+++ b/SOURCES/0031-Use-2-instead-of-1-for-our-right-hand-margin-so-line.patch
@@ -0,0 +1,24 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Fri, 21 Jun 2013 14:44:08 -0400
+Subject: [PATCH] Use -2 instead of -1 for our right-hand margin, so
+ linewrapping works (#976643).
+
+Signed-off-by: Peter Jones <grub2-owner@fedoraproject.org>
+---
+ grub-core/normal/menu_text.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/normal/menu_text.c b/grub-core/normal/menu_text.c
+index cbd62f714cb..26e9e82042a 100644
+--- a/grub-core/normal/menu_text.c
++++ b/grub-core/normal/menu_text.c
+@@ -334,7 +334,7 @@ grub_menu_init_page (int nested, int edit,
+ 
+   geo->border = 0;
+   geo->first_entry_x = 0; /* no margin */
+-  geo->entry_width = grub_term_width (term) - 1;
++  geo->entry_width = grub_term_width (term) - 2;
+ 
+   geo->first_entry_y = 3; /* three empty lines*/
+ 
diff --git a/SOURCES/0032-Enable-pager-by-default.-985860.patch b/SOURCES/0032-Enable-pager-by-default.-985860.patch
new file mode 100644
index 0000000..d92fbcc
--- /dev/null
+++ b/SOURCES/0032-Enable-pager-by-default.-985860.patch
@@ -0,0 +1,23 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 28 Oct 2013 10:09:27 -0400
+Subject: [PATCH] Enable pager by default. (#985860)
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ util/grub.d/00_header.in | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in
+index 93a90233ead..858b526c925 100644
+--- a/util/grub.d/00_header.in
++++ b/util/grub.d/00_header.in
+@@ -43,6 +43,8 @@ if [ "x${GRUB_DEFAULT_BUTTON}" = "xsaved" ] ; then GRUB_DEFAULT_BUTTON='${saved_
+ if [ "x${GRUB_TIMEOUT_BUTTON}" = "x" ] ; then GRUB_TIMEOUT_BUTTON="$GRUB_TIMEOUT" ; fi
+ 
+ cat << EOF
++set pager=1
++
+ if [ -s \$prefix/grubenv ]; then
+   load_env
+ fi
diff --git a/SOURCES/0033-F10-doesn-t-work-on-serial-so-don-t-tell-the-user-to.patch b/SOURCES/0033-F10-doesn-t-work-on-serial-so-don-t-tell-the-user-to.patch
new file mode 100644
index 0000000..7e9e829
--- /dev/null
+++ b/SOURCES/0033-F10-doesn-t-work-on-serial-so-don-t-tell-the-user-to.patch
@@ -0,0 +1,24 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 28 Oct 2013 10:13:27 -0400
+Subject: [PATCH] F10 doesn't work on serial, so don't tell the user to hit it
+ (#987443)
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/normal/menu_text.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/normal/menu_text.c b/grub-core/normal/menu_text.c
+index 26e9e82042a..4895ffe7d1d 100644
+--- a/grub-core/normal/menu_text.c
++++ b/grub-core/normal/menu_text.c
+@@ -157,7 +157,7 @@ print_message (int nested, int edit, struct grub_term_output *term, int dry_run)
+ 
+   if (edit)
+     {
+-      ret += grub_print_message_indented_real (_("Press Ctrl-x or F10 to start, Ctrl-c or F2 for a \
++      ret += grub_print_message_indented_real (_("Press Ctrl-x to start, Ctrl-c for a \
+ command prompt or Escape to discard edits and return to the menu. Pressing Tab lists possible completions."),
+ 					       STANDARD_MARGIN, STANDARD_MARGIN,
+ 					       term, dry_run);
diff --git a/SOURCES/0034-Don-t-say-GNU-Linux-in-generated-menus.patch b/SOURCES/0034-Don-t-say-GNU-Linux-in-generated-menus.patch
new file mode 100644
index 0000000..db2a8b5
--- /dev/null
+++ b/SOURCES/0034-Don-t-say-GNU-Linux-in-generated-menus.patch
@@ -0,0 +1,42 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 14 Mar 2011 14:27:42 -0400
+Subject: [PATCH] Don't say "GNU/Linux" in generated menus.
+
+---
+ util/grub.d/10_linux.in     | 4 ++--
+ util/grub.d/20_linux_xen.in | 4 ++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
+index ad2ac4b078d..4fc58c83304 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -29,9 +29,9 @@ export TEXTDOMAINDIR="@localedir@"
+ CLASS="--class gnu-linux --class gnu --class os"
+ 
+ if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then
+-  OS=GNU/Linux
++  OS="$(sed 's, release .*$,,g' /etc/system-release)"
+ else
+-  OS="${GRUB_DISTRIBUTOR} GNU/Linux"
++  OS="${GRUB_DISTRIBUTOR}"
+   CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr 'A-Z' 'a-z' | cut -d' ' -f1|LC_ALL=C sed 's,[^[:alnum:]_],_,g') ${CLASS}"
+ fi
+ 
+diff --git a/util/grub.d/20_linux_xen.in b/util/grub.d/20_linux_xen.in
+index 3b1f4704921..ada20775a14 100644
+--- a/util/grub.d/20_linux_xen.in
++++ b/util/grub.d/20_linux_xen.in
+@@ -29,9 +29,9 @@ export TEXTDOMAINDIR="@localedir@"
+ CLASS="--class gnu-linux --class gnu --class os --class xen"
+ 
+ if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then
+-  OS=GNU/Linux
++  OS="$(sed 's, release .*$,,g' /etc/system-release)"
+ else
+-  OS="${GRUB_DISTRIBUTOR} GNU/Linux"
++  OS="${GRUB_DISTRIBUTOR}"
+   CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr 'A-Z' 'a-z' | cut -d' ' -f1|LC_ALL=C sed 's,[^[:alnum:]_],_,g') ${CLASS}"
+ fi
+ 
diff --git a/SOURCES/0035-Don-t-draw-a-border-around-the-menu.patch b/SOURCES/0035-Don-t-draw-a-border-around-the-menu.patch
new file mode 100644
index 0000000..31a973e
--- /dev/null
+++ b/SOURCES/0035-Don-t-draw-a-border-around-the-menu.patch
@@ -0,0 +1,71 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Jon McCann <william.jon.mccann@gmail.com>
+Date: Wed, 15 May 2013 16:47:33 -0400
+Subject: [PATCH] Don't draw a border around the menu
+
+It looks cleaner without it.
+---
+ grub-core/normal/menu_text.c | 43 -------------------------------------------
+ 1 file changed, 43 deletions(-)
+
+diff --git a/grub-core/normal/menu_text.c b/grub-core/normal/menu_text.c
+index 4895ffe7d1d..e72ed438ba3 100644
+--- a/grub-core/normal/menu_text.c
++++ b/grub-core/normal/menu_text.c
+@@ -108,47 +108,6 @@ grub_print_message_indented (const char *msg, int margin_left, int margin_right,
+   grub_print_message_indented_real (msg, margin_left, margin_right, term, 0);
+ }
+ 
+-static void
+-draw_border (struct grub_term_output *term, const struct grub_term_screen_geometry *geo)
+-{
+-  int i;
+-
+-  grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
+-
+-  grub_term_gotoxy (term, (struct grub_term_coordinate) { geo->first_entry_x - 1,
+-	geo->first_entry_y - 1 });
+-  grub_putcode (GRUB_UNICODE_CORNER_UL, term);
+-  for (i = 0; i < geo->entry_width + 1; i++)
+-    grub_putcode (GRUB_UNICODE_HLINE, term);
+-  grub_putcode (GRUB_UNICODE_CORNER_UR, term);
+-
+-  for (i = 0; i < geo->num_entries; i++)
+-    {
+-      grub_term_gotoxy (term, (struct grub_term_coordinate) { geo->first_entry_x - 1,
+-	    geo->first_entry_y + i });
+-      grub_putcode (GRUB_UNICODE_VLINE, term);
+-      grub_term_gotoxy (term,
+-			(struct grub_term_coordinate) { geo->first_entry_x + geo->entry_width + 1,
+-			    geo->first_entry_y + i });
+-      grub_putcode (GRUB_UNICODE_VLINE, term);
+-    }
+-
+-  grub_term_gotoxy (term,
+-		    (struct grub_term_coordinate) { geo->first_entry_x - 1,
+-			geo->first_entry_y - 1 + geo->num_entries + 1 });
+-  grub_putcode (GRUB_UNICODE_CORNER_LL, term);
+-  for (i = 0; i < geo->entry_width + 1; i++)
+-    grub_putcode (GRUB_UNICODE_HLINE, term);
+-  grub_putcode (GRUB_UNICODE_CORNER_LR, term);
+-
+-  grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
+-
+-  grub_term_gotoxy (term,
+-		    (struct grub_term_coordinate) { geo->first_entry_x - 1,
+-			(geo->first_entry_y - 1 + geo->num_entries
+-			 + GRUB_TERM_MARGIN + 1) });
+-}
+-
+ static int
+ print_message (int nested, int edit, struct grub_term_output *term, int dry_run)
+ {
+@@ -406,8 +365,6 @@ grub_menu_init_page (int nested, int edit,
+ 
+   grub_term_normal_color = grub_color_menu_normal;
+   grub_term_highlight_color = grub_color_menu_highlight;
+-  if (geo->border)
+-    draw_border (term, geo);
+   grub_term_normal_color = old_color_normal;
+   grub_term_highlight_color = old_color_highlight;
+   geo->timeout_y = geo->first_entry_y + geo->num_entries
diff --git a/SOURCES/0036-Use-the-standard-margin-for-the-timeout-string.patch b/SOURCES/0036-Use-the-standard-margin-for-the-timeout-string.patch
new file mode 100644
index 0000000..49a382d
--- /dev/null
+++ b/SOURCES/0036-Use-the-standard-margin-for-the-timeout-string.patch
@@ -0,0 +1,40 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Jon McCann <william.jon.mccann@gmail.com>
+Date: Fri, 7 Jun 2013 10:52:32 -0400
+Subject: [PATCH] Use the standard margin for the timeout string
+
+So that it aligns with the other messages
+---
+ grub-core/normal/menu_text.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/normal/menu_text.c b/grub-core/normal/menu_text.c
+index e72ed438ba3..ca135624356 100644
+--- a/grub-core/normal/menu_text.c
++++ b/grub-core/normal/menu_text.c
+@@ -372,7 +372,7 @@ grub_menu_init_page (int nested, int edit,
+   if (bottom_message)
+     {
+       grub_term_gotoxy (term,
+-			(struct grub_term_coordinate) { GRUB_TERM_MARGIN,
++			(struct grub_term_coordinate) { STANDARD_MARGIN,
+ 			    geo->timeout_y });
+ 
+       print_message (nested, edit, term, 0);
+@@ -407,14 +407,14 @@ menu_text_print_timeout (int timeout, void *dataptr)
+   if (data->timeout_msg == TIMEOUT_UNKNOWN)
+     {
+       data->timeout_msg = grub_print_message_indented_real (msg_translated,
+-							    3, 1, data->term, 1)
++							    STANDARD_MARGIN, 1, data->term, 1)
+ 	<= data->geo.timeout_lines ? TIMEOUT_NORMAL : TIMEOUT_TERSE;
+       if (data->timeout_msg == TIMEOUT_TERSE)
+ 	{
+ 	  grub_free (msg_translated);
+ 	  msg_translated = grub_xasprintf (_("%ds"), timeout);
+ 	  if (grub_term_width (data->term) < 10)
+-	    data->timeout_msg = TIMEOUT_TERSE_NO_MARGIN;
++	    data->timeout_msg = STANDARD_MARGIN;
+ 	}
+     }
+ 
diff --git a/SOURCES/0037-Add-.eh_frame-to-list-of-relocations-stripped.patch b/SOURCES/0037-Add-.eh_frame-to-list-of-relocations-stripped.patch
new file mode 100644
index 0000000..ed48c5c
--- /dev/null
+++ b/SOURCES/0037-Add-.eh_frame-to-list-of-relocations-stripped.patch
@@ -0,0 +1,22 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fedora Ninjas <grub2-owner@fedoraproject.org>
+Date: Mon, 13 Jan 2014 21:50:59 -0500
+Subject: [PATCH] Add .eh_frame to list of relocations stripped
+
+---
+ conf/Makefile.common | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/conf/Makefile.common b/conf/Makefile.common
+index 2a1a886f6d5..191b1a70c6b 100644
+--- a/conf/Makefile.common
++++ b/conf/Makefile.common
+@@ -38,7 +38,7 @@ CFLAGS_KERNEL = $(CFLAGS_PLATFORM) -ffreestanding
+ LDFLAGS_KERNEL = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC)
+ CPPFLAGS_KERNEL = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) -DGRUB_KERNEL=1
+ CCASFLAGS_KERNEL = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM)
+-STRIPFLAGS_KERNEL = -R .rel.dyn -R .reginfo -R .note -R .comment -R .drectve -R .note.gnu.gold-version -R .MIPS.abiflags -R .ARM.exidx
++STRIPFLAGS_KERNEL = -R .eh_frame -R .rel.dyn -R .reginfo -R .note -R .comment -R .drectve -R .note.gnu.gold-version -R .MIPS.abiflags -R .ARM.exidx
+ 
+ CFLAGS_MODULE = $(CFLAGS_PLATFORM) -ffreestanding
+ LDFLAGS_MODULE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-r,-d
diff --git a/SOURCES/0038-Don-t-require-a-password-to-boot-entries-generated-b.patch b/SOURCES/0038-Don-t-require-a-password-to-boot-entries-generated-b.patch
new file mode 100644
index 0000000..fb0e197
--- /dev/null
+++ b/SOURCES/0038-Don-t-require-a-password-to-boot-entries-generated-b.patch
@@ -0,0 +1,28 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 11 Feb 2014 11:14:50 -0500
+Subject: [PATCH] Don't require a password to boot entries generated by
+ grub-mkconfig.
+
+When we set a password, we just want that to mean you can't /edit/ an entry.
+
+Resolves: rhbz#1030176
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ util/grub.d/10_linux.in | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
+index 4fc58c83304..635d2fe0cd3 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -26,7 +26,7 @@ datarootdir="@datarootdir@"
+ export TEXTDOMAIN=@PACKAGE@
+ export TEXTDOMAINDIR="@localedir@"
+ 
+-CLASS="--class gnu-linux --class gnu --class os"
++CLASS="--class gnu-linux --class gnu --class os --unrestricted"
+ 
+ if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then
+   OS="$(sed 's, release .*$,,g' /etc/system-release)"
diff --git a/SOURCES/0039-Don-t-emit-Booting-.-message.patch b/SOURCES/0039-Don-t-emit-Booting-.-message.patch
new file mode 100644
index 0000000..4133b52
--- /dev/null
+++ b/SOURCES/0039-Don-t-emit-Booting-.-message.patch
@@ -0,0 +1,49 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 18 Feb 2014 09:37:49 -0500
+Subject: [PATCH] Don't emit "Booting ..." message.
+
+UI team still hates this stuff, so we're disabling it for RHEL 7.
+
+Resolves: rhbz#1023142
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/normal/menu.c       | 4 +++-
+ grub-core/normal/menu_entry.c | 3 ---
+ 2 files changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c
+index d7a222e681b..37d753d8081 100644
+--- a/grub-core/normal/menu.c
++++ b/grub-core/normal/menu.c
+@@ -838,12 +838,14 @@ run_menu (grub_menu_t menu, int nested, int *auto_boot)
+ 
+ /* Callback invoked immediately before a menu entry is executed.  */
+ static void
+-notify_booting (grub_menu_entry_t entry,
++notify_booting (grub_menu_entry_t __attribute__((unused)) entry,
+ 		void *userdata __attribute__((unused)))
+ {
++#if 0
+   grub_printf ("  ");
+   grub_printf_ (N_("Booting `%s'"), entry->title);
+   grub_printf ("\n\n");
++#endif
+ }
+ 
+ /* Callback invoked when a default menu entry executed because of a timeout
+diff --git a/grub-core/normal/menu_entry.c b/grub-core/normal/menu_entry.c
+index 50eef918cf6..de64a367c4e 100644
+--- a/grub-core/normal/menu_entry.c
++++ b/grub-core/normal/menu_entry.c
+@@ -1176,9 +1176,6 @@ run (struct screen *screen)
+   char *dummy[1] = { NULL };
+ 
+   grub_cls ();
+-  grub_printf ("  ");
+-  grub_printf_ (N_("Booting a command list"));
+-  grub_printf ("\n\n");
+ 
+   errs_before = grub_err_printed_errors;
+ 
diff --git a/SOURCES/0040-Replace-a-lot-of-man-pages-with-slightly-nicer-ones.patch b/SOURCES/0040-Replace-a-lot-of-man-pages-with-slightly-nicer-ones.patch
new file mode 100644
index 0000000..daa6fae
--- /dev/null
+++ b/SOURCES/0040-Replace-a-lot-of-man-pages-with-slightly-nicer-ones.patch
@@ -0,0 +1,1959 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 4 Mar 2014 11:00:23 -0500
+Subject: [PATCH] Replace a lot of man pages with slightly nicer ones.
+
+Replace a bunch of machine generated ones with ones that look nicer.
+---
+ configure.ac                      |  23 ++++++
+ conf/Makefile.extra-dist          |   1 -
+ docs/Makefile.am                  |   2 -
+ docs/man/grub-bios-setup.h2m      |   6 --
+ docs/man/grub-editenv.h2m         |   5 --
+ docs/man/grub-emu.h2m             |   6 --
+ docs/man/grub-file.h2m            |   2 -
+ docs/man/grub-fstest.h2m          |   4 -
+ docs/man/grub-glue-efi.h2m        |   4 -
+ docs/man/grub-install.h2m         |   6 --
+ docs/man/grub-kbdcomp.h2m         |  10 ---
+ docs/man/grub-macbless.h2m        |   4 -
+ docs/man/grub-macho2img.h2m       |   4 -
+ docs/man/grub-menulst2cfg.h2m     |   4 -
+ docs/man/grub-mkconfig.h2m        |   4 -
+ docs/man/grub-mkfont.h2m          |   4 -
+ docs/man/grub-mkimage.h2m         |   6 --
+ docs/man/grub-mklayout.h2m        |  10 ---
+ docs/man/grub-mknetdir.h2m        |   4 -
+ docs/man/grub-mkpasswd-pbkdf2.h2m |   4 -
+ docs/man/grub-mkrelpath.h2m       |   4 -
+ docs/man/grub-mkrescue.h2m        |   4 -
+ docs/man/grub-mkstandalone.h2m    |   4 -
+ docs/man/grub-mount.h2m           |   2 -
+ docs/man/grub-ofpathname.h2m      |   4 -
+ docs/man/grub-pe2elf.h2m          |   4 -
+ docs/man/grub-probe.h2m           |   4 -
+ docs/man/grub-reboot.h2m          |   5 --
+ docs/man/grub-render-label.h2m    |   3 -
+ docs/man/grub-script-check.h2m    |   4 -
+ docs/man/grub-set-default.h2m     |   5 --
+ docs/man/grub-sparc64-setup.h2m   |   6 --
+ docs/man/grub-syslinux2cfg.h2m    |   4 -
+ gentpl.py                         |   5 +-
+ util/grub-bios-setup.8            |  54 +++++++++++++
+ util/grub-editenv.1               |  46 +++++++++++
+ util/grub-file.1                  | 165 ++++++++++++++++++++++++++++++++++++++
+ util/grub-fstest.1                |  99 +++++++++++++++++++++++
+ util/grub-glue-efi.1              |  31 +++++++
+ util/grub-install.8               | 128 +++++++++++++++++++++++++++++
+ util/grub-kbdcomp.1               |  19 +++++
+ util/grub-macbless.1              |  22 +++++
+ util/grub-menulst2cfg.1           |  12 +++
+ util/grub-mkconfig.8              |  17 ++++
+ util/grub-mkfont.1                |  87 ++++++++++++++++++++
+ util/grub-mkimage.1               |  95 ++++++++++++++++++++++
+ util/grub-mklayout.1              |  27 +++++++
+ util/grub-mknetdir.1              |  12 +++
+ util/grub-mkpasswd-pbkdf2.1       |  27 +++++++
+ util/grub-mkrelpath.1             |  12 +++
+ util/grub-mkrescue.1              | 123 ++++++++++++++++++++++++++++
+ util/grub-mkstandalone.1          | 100 +++++++++++++++++++++++
+ util/grub-ofpathname.8            |  12 +++
+ util/grub-probe.8                 |  80 ++++++++++++++++++
+ util/grub-reboot.8                |  21 +++++
+ util/grub-render-label.1          |  51 ++++++++++++
+ util/grub-script-check.1          |  21 +++++
+ util/grub-set-default.8           |  21 +++++
+ util/grub-sparc64-setup.8         |  12 +++
+ 59 files changed, 1318 insertions(+), 147 deletions(-)
+ delete mode 100644 docs/man/grub-bios-setup.h2m
+ delete mode 100644 docs/man/grub-editenv.h2m
+ delete mode 100644 docs/man/grub-emu.h2m
+ delete mode 100644 docs/man/grub-file.h2m
+ delete mode 100644 docs/man/grub-fstest.h2m
+ delete mode 100644 docs/man/grub-glue-efi.h2m
+ delete mode 100644 docs/man/grub-install.h2m
+ delete mode 100644 docs/man/grub-kbdcomp.h2m
+ delete mode 100644 docs/man/grub-macbless.h2m
+ delete mode 100644 docs/man/grub-macho2img.h2m
+ delete mode 100644 docs/man/grub-menulst2cfg.h2m
+ delete mode 100644 docs/man/grub-mkconfig.h2m
+ delete mode 100644 docs/man/grub-mkfont.h2m
+ delete mode 100644 docs/man/grub-mkimage.h2m
+ delete mode 100644 docs/man/grub-mklayout.h2m
+ delete mode 100644 docs/man/grub-mknetdir.h2m
+ delete mode 100644 docs/man/grub-mkpasswd-pbkdf2.h2m
+ delete mode 100644 docs/man/grub-mkrelpath.h2m
+ delete mode 100644 docs/man/grub-mkrescue.h2m
+ delete mode 100644 docs/man/grub-mkstandalone.h2m
+ delete mode 100644 docs/man/grub-mount.h2m
+ delete mode 100644 docs/man/grub-ofpathname.h2m
+ delete mode 100644 docs/man/grub-pe2elf.h2m
+ delete mode 100644 docs/man/grub-probe.h2m
+ delete mode 100644 docs/man/grub-reboot.h2m
+ delete mode 100644 docs/man/grub-render-label.h2m
+ delete mode 100644 docs/man/grub-script-check.h2m
+ delete mode 100644 docs/man/grub-set-default.h2m
+ delete mode 100644 docs/man/grub-sparc64-setup.h2m
+ delete mode 100644 docs/man/grub-syslinux2cfg.h2m
+ create mode 100644 util/grub-bios-setup.8
+ create mode 100644 util/grub-editenv.1
+ create mode 100644 util/grub-file.1
+ create mode 100644 util/grub-fstest.1
+ create mode 100644 util/grub-glue-efi.1
+ create mode 100644 util/grub-install.8
+ create mode 100644 util/grub-kbdcomp.1
+ create mode 100644 util/grub-macbless.1
+ create mode 100644 util/grub-menulst2cfg.1
+ create mode 100644 util/grub-mkconfig.8
+ create mode 100644 util/grub-mkfont.1
+ create mode 100644 util/grub-mkimage.1
+ create mode 100644 util/grub-mklayout.1
+ create mode 100644 util/grub-mknetdir.1
+ create mode 100644 util/grub-mkpasswd-pbkdf2.1
+ create mode 100644 util/grub-mkrelpath.1
+ create mode 100644 util/grub-mkrescue.1
+ create mode 100644 util/grub-mkstandalone.1
+ create mode 100644 util/grub-ofpathname.8
+ create mode 100644 util/grub-probe.8
+ create mode 100644 util/grub-reboot.8
+ create mode 100644 util/grub-render-label.1
+ create mode 100644 util/grub-script-check.1
+ create mode 100644 util/grub-set-default.8
+ create mode 100644 util/grub-sparc64-setup.8
+
+diff --git a/configure.ac b/configure.ac
+index 8331f95b645..bec8535af70 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -77,6 +77,29 @@ grub_TRANSFORM([grub-set-default])
+ grub_TRANSFORM([grub-sparc64-setup])
+ grub_TRANSFORM([grub-render-label])
+ grub_TRANSFORM([grub-file])
++grub_TRANSFORM([grub-bios-setup.3])
++grub_TRANSFORM([grub-editenv.1])
++grub_TRANSFORM([grub-fstest.3])
++grub_TRANSFORM([grub-glue-efi.3])
++grub_TRANSFORM([grub-install.1])
++grub_TRANSFORM([grub-kbdcomp.3])
++grub_TRANSFORM([grub-menulst2cfg.1])
++grub_TRANSFORM([grub-mkconfig.1])
++grub_TRANSFORM([grub-mkfont.3])
++grub_TRANSFORM([grub-mkimage.1])
++grub_TRANSFORM([grub-mklayout.3])
++grub_TRANSFORM([grub-mknetdir.3])
++grub_TRANSFORM([grub-mkpasswd-pbkdf2.3])
++grub_TRANSFORM([grub-mkrelpath.3])
++grub_TRANSFORM([grub-mkrescue.1])
++grub_TRANSFORM([grub-mkstandalone.3])
++grub_TRANSFORM([grub-ofpathname.3])
++grub_TRANSFORM([grub-probe.3])
++grub_TRANSFORM([grub-reboot.3])
++grub_TRANSFORM([grub-render-label.3])
++grub_TRANSFORM([grub-script-check.3])
++grub_TRANSFORM([grub-set-default.1])
++grub_TRANSFORM([grub-sparc64-setup.3])
+ 
+ # Optimization flag.  Allow user to override.
+ if test "x$TARGET_CFLAGS" = x; then
+diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist
+index 8f1485d52a5..b909f2c073a 100644
+--- a/conf/Makefile.extra-dist
++++ b/conf/Makefile.extra-dist
+@@ -11,7 +11,6 @@ EXTRA_DIST += unicode
+ EXTRA_DIST += util/import_gcry.py
+ EXTRA_DIST += util/import_unicode.py
+ 
+-EXTRA_DIST += docs/man
+ EXTRA_DIST += docs/autoiso.cfg
+ EXTRA_DIST += docs/grub.cfg
+ EXTRA_DIST += docs/osdetect.cfg
+diff --git a/docs/Makefile.am b/docs/Makefile.am
+index 93eb3962765..ab28f199694 100644
+--- a/docs/Makefile.am
++++ b/docs/Makefile.am
+@@ -5,5 +5,3 @@ info_TEXINFOS = grub.texi grub-dev.texi
+ grub_TEXINFOS = fdl.texi
+ 
+ EXTRA_DIST = font_char_metrics.png font_char_metrics.txt
+-
+-
+diff --git a/docs/man/grub-bios-setup.h2m b/docs/man/grub-bios-setup.h2m
+deleted file mode 100644
+index ac6ede36296..00000000000
+--- a/docs/man/grub-bios-setup.h2m
++++ /dev/null
+@@ -1,6 +0,0 @@
+-[NAME]
+-grub-bios-setup \- set up a device to boot using GRUB
+-[SEE ALSO]
+-.BR grub-install (8),
+-.BR grub-mkimage (1),
+-.BR grub-mkrescue (1)
+diff --git a/docs/man/grub-editenv.h2m b/docs/man/grub-editenv.h2m
+deleted file mode 100644
+index 3859d3d4c4f..00000000000
+--- a/docs/man/grub-editenv.h2m
++++ /dev/null
+@@ -1,5 +0,0 @@
+-[NAME]
+-grub-editenv \- edit GRUB environment block
+-[SEE ALSO]
+-.BR grub-reboot (8),
+-.BR grub-set-default (8)
+diff --git a/docs/man/grub-emu.h2m b/docs/man/grub-emu.h2m
+deleted file mode 100644
+index ef1c000656a..00000000000
+--- a/docs/man/grub-emu.h2m
++++ /dev/null
+@@ -1,6 +0,0 @@
+-[NAME]
+-grub-emu \- GRUB emulator
+-[SEE ALSO]
+-If you are trying to install GRUB, then you should use
+-.BR grub-install (8)
+-rather than this program.
+diff --git a/docs/man/grub-file.h2m b/docs/man/grub-file.h2m
+deleted file mode 100644
+index e09bb4d3101..00000000000
+--- a/docs/man/grub-file.h2m
++++ /dev/null
+@@ -1,2 +0,0 @@
+-[NAME]
+-grub-file \- check file type
+diff --git a/docs/man/grub-fstest.h2m b/docs/man/grub-fstest.h2m
+deleted file mode 100644
+index 9676b159afd..00000000000
+--- a/docs/man/grub-fstest.h2m
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[NAME]
+-grub-fstest \- debug tool for GRUB filesystem drivers
+-[SEE ALSO]
+-.BR grub-probe (8)
+diff --git a/docs/man/grub-glue-efi.h2m b/docs/man/grub-glue-efi.h2m
+deleted file mode 100644
+index c1c6ded49ff..00000000000
+--- a/docs/man/grub-glue-efi.h2m
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[NAME]
+-grub-glue-efi \- generate a fat binary for EFI
+-[DESCRIPTION]
+-grub-glue-efi processes ia32 and amd64 EFI images and glues them according to Apple format.
+diff --git a/docs/man/grub-install.h2m b/docs/man/grub-install.h2m
+deleted file mode 100644
+index 8cbbc87a0f2..00000000000
+--- a/docs/man/grub-install.h2m
++++ /dev/null
+@@ -1,6 +0,0 @@
+-[NAME]
+-grub-install \- install GRUB to a device
+-[SEE ALSO]
+-.BR grub-mkconfig (8),
+-.BR grub-mkimage (1),
+-.BR grub-mkrescue (1)
+diff --git a/docs/man/grub-kbdcomp.h2m b/docs/man/grub-kbdcomp.h2m
+deleted file mode 100644
+index d81f9157e01..00000000000
+--- a/docs/man/grub-kbdcomp.h2m
++++ /dev/null
+@@ -1,10 +0,0 @@
+-[NAME]
+-grub-kbdcomp \- generate a GRUB keyboard layout file
+-[DESCRIPTION]
+-grub-kbdcomp processes a X keyboard layout description in
+-.BR keymaps (5)
+-format into a format that can be used by GRUB's
+-.B keymap
+-command.
+-[SEE ALSO]
+-.BR grub-mklayout (8)
+diff --git a/docs/man/grub-macbless.h2m b/docs/man/grub-macbless.h2m
+deleted file mode 100644
+index 0197c0087d7..00000000000
+--- a/docs/man/grub-macbless.h2m
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[NAME]
+-grub-macbless \- bless a mac file/directory
+-[SEE ALSO]
+-.BR grub-install (1)
+diff --git a/docs/man/grub-macho2img.h2m b/docs/man/grub-macho2img.h2m
+deleted file mode 100644
+index d79aaeed8f9..00000000000
+--- a/docs/man/grub-macho2img.h2m
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[NAME]
+-grub-macho2img \- convert Mach-O to raw image
+-[SEE ALSO]
+-.BR grub-mkimage (1)
+diff --git a/docs/man/grub-menulst2cfg.h2m b/docs/man/grub-menulst2cfg.h2m
+deleted file mode 100644
+index c2e0055ed7e..00000000000
+--- a/docs/man/grub-menulst2cfg.h2m
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[NAME]
+-grub-menulst2cfg \- transform legacy menu.lst into grub.cfg
+-[SEE ALSO]
+-.BR grub-mkconfig (8)
+diff --git a/docs/man/grub-mkconfig.h2m b/docs/man/grub-mkconfig.h2m
+deleted file mode 100644
+index 9b42f813010..00000000000
+--- a/docs/man/grub-mkconfig.h2m
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[NAME]
+-grub-mkconfig \- generate a GRUB configuration file
+-[SEE ALSO]
+-.BR grub-install (8)
+diff --git a/docs/man/grub-mkfont.h2m b/docs/man/grub-mkfont.h2m
+deleted file mode 100644
+index d46fe600eca..00000000000
+--- a/docs/man/grub-mkfont.h2m
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[NAME]
+-grub-mkfont \- make GRUB font files
+-[SEE ALSO]
+-.BR grub-mkconfig (8)
+diff --git a/docs/man/grub-mkimage.h2m b/docs/man/grub-mkimage.h2m
+deleted file mode 100644
+index f0fbc2bb197..00000000000
+--- a/docs/man/grub-mkimage.h2m
++++ /dev/null
+@@ -1,6 +0,0 @@
+-[NAME]
+-grub-mkimage \- make a bootable image of GRUB
+-[SEE ALSO]
+-.BR grub-install (8),
+-.BR grub-mkrescue (1),
+-.BR grub-mknetdir (8)
+diff --git a/docs/man/grub-mklayout.h2m b/docs/man/grub-mklayout.h2m
+deleted file mode 100644
+index 1e43409c0ab..00000000000
+--- a/docs/man/grub-mklayout.h2m
++++ /dev/null
+@@ -1,10 +0,0 @@
+-[NAME]
+-grub-mklayout \- generate a GRUB keyboard layout file
+-[DESCRIPTION]
+-grub-mklayout processes a keyboard layout description in
+-.BR keymaps (5)
+-format into a format that can be used by GRUB's
+-.B keymap
+-command.
+-[SEE ALSO]
+-.BR grub-mkconfig (8)
+diff --git a/docs/man/grub-mknetdir.h2m b/docs/man/grub-mknetdir.h2m
+deleted file mode 100644
+index a2ef13ec111..00000000000
+--- a/docs/man/grub-mknetdir.h2m
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[NAME]
+-grub-mknetdir \- prepare a GRUB netboot directory.
+-[SEE ALSO]
+-.BR grub-mkimage (1)
+diff --git a/docs/man/grub-mkpasswd-pbkdf2.h2m b/docs/man/grub-mkpasswd-pbkdf2.h2m
+deleted file mode 100644
+index 4d202f3da7e..00000000000
+--- a/docs/man/grub-mkpasswd-pbkdf2.h2m
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[NAME]
+-grub-mkpasswd-pbkdf2 \- generate hashed password for GRUB
+-[SEE ALSO]
+-.BR grub-mkconfig (8)
+diff --git a/docs/man/grub-mkrelpath.h2m b/docs/man/grub-mkrelpath.h2m
+deleted file mode 100644
+index d01f3961e3f..00000000000
+--- a/docs/man/grub-mkrelpath.h2m
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[NAME]
+-grub-mkrelpath \- make a system path relative to its root
+-[SEE ALSO]
+-.BR grub-probe (8)
+diff --git a/docs/man/grub-mkrescue.h2m b/docs/man/grub-mkrescue.h2m
+deleted file mode 100644
+index a427f02e3c6..00000000000
+--- a/docs/man/grub-mkrescue.h2m
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[NAME]
+-grub-mkrescue \- make a GRUB rescue image
+-[SEE ALSO]
+-.BR grub-mkimage (1)
+diff --git a/docs/man/grub-mkstandalone.h2m b/docs/man/grub-mkstandalone.h2m
+deleted file mode 100644
+index c77313978ad..00000000000
+--- a/docs/man/grub-mkstandalone.h2m
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[NAME]
+-grub-mkstandalone \- make a memdisk-based GRUB image
+-[SEE ALSO]
+-.BR grub-mkimage (1)
+diff --git a/docs/man/grub-mount.h2m b/docs/man/grub-mount.h2m
+deleted file mode 100644
+index 8d168982d72..00000000000
+--- a/docs/man/grub-mount.h2m
++++ /dev/null
+@@ -1,2 +0,0 @@
+-[NAME]
+-grub-mount \- export GRUB filesystem with FUSE
+diff --git a/docs/man/grub-ofpathname.h2m b/docs/man/grub-ofpathname.h2m
+deleted file mode 100644
+index 74b43eea039..00000000000
+--- a/docs/man/grub-ofpathname.h2m
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[NAME]
+-grub-ofpathname \- find OpenBOOT path for a device
+-[SEE ALSO]
+-.BR grub-probe (8)
+diff --git a/docs/man/grub-pe2elf.h2m b/docs/man/grub-pe2elf.h2m
+deleted file mode 100644
+index 7ca29bd703c..00000000000
+--- a/docs/man/grub-pe2elf.h2m
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[NAME]
+-grub-pe2elf \- convert PE image to ELF
+-[SEE ALSO]
+-.BR grub-mkimage (1)
+diff --git a/docs/man/grub-probe.h2m b/docs/man/grub-probe.h2m
+deleted file mode 100644
+index 6e1ffdcf937..00000000000
+--- a/docs/man/grub-probe.h2m
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[NAME]
+-grub-probe \- probe device information for GRUB
+-[SEE ALSO]
+-.BR grub-fstest (1)
+diff --git a/docs/man/grub-reboot.h2m b/docs/man/grub-reboot.h2m
+deleted file mode 100644
+index e4acace65ce..00000000000
+--- a/docs/man/grub-reboot.h2m
++++ /dev/null
+@@ -1,5 +0,0 @@
+-[NAME]
+-grub-reboot \- set the default boot entry for GRUB, for the next boot only
+-[SEE ALSO]
+-.BR grub-set-default (8),
+-.BR grub-editenv (1)
+diff --git a/docs/man/grub-render-label.h2m b/docs/man/grub-render-label.h2m
+deleted file mode 100644
+index 50ae5247c05..00000000000
+--- a/docs/man/grub-render-label.h2m
++++ /dev/null
+@@ -1,3 +0,0 @@
+-[NAME]
+-grub-render-label \- generate a .disk_label for Apple Macs.
+-
+diff --git a/docs/man/grub-script-check.h2m b/docs/man/grub-script-check.h2m
+deleted file mode 100644
+index 3653682671a..00000000000
+--- a/docs/man/grub-script-check.h2m
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[NAME]
+-grub-script-check \- check grub.cfg for syntax errors
+-[SEE ALSO]
+-.BR grub-mkconfig (8)
+diff --git a/docs/man/grub-set-default.h2m b/docs/man/grub-set-default.h2m
+deleted file mode 100644
+index 7945001c154..00000000000
+--- a/docs/man/grub-set-default.h2m
++++ /dev/null
+@@ -1,5 +0,0 @@
+-[NAME]
+-grub-set-default \- set the saved default boot entry for GRUB
+-[SEE ALSO]
+-.BR grub-reboot (8),
+-.BR grub-editenv (1)
+diff --git a/docs/man/grub-sparc64-setup.h2m b/docs/man/grub-sparc64-setup.h2m
+deleted file mode 100644
+index 18f803a50db..00000000000
+--- a/docs/man/grub-sparc64-setup.h2m
++++ /dev/null
+@@ -1,6 +0,0 @@
+-[NAME]
+-grub-sparc64-setup \- set up a device to boot using GRUB
+-[SEE ALSO]
+-.BR grub-install (8),
+-.BR grub-mkimage (1),
+-.BR grub-mkrescue (1)
+diff --git a/docs/man/grub-syslinux2cfg.h2m b/docs/man/grub-syslinux2cfg.h2m
+deleted file mode 100644
+index ad25c8ab753..00000000000
+--- a/docs/man/grub-syslinux2cfg.h2m
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[NAME]
+-grub-syslinux2cfg \- transform syslinux config into grub.cfg
+-[SEE ALSO]
+-.BR grub-menulst2cfg (8)
+diff --git a/gentpl.py b/gentpl.py
+index c86550d4f9e..2cba0bbbd6f 100644
+--- a/gentpl.py
++++ b/gentpl.py
+@@ -805,10 +805,7 @@ def manpage(defn, adddeps):
+ 
+     output("if COND_MAN_PAGES\n")
+     gvar_add("man_MANS", name + "." + mansection)
+-    rule(name + "." + mansection, name + " " + adddeps, """
+-chmod a+x """ + name + """
+-PATH=$(builddir):$$PATH pkgdatadir=$(builddir) $(HELP2MAN) --section=""" + mansection + """ -i $(top_srcdir)/docs/man/""" + name + """.h2m -o $@ """ + name + """
+-""")
++    rule(name + "." + mansection, name + " " + adddeps, "cat $(top_srcdir)/util/" + name + "." + mansection + " | $(top_builddir)/config.status --file=$@:-")
+     gvar_add("CLEANFILES", name + "." + mansection)
+     output("endif\n")
+ 
+diff --git a/util/grub-bios-setup.8 b/util/grub-bios-setup.8
+new file mode 100644
+index 00000000000..56f582b3d75
+--- /dev/null
++++ b/util/grub-bios-setup.8
+@@ -0,0 +1,54 @@
++.TH GRUB-BIOS-SETUP 3 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-bios-setup\fR \(em Set up images to boot from a device.
++
++.SH SYNOPSIS
++\fBgrub-bios-setup\fR [-a | --allow-floppy] [-b | --boot-image=\fIFILE\fR]
++.RS 17
++[-c | --core-image=\fIFILE\fR] [-d | --directory=\fIDIR\fR]
++.RE
++.RS 17
++[-f | --force] [-m | --device-map=\fIFILE\fR]
++.RE
++.RS 17
++[-s | --skip-fs-probe] [-v | --verbose] \fIDEVICE\fR
++
++.SH DESCRIPTION
++You should not normally run this program directly.  Use grub-install instead.
++
++.SH OPTIONS
++.TP
++\fB--allow-floppy\fR
++Make the device also bootable as a floppy.  This option is the default for
++/dev/fdX devices.  Some BIOSes will not boot images created with this option.
++
++.TP
++\fB--boot-image\fR=\fIFILE\fR
++Use FILE as the boot image.  The default value is \fBboot.img\fR.
++
++.TP
++\fB--core-image\fR=\fIFILE\fR
++Use FILE as ther core image.  The default value is \fBcore.img\fR.
++
++.TP
++\fB--directory\fR=\fIDIR\fR
++Use GRUB files in the directory DIR.  The default value is \fB/boot/grub\fR.
++
++.TP
++\fB--force\fR
++Install even if problems are detected.
++
++.TP
++\fB--device-map\fR=\fIFILE\fR
++Use FILE as the device map.  The default value is /boot/grub/device.map .
++
++.TP
++\fB--skip-fs-probe\fR
++Do not probe DEVICE for filesystems.
++
++.TP
++\fB--verbose\fR
++Print verbose messages.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-editenv.1 b/util/grub-editenv.1
+new file mode 100644
+index 00000000000..d28ba03ba42
+--- /dev/null
++++ b/util/grub-editenv.1
+@@ -0,0 +1,46 @@
++.TH GRUB-EDITENV 1 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-editenv\fR \(em Manage the GRUB environment block.
++
++.SH SYNOPSIS
++\fBgrub-editenv\fR [-v | --verbose] [\fIFILE\fR]
++.RS 14
++<create | list | set \fINAME\fR=\fIVALUE\fR | unset \fINAME\fR>
++
++.SH DESCRIPTION
++\fBgrub-editenv\fR is a command line tool to manage GRUB's stored environment.
++
++.SH OPTIONS
++.TP
++\fB--verbose\fR
++Print verbose messages.
++
++.TP
++\fBFILE\fR
++.RS 7
++File name to use for grub environment.  Default is /boot/grub/grubenv .
++.RE
++
++.SH COMMANDS
++.TP
++\fBcreate\fR
++.RS 7
++Create a blank environment block file.
++.RE
++
++.TP
++\fBlist\fR
++.RS 7
++List the current variables.
++.RE
++
++.TP
++\fBset\fR [\fINAME\fR=\fIVALUE\fR ...]
++Set variables.
++
++.TP
++\fBunset [\fINAME\fR ...]
++Delete variables.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-file.1 b/util/grub-file.1
+new file mode 100644
+index 00000000000..b29cb327889
+--- /dev/null
++++ b/util/grub-file.1
+@@ -0,0 +1,165 @@
++.TH GRUB-FILE 1 "Web Feb 26 2014"
++.SH NAME
++\fBgrub-file\fR \(em Check if FILE is of specified type.
++
++.SH SYNOPSIS
++\fBgrub-file\fR (--is-i386-xen-pae-domu | --is-x86_64-xen-domu |
++.RS 11
++--is-x86-xen-dom0 | --is-x86-multiboot |
++.RE
++.RS 11
++--is-x86-multiboot2 | --is-arm-linux | --is-arm64-linux |
++.RE
++.RS 11
++--is-ia64-linux | --is-mips-linux | --is-mipsel-linux |
++.RE
++.RS 11
++--is-sparc64-linux | --is-powerpc-linux | --is-x86-linux |
++.RE
++.RS 11
++--is-x86-linux32 | --is-x86-kfreebsd | --is-i386-kfreebsd |
++.RE
++.RS 11
++--is-x86_64-kfreebsd | --is-x86-knetbsd |
++.RE
++.RS 11
++--is-i386-knetbsd | --is-x86_64-knetbsd | --is-i386-efi |
++.RE
++.RS 11
++--is-x86_64-efi | --is-ia64-efi | --is-arm64-efi |
++.RE
++.RS 11
++--is-arm-efi | --is-hibernated-hiberfil | --is-x86_64-xnu |
++.RE
++.RS 11
++--is-i386-xnu | --is-xnu-hibr | --is-x86-bios-bootsector)
++.RE
++.RS 11
++\fIFILE\fR
++
++.SH DESCRIPTION
++\fBgrub-file\fR is used to check if \fIFILE\fR is of a specified type.
++
++.SH OPTIONS
++.TP
++--is-i386-xen-pae-domu
++Check if FILE can be booted as i386 PAE Xen unprivileged guest kernel
++
++.TP
++--is-x86_64-xen-domu
++Check if FILE can be booted as x86_64 Xen unprivileged guest kernel
++
++.TP
++--is-x86-xen-dom0
++Check if FILE can be used as Xen x86 privileged guest kernel
++
++.TP
++--is-x86-multiboot
++Check if FILE can be used as x86 multiboot kernel
++
++.TP
++--is-x86-multiboot2
++Check if FILE can be used as x86 multiboot2 kernel
++
++.TP
++--is-arm-linux
++Check if FILE is ARM Linux
++
++.TP
++--is-arm64-linux
++Check if FILE is ARM64 Linux
++
++.TP
++--is-ia64-linux
++Check if FILE is IA64 Linux
++
++.TP
++--is-mips-linux
++Check if FILE is MIPS Linux
++
++.TP
++--is-mipsel-linux
++Check if FILE is MIPSEL Linux
++
++.TP
++--is-sparc64-linux
++Check if FILE is SPARC64 Linux
++
++.TP
++--is-powerpc-linux
++Check if FILE is POWERPC Linux
++
++.TP
++--is-x86-linux
++Check if FILE is x86 Linux
++
++.TP
++--is-x86-linux32
++Check if FILE is x86 Linux supporting 32-bit protocol
++
++.TP
++--is-x86-kfreebsd
++Check if FILE is x86 kFreeBSD
++
++.TP
++--is-i386-kfreebsd
++Check if FILE is i386 kFreeBSD
++
++.TP
++--is-x86_64-kfreebsd
++Check if FILE is x86_64 kFreeBSD
++
++.TP
++--is-x86-knetbsd
++Check if FILE is x86 kNetBSD
++
++.TP
++--is-i386-knetbsd
++Check if FILE is i386 kNetBSD
++
++.TP
++--is-x86_64-knetbsd
++Check if FILE is x86_64 kNetBSD
++
++.TP
++--is-i386-efi
++Check if FILE is i386 EFI file
++
++.TP
++--is-x86_64-efi
++Check if FILE is x86_64 EFI file
++
++.TP
++--is-ia64-efi
++Check if FILE is IA64 EFI file
++
++.TP
++--is-arm64-efi
++Check if FILE is ARM64 EFI file
++
++.TP
++--is-arm-efi
++Check if FILE is ARM EFI file
++
++.TP
++--is-hibernated-hiberfil
++Check if FILE is hiberfil.sys in hibernated state
++
++.TP
++--is-x86_64-xnu
++Check if FILE is x86_64 XNU (Mac OS X kernel)
++
++.TP
++--is-i386-xnu
++Check if FILE is i386 XNU (Mac OS X kernel)
++
++.TP
++--is-xnu-hibr
++Check if FILE is XNU (Mac OS X kernel) hibernated image
++
++.TP
++--is-x86-bios-bootsector
++Check if FILE is BIOS bootsector
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-fstest.1 b/util/grub-fstest.1
+new file mode 100644
+index 00000000000..792fa78634c
+--- /dev/null
++++ b/util/grub-fstest.1
+@@ -0,0 +1,99 @@
++.TH GRUB-FSTEST 3 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-fstest\fR — Debug tool for GRUB's filesystem driver.
++
++.SH SYNOPSIS
++\fBgrub-fstest\fR [-c | --diskcount=\fINUM\fR] [-C | --crypto]
++.RS 13
++[-d | --debug=\fISTRING\fR] [-K | --zfs-key=\fIFILE\fR|\fIprompt\fR]
++.RE
++.RS 13
++[-n | --length=\fINUM\fR] [-r | --root=\fIDEVICE_NAME\fR]
++.RE
++.RS 13
++[-s | --skip=\fINUM\fR] [-u | --uncompress] [-v | --verbose]
++.RE
++.RS 13
++\fIIMAGE_PATH\fR <blocklist \fIFILE\fR | cat \fIFILE\fR |
++.RE
++.RS 13
++cmp \fIFILE\fR \fILOCAL\fR | cp \fIFILE\fR \fILOCAL\fR | crc \fIFILE\fR |
++.RE
++.RS 13
++hex \fIFILE\fR | ls \fIPATH\fR | xnu_uuid \fIDEVICE\fR>
++
++.SH DESCRIPTION
++\fBgrub-fstest\fR is a tool for testing GRUB's filesystem drivers.  You should not normally need to run this program.
++
++.SH OPTIONS
++.TP
++\fB--diskcount\fR=\fINUM\fR
++Specify the number of input files.
++
++.TP
++\fB--crypto\fR
++Mount cryptographic devices.
++
++.TP
++\fB--debug\fR=\fISTRING\fR
++Set debug environment variable.
++
++.TP
++\fB--zfs-key\fR=\fIFILE\fR|\fIprompt\fR
++Load ZFS cryptographic key.
++
++.TP
++\fB--length\fR=\fINUM\fR
++Handle NUM bytes in output file.
++
++.TP
++\fB--root\fR=\fIDEVICE_NAME\fR
++Set root device.
++
++.TP
++\fB--skip\fR=\fINUM\fR
++Skip NUM bytes from output file.
++
++.TP
++\fB--uncompress\fR
++Uncompress data.
++
++.TP
++\fB--verbose\fR
++Print verbose messages.
++
++.SH COMMANDS
++.TP
++\fBblocklist\fR \fIFILE\fR
++Display block list of \fIFILE\fR.
++
++.TP
++\fBcat\fR \fIFILE\fR
++Display \fIFILE\fR on standard output.
++
++.TP
++\fBcmp\fR \fIFILE\fR \fILOCAL\fR
++Compare \fIFILE\fR with local file \fILOCAL\fR.
++
++.TP
++\fBcp\fR \fIFILE\fR \fILOCAL\fR
++Copy \fIFILE\fR to local file \fILOCAL\fR.
++
++.TP
++\fBcrc\fR \fIFILE\fR
++Display the CRC-32 checksum of \fIFILE\fR.
++
++.TP
++\fBhex\fR \fIFILE\fR
++Display contents of \fIFILE\fR in hexidecimal.
++
++.TP
++\fBls\fR \fIPATH\fR
++List files at \fIPATH\fR.
++
++.TP
++\fBxnu_uuid\fR \fIDEVICE\fR
++Display the XNU UUID of \fIDEVICE\fR.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-glue-efi.1 b/util/grub-glue-efi.1
+new file mode 100644
+index 00000000000..72bd555d577
+--- /dev/null
++++ b/util/grub-glue-efi.1
+@@ -0,0 +1,31 @@
++.TH GRUB-GLUE-EFI 3 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-glue-efi\fR \(em Create an Apple fat EFI binary.
++
++.SH SYNOPSIS
++\fBgrub-glue-efi\fR <-3 | --input32=\fIFILE\fR> <-6 | --input64=\fIFILE\fR>
++.RS 15
++<-o | --output=\fIFILE\fR> [-v | --verbose]
++
++.SH DESCRIPTION
++\fBgrub-glue-efi\fR creates an Apple fat EFI binary from two EFI binaries.
++
++.SH OPTIONS
++.TP
++\fB--input32\fR=\fIFILE\fR
++Read 32-bit binary from \fIFILE\fR.
++
++.TP
++\fB--input64\fR=\fIFILE\fR
++Read 64-bit binary from \fIFILE\fR.
++
++.TP
++\fB--output\fR=\fIFILE\fR
++Write resulting fat binary to \fIFILE\fR.
++
++.TP
++\fB--verbose\fR
++Print verbose messages.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-install.8 b/util/grub-install.8
+new file mode 100644
+index 00000000000..1db89e94b3b
+--- /dev/null
++++ b/util/grub-install.8
+@@ -0,0 +1,128 @@
++.TH GRUB-INSTALL 1 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-install\fR \(em Install GRUB on a device.
++
++.SH SYNOPSIS
++\fBgrub-install\fR [--modules=\fIMODULES\fR] [--install-modules=\fIMODULES\fR]
++.RS 14
++[--themes=\fITHEMES\fR] [--fonts=\fIFONTS\fR] [--locales=\fILOCALES\fR]
++.RE
++.RS 14
++[--compress[=\fIno\fR,\fIxz\fR,\fIgz\fR,\fIlzo\fR]] [-d | --directory=\fIDIR\fR]
++.RE
++.RS 14
++[--grub-mkimage=\fIFILE\fR] [--boot-directory=\fIDIR\fR]
++.RE
++.RS 14
++[--target=\fITARGET\fR] [--grub-setup=\fIFILE\fR]
++.RE
++.RS 14
++[--grub-mkrelpath=\fIFILE\fR] [--grub-probe=\fIFILE\fR]
++.RE
++.RS 14
++[--allow-floppy] [--recheck] [--force] [--force-file-id]
++.RE
++.RS 14
++[--disk-module=\fIMODULE\fR] [--no-nvram] [--removable]
++.RE
++.RS 14
++[--bootloader-id=\fIID\fR] [--efi-directory=\fIDIR\fR] \fIINSTALL_DEVICE\fR
++
++.SH DESCRIPTION
++\fBgrub-install\fR installs GRUB onto a device.  This includes copying GRUB images into the target directory (generally \fI/boot/grub\fR), and on some platforms may also include installing GRUB onto a boot sector.
++
++.SH OPTIONS
++.TP
++\fB--modules\fR=\fIMODULES\fR\!
++Pre-load modules specified by \fIMODULES\fR.
++
++.TP
++\fB--install-modules\fR=\fIMODULES\fR
++Install only \fIMODULES\fR and their dependencies.  The default is to install all available modules.
++
++.TP
++\fB--themes\fR=\fITHEMES\fR
++Install \fITHEMES\fR.  The default is to install the \fIstarfield\fR theme, if available.
++
++.TP
++\fB--fonts\fR=\fIFONTS\fR
++Install \fIFONTS\fR.  The default is to install the \fIunicode\fR font.
++
++.TP
++\fB--locales\fR=\fILOCALES\fR
++Install only locales listed in \fILOCALES\fR.  The default is to install all available locales.
++
++.TP
++\fB--compress\fR=\fIno\fR,\fIxz\fR,\fIgz\fR,\fIlzo\fR
++Compress GRUB files using the specified compression algorithm.
++
++.TP
++\fB--directory\fR=\fIDIR\fR
++Use images and modules in \fIDIR\fR.
++
++.TP
++\fB--grub-mkimage\fR=\fIFILE\fR
++Use \fIFILE\fR as \fBgrub-mkimage\fR.  The default is \fI/usr/bin/grub-mkimage\fR.
++
++.TP
++\fB--boot-directory\fR=\fIDIR\fR
++Use \fIDIR\fR as the boot directory.  The default is \fI/boot\fR.  GRUB will put its files in a subdirectory of this directory named \fIgrub\fR.
++
++.TP
++\fB--target\fR=\fITARGET\fR
++Install GRUB for \fITARGET\fR platform.  The default is the platform \fBgrub-install\fR is running on.
++
++.TP
++\fB--grub-setup\fR=\fIFILE\fR
++Use \fIFILE\fR as \fBgrub-setup\fR.  The default is \fI/usr/bin/grub-setup\fR.
++
++.TP
++\fB--grub-mkrelpath\fR=\fIFILE\fR
++Use \fIFILE\fR as \fBgrub-mkrelpath\fR.  The default is \fI/usr/bin/grub-mkrelpath\fR.
++
++.TP
++\fB--grub-probe\fR=\fIFILE\fR
++Use \fIFILE\fR as \fBgrub-probe\fR.  The default is \fI/usr/bin/grub-mkrelpath\fR.
++
++.TP
++\fB--allow-floppy
++Make the device also bootable as a floppy.  This option is the default for /dev/fdX devices. Some BIOSes will not boot images created with this option.
++
++.TP
++\fB--recheck
++Delete any existing device map and create a new one if necessary.
++
++.TP
++\fB--force
++Install even if problems are detected.
++
++.TP
++\fB--force-file-id
++Use identifier file even if UUID is available.
++
++.TP
++\fB--disk-module\fR=\fIMODULE\fR
++Use \fIMODULE\fR for disk access.  This allows you to manually specify either \fIbiosdisk\fR or \fInative\fR disk access.  This option is only available on the BIOS target platform.
++
++.TP
++\fB--no-nvram
++Do not update the \fIboot-device\fR NVRAM variable.  This option is only available on IEEE1275 target platforms.
++
++.TP
++\fB--removable
++Treat the target device as if it is removeable.  This option is only available on the EFI target platform.
++
++.TP
++\fB--bootloader-id\fR=\fIID\fR
++Use \fIID\fR as the bootloader ID.  This option is only available on the EFI target platform.
++
++.TP
++\fB--efi-directory\fR=\fIDIR\fR
++Use \fIDIR\fR as the EFI System Partition root.  This option is only available on the EFI target platform.
++
++.TP
++\fIINSTALL_DEVICE\fR
++Install GRUB to the block device \fIINSTALL_DEVICE\fR.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-kbdcomp.1 b/util/grub-kbdcomp.1
+new file mode 100644
+index 00000000000..0bb969a5b43
+--- /dev/null
++++ b/util/grub-kbdcomp.1
+@@ -0,0 +1,19 @@
++.TH GRUB-KBDCOMP 3 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-kbdcomp\fR \(em Generate a GRUB keyboard layout file.
++
++.SH SYNOPSIS
++\fBgrub-kbdcomp\fR <-o | --output=\fIFILE\fR> \fICKBMAP_ARGUMENTS\fR
++
++.SH DESCRIPTION
++\fBgrub-kbdcomp\fR processes an X keyboard layout description in
++\fBkeymaps\fR(5) format into a format that can be used by GRUB's \fBkeymap\fR
++command.
++
++.SH OPTIONS
++.TP
++\fB--output\fR=\fIFILE\fR
++Write output to \fIFILE\fR.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-macbless.1 b/util/grub-macbless.1
+new file mode 100644
+index 00000000000..41a96186f70
+--- /dev/null
++++ b/util/grub-macbless.1
+@@ -0,0 +1,22 @@
++.TH GRUB-MACBLESS 1 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-macbless\fR \(em Mac-style bless on HFS or HFS+
++
++.SH SYNOPSIS
++\fBgrub-macbless\fR [-v | --verbose] [-p | --ppc] \fIFILE\fR | [-x | --x86] \fIFILE\fR
++
++.SH OPTIONS
++.TP
++--x86
++Bless for x86 based Macs.
++
++.TP
++--ppc
++Bless for PPC based Macs.
++
++.TP
++--verbose
++Print verbose messages.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-menulst2cfg.1 b/util/grub-menulst2cfg.1
+new file mode 100644
+index 00000000000..91e2ef87113
+--- /dev/null
++++ b/util/grub-menulst2cfg.1
+@@ -0,0 +1,12 @@
++.TH GRUB-MENULST2CFG 1 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-menulst2cfg\fR \(em Convert a configuration file from GRUB 0.xx to GRUB 2.xx format.
++
++.SH SYNOPSIS
++\fBgrub-menulst2cfg\fR [\fIINFILE\fR [\fIOUTFILE\fR]]
++
++.SH DESCRIPTION
++\fBgrub-menulst2cfg\fR converts a configuration file from GRUB 0.xx to the current format.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-mkconfig.8 b/util/grub-mkconfig.8
+new file mode 100644
+index 00000000000..a2d1f577b9b
+--- /dev/null
++++ b/util/grub-mkconfig.8
+@@ -0,0 +1,17 @@
++.TH GRUB-MKCONFIG 1 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-mkconfig\fR \(em Generate a GRUB configuration file.
++
++.SH SYNOPSIS
++\fBgrub-mkconfig\fR [-o | --output=\fIFILE\fR]
++
++.SH DESCRIPTION
++\fBgrub-mkconfig\fR generates a configuration file for GRUB.
++
++.SH OPTIONS
++.TP
++\fB--output\fR=\fIFILE\fR
++Write generated output to \fIFILE\fR.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-mkfont.1 b/util/grub-mkfont.1
+new file mode 100644
+index 00000000000..3494857987d
+--- /dev/null
++++ b/util/grub-mkfont.1
+@@ -0,0 +1,87 @@
++.TH GRUB-MKFONT 3 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-mkfont\fR \(em Convert common font file formats into the PF2 format.
++
++.SH SYNOPSIS
++\fBgrub-mkfont\fR [--ascii-bitmaps] [-a | --force-autohint]
++.RS 13
++[-b | --bold] [-c | --asce=\fINUM\fR] [-d | --desc=\fINUM\fR]
++.RE
++.RS 13
++[-i | --index=\fINUM\fR] [-n | --name=\fINAME\fR] [--no-bitmap]
++.RE
++.RS 13
++[--no-hinting] <-o | --output=\fIFILE\fR>
++.RE
++.RS 13
++[-r | --range=\fIFROM-TO\fR[\fI,FROM-TO\fR]] [-s | --size=\fISIZE\fR]
++.RE
++.RS 13
++[-v | --verbose] [--width-spec] \fIFONT_FILES\fR
++
++.SH DESCRIPTION
++\fBgrub-mkfont\fR converts font files from common formats into the PF2 format used by GRUB.
++
++.SH OPTIONS
++.TP
++--ascii-bitmaps
++Save only bitmaps for ASCII characters.
++
++.TP
++--force-autohint
++Force generation of automatic hinting.
++
++.TP
++--bold
++Convert font to bold.
++
++.TP
++--asce=\fINUM\fR
++Set font ascent to \fINUM\fR.
++
++.TP
++--desc=\fINUM\fR
++Set font descent to \fINUM\fR.
++
++.TP
++--index=\fINUM\fR
++Select face index \fINUM\fR.
++
++.TP
++--name=\fINAME\fR
++Set font family to \fINAME\fR.
++
++.TP
++--no-bitmap
++Ignore bitmap strikes when loading.
++
++.TP
++--no-hinting
++Disable hinting.
++
++.TP
++--output=\fIFILE\fR
++Save ouptut to \fIFILE\fR.  This argument is required.
++
++.TP
++--range=\fIFROM-TO\fR\fI,FROM-TO\fR
++Set the font ranges to each pair of \fIFROM\fR,\fITO\fR.
++
++.TP
++--size=\fISIZE\fR
++Set font size to \fISIZE\fR.
++
++.TP
++--verbose
++Print verbose messages.
++
++.TP
++--width-spec
++Create a width summary file.
++
++.TP
++\fIFONT_FILES\fR
++The input files to be converted.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-mkimage.1 b/util/grub-mkimage.1
+new file mode 100644
+index 00000000000..4dea4f54597
+--- /dev/null
++++ b/util/grub-mkimage.1
+@@ -0,0 +1,95 @@
++.TH GRUB-MKIMAGE 1 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-mkimage\fR \(em Make a bootable GRUB image.
++
++.SH SYNOPSIS
++\fBgrub-mkimage\fR [-c | --config=\fRFILE\fI] [-C | --compression=(\fIxz\fR,\fInone\fR,\fIauto\fR)]
++.RS 14
++[-d | --directory=\fRDIR\fR] [-k | --pubkey=\fIFILE\fR]
++.RE
++.RS 14
++[-m | --memdisk=\fIFILE\fR] [-n | --note] [-o | --output=\fIFILE\fR]
++.RE
++.RS 14
++[-O | --format=\fIFORMAT\fR] [-p | --prefix=\fIDIR\fR]
++.RE
++.RS 14
++[-v | --verbose] \fIMODULES\fR
++
++.SH DESCRIPTION
++\fBgrub-mkimage\fI builds a bootable image of GRUB.
++
++.SH OPTIONS
++.TP
++--config=\fIFILE\fR
++Embed \fIFILE\fR as the image's initial configuration file.
++
++.TP
++--compression=(\fIxz\fR,\fInone\fR,\fIauto\fR)
++Use one of \fIxz\fR, \fInone\fR, or \fIauto\fR as the compression method for the core image.
++
++.TP
++--directory=\fIDIR\fR
++Use images and modules from \fIDIR\fR.  The default value is \fB/usr/lib/grub/<platform>\fR.
++
++.TP
++--pubkey=\fIFILE\fR
++Embed the public key \fIFILE\fR for signature checking.
++
++.TP
++--memdisk=\fIFILE\fR
++Embed the memdisk image \fIFILE\fR.  If no \fB-p\fR option is also specified, this implies \fI-p (memdisk)/boot/grub\fR.
++
++.TP
++--note
++Add a CHRP \fINOTE\fR section.  This option is only valid on IEEE1275 platforms.
++
++.TP
++--output=\fIFILE\fR
++Write the generated file to \fIFILE\fR.  The default is to write to standard output.
++
++.TP
++--format=\fIFORMAT\fR
++Generate an image in the specified \fIFORMAT\fR.  Valid values are:
++.RS
++.RS 4
++.P
++i386-coreboot,
++i386-multiboot,
++i386-pc,
++i386-pc-pxe,
++i386-efi,
++i386-ieee1275,
++i386-qemu,
++x86_64-efi,
++mipsel-yeeloong-flash,
++mipsel-fuloong2f-flash,
++mipself-loongson-elf,
++powerpc-ieee1275,
++sparc64-ieee1275-raw,
++sparc64-ieee1275-cdcore,
++sparc64-ieee1275-aout,
++ia64-efi,
++mips-arc,
++mipsel-arc,
++mipsel-qemu_mips-elf,
++mips-qemu_mips-flash,
++mipsel-qemu_mips-flash,
++mips-qemu_mips-elf
++.RE
++.RE
++
++.TP
++--prefix=\fIDIR\fR
++Set prefix directory.  The default value is \fI/boot/grub\fR.
++
++.TP
++--verbose
++Print verbose messages.
++
++.TP
++\fIMODULES\fR
++Include \fIMODULES\fR.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-mklayout.1 b/util/grub-mklayout.1
+new file mode 100644
+index 00000000000..d1bbc2ec515
+--- /dev/null
++++ b/util/grub-mklayout.1
+@@ -0,0 +1,27 @@
++.TH GRUB-MKLAYOUT 3 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-mklayout\fR \(em Generate a GRUB keyboard layout file.
++
++.SH SYNOPSIS
++\fBgrub-mklayout\fR [-i | --input=\fIFILE\fR] [-o | --output=\fIFILE\fR]
++.RS 15
++[-v | --verbose]
++
++.SH DESCRIPTION
++\fBgrub-mklayout\fR generates a GRUB keyboard layout description which corresponds with the Linux console layout description given as input.
++
++.SH OPTIONS
++.TP
++--input=\fIFILE\fR
++Use \fIFILE\fR as the input.  The default value is the standard input device.
++
++.TP
++--output=\fIFILE\fR
++Use \fIFILE\fR as the output.  The default value is the standard output device.
++
++.TP
++--verbose
++Print verbose messages.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-mknetdir.1 b/util/grub-mknetdir.1
+new file mode 100644
+index 00000000000..fa7e8d4ef0d
+--- /dev/null
++++ b/util/grub-mknetdir.1
+@@ -0,0 +1,12 @@
++.TH GRUB-MKNETDIR 3 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-mknetdir\fR \(em Prepare a GRUB netboot directory.
++
++.SH SYNOPSIS
++\fBgrub-mknetdir\fR
++
++.SH DESCRIPTION
++\fBgrub-mknetdir\fR prepares a directory for GRUB to be netbooted from.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-mkpasswd-pbkdf2.1 b/util/grub-mkpasswd-pbkdf2.1
+new file mode 100644
+index 00000000000..73c437c15d8
+--- /dev/null
++++ b/util/grub-mkpasswd-pbkdf2.1
+@@ -0,0 +1,27 @@
++.TH GRUB-MKPASSWD-PBKDF2 3 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-mkpasswd-pbkdf2\fR \(em Generate a PBKDF2 password hash.
++
++.SH SYNOPSIS
++\fBgrub-mkpasswd-pbkdf2\fR [-c | --iteration-count=\fINUM\fR] [-l | --buflen=\fINUM\fR]
++.RS 22
++[-s | --salt=\fINUM\fR]
++
++.SH DESCRIPTION
++\fBgrub-mkpasswd-pbkdf2\fR generates a PBKDF2 password string suitable for use in a GRUB configuration file.
++
++.SH OPTIONS
++.TP
++--iteration-count=\fINUM\fR
++Number of PBKDF2 iterations.
++
++.TP
++--buflen=\fINUM\fR
++Length of generated hash.
++
++.TP
++--salt=\fINUM\fR
++Length of salt to use.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-mkrelpath.1 b/util/grub-mkrelpath.1
+new file mode 100644
+index 00000000000..85f1113621d
+--- /dev/null
++++ b/util/grub-mkrelpath.1
+@@ -0,0 +1,12 @@
++.TH GRUB-MKRELPATH 3 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-mkrelpath\fR \(em Generate a relative GRUB path given an OS path.
++
++.SH SYNOPSIS
++\fBgrub-mkrelpath\fR \fIFILE\fR
++
++.SH DESCRIPTION
++\fBgrub-mkrelpath\fR takes an OS filesystem path for \fIFILE\fR and returns a relative path suitable for use in a GRUB configuration file.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-mkrescue.1 b/util/grub-mkrescue.1
+new file mode 100644
+index 00000000000..4ed9fc723fd
+--- /dev/null
++++ b/util/grub-mkrescue.1
+@@ -0,0 +1,123 @@
++.TH GRUB-MKRESCUE 3 "Wed Feb 26 2014"
++.SH NAME
++grub-mkrescue \(em Generate a GRUB rescue image using GNU Xorriso.
++
++.SH SYNOPSIS
++\fBgrub-mkrescue\fR [-o | --output=\fIFILE\fR] [--modules=\fIMODULES\fR]
++.RS 15
++[--install-modules=\fIMODULES\fR] [--themes=\fITHEMES\fR]
++.RE
++.RS 15
++[--fonts=\fIFONTS\fR] [--locales=\fILOCALES\fR]
++.RE
++.RS 15
++[--compress[=\fIno\fR,\fIxz\fR,\fIgz\fR,\fIlzo\fR]] [-d | --directory=\fIDIR\fR]
++.RE
++.RS 15
++[--grub-mkimage=\fIFILE\fR] [--rom-directory=\fIDIR\fR]
++.RE
++.RS 15
++[--xorriso=\fIFILE\fR] [--grub-glue-efi=\fIFILE\fR]
++.RE
++.RS 15
++[--grub-render-label=\fIFILE\fR] [--label-font=\fIFILE\fR]
++.RE
++.RS 15
++[--label-color=\fICOLOR\fR] [--label-bgcolor=\fIFILE\fR]
++.RE
++.RS 15
++[--product-name=\fISTRING\fR] [--product-version=\fISTRING\fR]
++.RE
++.RS 15
++[--sparc-boot] [--arcs-boot]
++
++.SH DESCRIPTION
++\fBgrub-mkrescue\fR can be used to generate a rescue image with the GRUB bootloader.
++
++.SH OPTIONS
++.TP
++\fB--output\fR=\fIFILE\fR
++Write the generated file to \fIFILE\fR.  The default is to write to standard output.
++
++.TP
++\fB--modules\fR=\fIMODULES\fR
++Pre-load modules specified by \fIMODULES\fR.
++
++.TP
++\fB--install-modules\fR=\fIMODULES\fR
++Install only \fIMODULES\fR and their dependencies.  The default is to install all available modules.
++
++.TP
++\fB--themes\fR=\fITHEMES\fR
++Install \fITHEMES\fR.  The default is to install the \fIstarfield\fR theme, if available.
++
++.TP
++\fB--fonts\fR=\fIFONTS\fR
++Install \fIFONTS\fR.  The default is to install the \fIunicode\fR font.
++
++.TP
++\fB--locales\fR=\fILOCALES\fR
++Install only locales listed in \fILOCALES\fR.  The default is to install all available locales.
++
++.TP
++\fB--compress\fR[=\fIno\fR,\fIxz\fR,\fIgz\fR,\fIlzo\fR]
++Compress GRUB files using the specified compression algorithm.
++
++.TP
++\fB--directory\fR=\fIDIR\fR
++Use images and modules in \fIDIR\fR.
++
++.TP
++\fB--grub-mkimage\fR=\fIFILE\fR
++Use \fIFILE\fR as \fBgrub-mkimage\fR(1).  The default is \fI/usr/bin/grub-mkimage\fR.
++
++.TP
++\fB--rom-directory\fR=\fIDIR\fR
++Save ROM images in \fIDIR\fR.
++
++.TP
++\fB--xorriso\fR=\fIFILE\fR
++Use \fIFILE\fR as \fBxorriso\fI.
++
++.TP
++\fB--grub-glue-efi\fR=\fIFILE\fR
++Use \fIFILE\fR as \fBgrub-glue-efi\fR(3).
++
++.TP
++\fB--grub-render-label\fR=\fIFILE\fR
++Use \fIFILE\fR as \fBgrub-render-label\fR(3).
++
++.TP
++\fB--label-font\fR=\fIFILE\fR
++Use \fIFILE\fR as the font file for generated labels.
++
++.TP
++\fB--label-color\fR=\fICOLOR\fR
++Use \fICOLOR\fI as the color for generated labels.
++
++.TP
++\fB--label-bgcolor\fR=\fICOLOR\fR
++Use \fICOLOR\fR as the background color for generated labels.
++
++.TP
++\fB--product-name\fR=\fISTRING\fR
++Use \fISTRING\fR as the product name in generated labels.
++
++.TP
++\fB--product-version\fR=\fISTRING\fR
++Use \fISTRING\fR as the product version in generated labels.
++
++.TP
++\fB--sparc-boot\fR
++Enable booting the SPARC platform.  This disables HFS+, APM, ARCS, and "boot as disk image" on the \fIi386-pc\fR target platform.
++
++.TP
++\fB--arcs-boot\fR
++Enable ARCS booting.  This is typically for big-endian MIPS machines, and disables HFS+, APM, sparc64, and "boot as disk image" on the \fIi386-pc\fR target platform.
++
++.TP
++\fB--\fR
++All options after a \fB--\fR will be passed directly to xorriso's command line when generating the image.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-mkstandalone.1 b/util/grub-mkstandalone.1
+new file mode 100644
+index 00000000000..ba2d2bdf279
+--- /dev/null
++++ b/util/grub-mkstandalone.1
+@@ -0,0 +1,100 @@
++.TH GRUB-MKSTANDALONE 3 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-mkstandalone\fR \(em Generate a standalone image in the selected format.
++
++.SH SYNOPSIS
++\fBgrub-mkstandalone\fR [-o | --output=\fIFILE\fR] [-O | --format=\fIFORMAT\fR]
++.RS 19
++[-C | --compression=(\fIxz\fR|\fInone\fR|\fIauto\fR)]
++.RE
++.RS 19
++[--modules=\fIMODULES\fR] [--install-modules=\fIMODULES\fR]
++.RE
++.RS 19
++[--themes=\fITHEMES\fR] [--fonts=\fIFONTS\fR]
++.RE
++.RS 19
++[--locales=\fILOCALES\fR] [--compress[=\fIno\fR,\fIxz\fR,\fIgz\fR,\fIlzo\fR]]
++.RE
++.RS 19
++[-d | --directory=\fIDIR\fR] [--grub-mkimage=\fIFILE\fR]
++.RE
++.RS 19
++\fISOURCE...\fR
++
++.SH DESCRIPTION
++
++.SH OPTIONS
++.TP
++--output=\fIFILE\fR
++Write the generated file to \fIFILE\fR.  The default is to write to standard output.
++
++.TP
++--format=\fIFORMAT\fR
++Generate an image in the specified \fIFORMAT\fR.  Valid values are:
++.RS
++.RS 4
++.P
++i386-coreboot,
++i386-multiboot,
++i386-pc,
++i386-pc-pxe,
++i386-efi,
++i386-ieee1275,
++i386-qemu,
++x86_64-efi,
++mipsel-yeeloong-flash,
++mipsel-fuloong2f-flash,
++mipself-loongson-elf,
++powerpc-ieee1275,
++sparc64-ieee1275-raw,
++sparc64-ieee1275-cdcore,
++sparc64-ieee1275-aout,
++ia64-efi,
++mips-arc,
++mipsel-arc,
++mipsel-qemu_mips-elf,
++mips-qemu_mips-flash,
++mipsel-qemu_mips-flash,
++mips-qemu_mips-elf
++.RE
++.RE
++
++.TP
++--compression=(\fIxz\fR|\fInone\fR|\fIauto\fR)
++Use one of \fIxz\fR, \fInone\fR, or \fIauto\fR as the compression method for the core image.
++
++.TP
++--modules=\fIMODULES\fR
++Pre-load modules specified by \fIMODULES\fR.
++
++.TP
++--install-modules=\fIMODULES\fR
++Install only \fIMODULES\fR and their dependencies.  The default is to install all available modules.
++
++.TP
++--themes=\fITHEMES\fR
++Install \fITHEMES\fR.  The default is to install the \fIstarfield\fR theme, if available.
++
++.TP
++--fonts=\fIFONTS\fR
++Install \fIFONTS\fR.  The default is to install the \fIunicode\fR font.
++
++.TP
++--locales=\fILOCALES\fR
++Install only locales listed in \fILOCALES\fR.  The default is to install all available locales.
++
++.TP
++--compress[=\fIno\fR,\fIxz\fR,\fIgz\fR,\fIlzo\fR]
++Compress GRUB files using the specified compression algorithm.
++
++.TP
++--directory=\fIDIR\fR
++Use images and modules in \fIDIR\fR.
++
++.TP
++--grub-mkimage=\fIFILE\fR
++Use \fIFILE\fR as \fBgrub-mkimage\fR.  The default is \fI/usr/bin/grub-mkimage\fR.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-ofpathname.8 b/util/grub-ofpathname.8
+new file mode 100644
+index 00000000000..bf3743aeba1
+--- /dev/null
++++ b/util/grub-ofpathname.8
+@@ -0,0 +1,12 @@
++.TH GRUB-OFPATHNAME 3 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-ofpathname\fR \(em Generate an IEEE-1275 device path for a specified device.
++
++.SH SYNOPSIS
++\fBgrub-ofpathname\fR \fIDEVICE\fR
++
++.SH DESCRIPTION
++\fBgrub-ofpathname\fR generates an IEEE-1275 device path for the specified \fIDEVICE\fR.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-probe.8 b/util/grub-probe.8
+new file mode 100644
+index 00000000000..04e26c832bb
+--- /dev/null
++++ b/util/grub-probe.8
+@@ -0,0 +1,80 @@
++.TH GRUB-PROBE 3 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-probe\fR \(em Probe device information for a given path.
++
++.SH SYNOPSIS
++\fBgrub-probe\fR \[-d | --device] [-m | --device-map=\fIFILE\fR]
++.RS 12
++[-t | --target=(fs|fs_uuid|fs_label|drive|device|partmap|
++.RE
++.RS 28
++abstraction|cryptodisk_uuid|
++.RE
++.RS 28
++msdos_parttype)]
++.RE
++.RS 12
++[-v | --verbose] (PATH|DEVICE)
++
++.SH DESCRIPTION
++\fBgrub-probe\fR probes a path or device for filesystem and related information.
++
++.SH OPTIONS
++.TP
++--device
++Final option represents a \fIDEVICE\fR, rather than a filesystem \fIPATH\fR.
++.TP
++--device-map=\fIFILE\fR
++Use \fIFILE\fR as the device map.  The default value is \fI/boot/grub/device.map\fR.
++
++.TP
++--target=(fs|fs_uuid|fs_label|drive|device|partmap|msdos_parttype)
++Select among various output definitions.  The default is \fIfs\fR.
++.RS
++.TP
++\fIfs\fR
++filesystem module
++
++.TP
++\fIfs_uuid\fR
++filesystem UUID
++
++.TP
++\fIfs_label\fR
++filesystem label
++
++.TP
++\fIdrive\fR
++GRUB drive name
++
++.TP
++\fIdevice\fR
++System device
++
++.TP
++\fIpartmap\fR
++partition map module
++
++.TP
++\fIabstraction\fR
++abstraction module
++
++.TP
++\fIcryptodisk_uuid\fR
++cryptographic container
++
++.TP
++\fImsdos_partmap\fR
++MS-DOS partition map
++.RE
++
++.TP
++--verbose
++Print verbose output.
++
++.TP
++(\fIPATH\fR|\fIDEVICE\fR)
++If --device is passed, a block \fIDEVICE\fR.  Otherwise, the \fIPATH\fR of a file on the filesystem.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-reboot.8 b/util/grub-reboot.8
+new file mode 100644
+index 00000000000..faa5e4eece2
+--- /dev/null
++++ b/util/grub-reboot.8
+@@ -0,0 +1,21 @@
++.TH GRUB-REBOOT 3 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-reboot\fR \(em Set the default boot menu entry for the next boot only.
++
++.SH SYNOPSIS
++\fBgrub-reboot\fR [--boot-directory=\fIDIR\fR] \fIMENU_ENTRY\fR
++
++.SH DESCRIPTION
++\fBgrub-reboot\fR sets the default boot menu entry for the next boot, but not further boots after that.  This command only works for GRUB configuration files created with \fIGRUB_DEFAULT=saved\fR in \fI/etc/default/grub\fR.
++
++.SH OPTIONS
++.TP
++--boot-directory=\fIDIR\fR
++Find GRUB images under \fIDIR/grub\fR.  The default value is \fI/boot\fR, resulting in grub images being search for at \fI/boot/grub\fR.
++
++.TP
++\fIMENU_ENTRY\fR
++A number, a menu item title or a menu item identifier.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-render-label.1 b/util/grub-render-label.1
+new file mode 100644
+index 00000000000..4d51c8abf01
+--- /dev/null
++++ b/util/grub-render-label.1
+@@ -0,0 +1,51 @@
++.TH GRUB-RENDER-LABEL 3 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-render-label\fR \(em Render an Apple disk label.
++
++.SH SYNOPSIS
++\fBgrub-render-label\fR [-b | --bgcolor=\fICOLOR\fR] [-c | --color=\fICOLOR\fR]
++.RS 19
++[-f | --font=\fIFILE\fR] [-i | --input=\fIFILE\fR]
++.RE
++.RS 19
++[-o | --output=\fIFILE\fR] [-t | --text=\fISTRING\fR]
++.RE
++.RS 19
++[-v | --verbose]
++
++.SH DESCRIPTION
++\fBgrub-render-label\fR renders an Apple disk label (.disk_label) file.
++
++
++.SH OPTIONS
++.TP
++\fB--color\fR=\fICOLOR\fR
++Use \fICOLOR\fI as the color for generated labels.
++
++.TP
++\fB--bgcolor\fR=\fICOLOR\fR
++Use \fICOLOR\fR as the background color for generated labels.
++
++.TP
++\fB--font\fR=\fIFILE\fR
++Use \fIFILE\fR as the font file for generated labels.
++
++.TP
++--input=\fIFILE\fR
++Read input text from \fIFILE\fR.
++
++.TP
++--output=\fIFILE\fR
++Render output to \fIFILE\fR.
++
++.TP
++--text=\fISTRING\fR
++Use \fISTRING\fR as input text.
++
++.TP
++--verbose
++Print verbose output.
++
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-script-check.1 b/util/grub-script-check.1
+new file mode 100644
+index 00000000000..0f1f625b05d
+--- /dev/null
++++ b/util/grub-script-check.1
+@@ -0,0 +1,21 @@
++.TH GRUB-SCRIPT-CHECK 3 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-script-check\fR \(em Check GRUB configuration file for syntax errors.
++
++.SH SYNOPSIS
++\fBgrub-script-check\fR [-v | --verbose] \fIPATH\fR
++
++.SH DESCRIPTION
++\fBgrub-script-check\fR verifies that a specified GRUB configuration file does not contain syntax errors.
++
++.SH OPTIONS
++.TP
++--verbose
++Print verbose output.
++
++.TP
++\fIPATH\fR
++Path of the file to use as input.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-set-default.8 b/util/grub-set-default.8
+new file mode 100644
+index 00000000000..a96265a1509
+--- /dev/null
++++ b/util/grub-set-default.8
+@@ -0,0 +1,21 @@
++.TH GRUB-SET-DEFAULT 1 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-set-default\fR \(em Set the default boot menu entry for GRUB.
++
++.SH SYNOPSIS
++\fBgrub-set-default\fR [--boot-directory=\fIDIR\fR] \fIMENU_ENTRY\fR
++
++.SH DESCRIPTION
++\fBgrub-set-default\fR sets the default boot menu entry for all subsequent boots.  This command only works for GRUB configuration files created with \fIGRUB_DEFAULT=saved\fR in \fI/etc/default/grub\fR.
++
++.SH OPTIONS
++.TP
++--boot-directory=\fIDIR\fR
++Find GRUB images under \fIDIR/grub\fR.  The default value is \fI/boot\fR, resulting in grub images being search for at \fI/boot/grub\fR.
++
++.TP
++\fIMENU_ENTRY\fR
++A number, a menu item title or a menu item identifier.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-sparc64-setup.8 b/util/grub-sparc64-setup.8
+new file mode 100644
+index 00000000000..37ea2dd5eaa
+--- /dev/null
++++ b/util/grub-sparc64-setup.8
+@@ -0,0 +1,12 @@
++.TH GRUB-SPARC64-SETUP 3 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-sparc64-setup\fR \(em Set up a device to boot a sparc64 GRUB image.
++
++.SH SYNOPSIS
++\fBgrub-sparc64-setup\fR [OPTIONS].
++
++.SH DESCRIPTION
++You should not normally run this program directly.  Use grub-install instead.
++
++.SH SEE ALSO
++.BR "info grub"
diff --git a/SOURCES/0041-use-fw_path-prefix-when-fallback-searching-for-grub-.patch b/SOURCES/0041-use-fw_path-prefix-when-fallback-searching-for-grub-.patch
new file mode 100644
index 0000000..985205c
--- /dev/null
+++ b/SOURCES/0041-use-fw_path-prefix-when-fallback-searching-for-grub-.patch
@@ -0,0 +1,41 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fedora Ninjas <grub2-owner@fedoraproject.org>
+Date: Wed, 19 Feb 2014 15:58:43 -0500
+Subject: [PATCH] use fw_path prefix when fallback searching for grub config
+
+When PXE booting via UEFI firmware, grub was searching for grub.cfg
+in the fw_path directory where the grub application was found. If
+that didn't exist, a fallback search would look for config file names
+based on MAC and IP address. However, the search would look in the
+prefix directory which may not be the same fw_path. This patch
+changes that behavior to use the fw_path directory for the fallback
+search. Only if fw_path is NULL will the prefix directory be searched.
+
+Signed-off-by: Mark Salter <msalter@redhat.com>
+---
+ grub-core/normal/main.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
+index ec1cd257397..d85f7598d23 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -349,7 +349,7 @@ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)),
+       char *config;
+       const char *prefix, *fw_path;
+ 
+-      fw_path = grub_env_get ("fw_path");
++      prefix = fw_path = grub_env_get ("fw_path");
+       if (fw_path)
+ 	{
+ 	  config = grub_xasprintf ("%s/grub.cfg", fw_path);
+@@ -372,7 +372,8 @@ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)),
+ 	    }
+ 	}
+ 
+-      prefix = grub_env_get ("prefix");
++      if (! prefix)
++	      prefix = grub_env_get ("prefix");
+       if (prefix)
+         {
+           grub_size_t config_len;
diff --git a/SOURCES/0042-Try-mac-guid-etc-before-grub.cfg-on-tftp-config-file.patch b/SOURCES/0042-Try-mac-guid-etc-before-grub.cfg-on-tftp-config-file.patch
new file mode 100644
index 0000000..61dd5ca
--- /dev/null
+++ b/SOURCES/0042-Try-mac-guid-etc-before-grub.cfg-on-tftp-config-file.patch
@@ -0,0 +1,127 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 8 Jul 2019 17:33:22 +0200
+Subject: [PATCH] Try mac/guid/etc before grub.cfg on tftp config files.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/normal/main.c | 97 ++++++++++++++++++++++++++-----------------------
+ 1 file changed, 51 insertions(+), 46 deletions(-)
+
+diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
+index d85f7598d23..1e509fceb91 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -347,61 +347,66 @@ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)),
+       /* Guess the config filename. It is necessary to make CONFIG static,
+ 	 so that it won't get broken by longjmp.  */
+       char *config;
+-      const char *prefix, *fw_path;
+-
+-      prefix = fw_path = grub_env_get ("fw_path");
+-      if (fw_path)
+-	{
+-	  config = grub_xasprintf ("%s/grub.cfg", fw_path);
+-	  if (config)
+-	    {
+-	      grub_file_t file;
+-
+-	      file = grub_file_open (config, GRUB_FILE_TYPE_CONFIG);
+-	      if (file)
+-		{
+-		  grub_file_close (file);
+-		  grub_enter_normal_mode (config);
+-		}
+-              else
+-                {
+-                  /*  Ignore all errors.  */
+-                  grub_errno = 0;
+-                }
+-	      grub_free (config);
+-	    }
+-	}
++      const char *prefix;
++      const char *net_search_cfg;
++      int disable_net_search = 0;
+ 
++      prefix = grub_env_get ("fw_path");
+       if (! prefix)
+ 	      prefix = grub_env_get ("prefix");
++
++      net_search_cfg = grub_env_get ("feature_net_search_cfg");
++      if (net_search_cfg && net_search_cfg[0] == 'n')
++	      disable_net_search = 1;
++
+       if (prefix)
+         {
+-          grub_size_t config_len;
+-          int disable_net_search = 0;
+-          const char *net_search_cfg;
+-
+-          config_len = grub_strlen (prefix) +
+-                       sizeof ("/grub.cfg-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX");
+-          config = grub_malloc (config_len);
+-
+-          if (!config)
+-            goto quit;
+-
+-          grub_snprintf (config, config_len, "%s/grub.cfg", prefix);
+-
+-          net_search_cfg = grub_env_get ("feature_net_search_cfg");
+-          if (net_search_cfg && net_search_cfg[0] == 'n')
+-            disable_net_search = 1;
+-
+           if (grub_strncmp (prefix + 1, "tftp", sizeof ("tftp") - 1) == 0 &&
+               !disable_net_search)
+-            grub_net_search_config_file (config);
++            {
++              grub_size_t config_len;
++              config_len = grub_strlen (prefix) +
++                sizeof ("/grub.cfg-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX");
++              config = grub_malloc (config_len);
+ 
+-	  grub_enter_normal_mode (config);
+-	  grub_free (config);
+-	}
++              if (! config)
++                goto quit;
++
++              grub_snprintf (config, config_len, "%s/grub.cfg", prefix);
++
++              grub_net_search_configfile (config);
++
++              grub_enter_normal_mode (config);
++              grub_free (config);
++              config = NULL;
++            }
++
++          if (!config)
++            {
++              config = grub_xasprintf ("%s/grub.cfg", prefix);
++              if (config)
++                {
++                  grub_file_t file;
++
++                  file = grub_file_open (config, GRUB_FILE_TYPE_CONFIG);
++                  if (file)
++                    {
++                      grub_file_close (file);
++                      grub_enter_normal_mode (config);
++                    }
++                  else
++                    {
++                      /*  Ignore all errors.  */
++                      grub_errno = 0;
++                    }
++                  grub_free (config);
++                }
++            }
++        }
+       else
+-	grub_enter_normal_mode (0);
++        {
++          grub_enter_normal_mode (0);
++        }
+     }
+   else
+     grub_enter_normal_mode (argv[0]);
diff --git a/SOURCES/0043-Generate-OS-and-CLASS-in-10_linux-from-etc-os-releas.patch b/SOURCES/0043-Generate-OS-and-CLASS-in-10_linux-from-etc-os-releas.patch
new file mode 100644
index 0000000..ef0b0f4
--- /dev/null
+++ b/SOURCES/0043-Generate-OS-and-CLASS-in-10_linux-from-etc-os-releas.patch
@@ -0,0 +1,29 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 4 Sep 2014 14:23:23 -0400
+Subject: [PATCH] Generate OS and CLASS in 10_linux from /etc/os-release
+
+This makes us use pretty names in the titles we generate in
+grub2-mkconfig when GRUB_DISTRIBUTOR isn't set.
+
+Resolves: rhbz#996794
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ util/grub.d/10_linux.in | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
+index 635d2fe0cd3..fed73271478 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -29,7 +29,8 @@ export TEXTDOMAINDIR="@localedir@"
+ CLASS="--class gnu-linux --class gnu --class os --unrestricted"
+ 
+ if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then
+-  OS="$(sed 's, release .*$,,g' /etc/system-release)"
++  OS="$(eval $(grep PRETTY_NAME /etc/os-release) ; echo ${PRETTY_NAME})"
++  CLASS="--class $(eval $(grep '^ID_LIKE=\|^ID=' /etc/os-release) ; [ -n "${ID_LIKE}" ] && echo ${ID_LIKE} || echo ${ID}) ${CLASS}"
+ else
+   OS="${GRUB_DISTRIBUTOR}"
+   CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr 'A-Z' 'a-z' | cut -d' ' -f1|LC_ALL=C sed 's,[^[:alnum:]_],_,g') ${CLASS}"
diff --git a/SOURCES/0044-Minimize-the-sort-ordering-for-.debug-and-rescue-ker.patch b/SOURCES/0044-Minimize-the-sort-ordering-for-.debug-and-rescue-ker.patch
new file mode 100644
index 0000000..3e2d3ae
--- /dev/null
+++ b/SOURCES/0044-Minimize-the-sort-ordering-for-.debug-and-rescue-ker.patch
@@ -0,0 +1,30 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 4 Sep 2014 15:52:08 -0400
+Subject: [PATCH] Minimize the sort ordering for .debug and -rescue- kernels.
+
+Resolves: rhbz#1065360
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ util/grub-mkconfig_lib.in | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in
+index 301d1ac229a..0f6505bf3b6 100644
+--- a/util/grub-mkconfig_lib.in
++++ b/util/grub-mkconfig_lib.in
+@@ -253,6 +253,14 @@ version_test_gt ()
+     *.old:*.old) ;;
+     *.old:*) version_test_gt_a="`echo "$version_test_gt_a" | sed -e 's/\.old$//'`" ; version_test_gt_cmp=gt ;;
+     *:*.old) version_test_gt_b="`echo "$version_test_gt_b" | sed -e 's/\.old$//'`" ; version_test_gt_cmp=ge ;;
++    *-rescue*:*-rescue*) ;;
++    *?debug:*?debug) ;;
++    *-rescue*:*?debug) return 1 ;;
++    *?debug:*-rescue*) return 0 ;;
++    *-rescue*:*) return 1 ;;
++    *:*-rescue*) return 0 ;;
++    *?debug:*) return 1 ;;
++    *:*?debug) return 0 ;;
+   esac
+   version_test_numeric "$version_test_gt_a" "$version_test_gt_cmp" "$version_test_gt_b"
+   return "$?"
diff --git a/SOURCES/0045-Try-prefix-if-fw_path-doesn-t-work.patch b/SOURCES/0045-Try-prefix-if-fw_path-doesn-t-work.patch
new file mode 100644
index 0000000..59489bd
--- /dev/null
+++ b/SOURCES/0045-Try-prefix-if-fw_path-doesn-t-work.patch
@@ -0,0 +1,222 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 9 Jul 2019 10:35:16 +0200
+Subject: [PATCH] Try $prefix if $fw_path doesn't work.
+
+Related: rhbz#1148652
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/kern/ieee1275/init.c |  28 +++++----
+ grub-core/net/net.c            |   2 +-
+ grub-core/normal/main.c        | 134 ++++++++++++++++++++---------------------
+ 3 files changed, 82 insertions(+), 82 deletions(-)
+
+diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
+index e71d1584164..0cd2a627231 100644
+--- a/grub-core/kern/ieee1275/init.c
++++ b/grub-core/kern/ieee1275/init.c
+@@ -127,23 +127,25 @@ grub_machine_get_bootlocation (char **device, char **path)
+       grub_free (canon);
+     }
+   else
+-    *device = grub_ieee1275_encode_devname (bootpath);
+-  grub_free (type);
+-
+-  filename = grub_ieee1275_get_filename (bootpath);
+-  if (filename)
+     {
+-      char *lastslash = grub_strrchr (filename, '\\');
+-
+-      /* Truncate at last directory.  */
+-      if (lastslash)
++      filename = grub_ieee1275_get_filename (bootpath);
++      if (filename)
+         {
+-	  *lastslash = '\0';
+-	  grub_translate_ieee1275_path (filename);
++          char *lastslash = grub_strrchr (filename, '\\');
+ 
+-	  *path = filename;
+-	}
++          /* Truncate at last directory.  */
++          if (lastslash)
++            {
++              *lastslash = '\0';
++              grub_translate_ieee1275_path (filename);
++
++              *path = filename;
++            }
++        }
++      *device = grub_ieee1275_encode_devname (bootpath);
+     }
++
++  grub_free (type);
+   grub_free (bootpath);
+ }
+ 
+diff --git a/grub-core/net/net.c b/grub-core/net/net.c
+index 4d3eb5c1a52..0ef148f4adc 100644
+--- a/grub-core/net/net.c
++++ b/grub-core/net/net.c
+@@ -1869,7 +1869,7 @@ grub_net_search_config_file (char *config)
+   /* Remove the remaining minus sign at the end. */
+   config[config_len] = '\0';
+ 
+-  return GRUB_ERR_NONE;
++  return GRUB_ERR_FILE_NOT_FOUND;
+ }
+ 
+ static struct grub_preboot *fini_hnd;
+diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
+index 1e509fceb91..d5968797f4f 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -337,81 +337,79 @@ grub_enter_normal_mode (const char *config)
+   grub_boot_time ("Exiting normal mode");
+ }
+ 
++static grub_err_t
++grub_try_normal (const char *variable)
++{
++    char *config;
++    const char *prefix;
++    grub_err_t err = GRUB_ERR_FILE_NOT_FOUND;
++    const char *net_search_cfg;
++    int disable_net_search = 0;
++
++    prefix = grub_env_get (variable);
++    if (!prefix)
++      return GRUB_ERR_FILE_NOT_FOUND;
++
++    net_search_cfg = grub_env_get ("feature_net_search_cfg");
++    if (net_search_cfg && net_search_cfg[0] == 'n')
++      disable_net_search = 1;
++
++    if (grub_strncmp (prefix + 1, "tftp", sizeof ("tftp") - 1) == 0 &&
++        !disable_net_search)
++      {
++       grub_size_t config_len;
++       config_len = grub_strlen (prefix) +
++         sizeof ("/grub.cfg-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX");
++       config = grub_malloc (config_len);
++
++       if (! config)
++         return GRUB_ERR_FILE_NOT_FOUND;
++
++       grub_snprintf (config, config_len, "%s/grub.cfg", prefix);
++       err = grub_net_search_config_file (config);
++      }
++
++    if (err != GRUB_ERR_NONE)
++      {
++       config = grub_xasprintf ("%s/grub.cfg", prefix);
++       if (config)
++         {
++           grub_file_t file;
++           file = grub_file_open (config, GRUB_FILE_TYPE_CONFIG);
++           if (file)
++             {
++               grub_file_close (file);
++               err = GRUB_ERR_NONE;
++             }
++         }
++      }
++
++    if (err == GRUB_ERR_NONE)
++      grub_enter_normal_mode (config);
++
++    grub_errno = 0;
++    grub_free (config);
++    return err;
++}
++
+ /* Enter normal mode from rescue mode.  */
+ static grub_err_t
+ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)),
+ 		 int argc, char *argv[])
+ {
+-  if (argc == 0)
+-    {
+-      /* Guess the config filename. It is necessary to make CONFIG static,
+-	 so that it won't get broken by longjmp.  */
+-      char *config;
+-      const char *prefix;
+-      const char *net_search_cfg;
+-      int disable_net_search = 0;
+-
+-      prefix = grub_env_get ("fw_path");
+-      if (! prefix)
+-	      prefix = grub_env_get ("prefix");
+-
+-      net_search_cfg = grub_env_get ("feature_net_search_cfg");
+-      if (net_search_cfg && net_search_cfg[0] == 'n')
+-	      disable_net_search = 1;
+-
+-      if (prefix)
+-        {
+-          if (grub_strncmp (prefix + 1, "tftp", sizeof ("tftp") - 1) == 0 &&
+-              !disable_net_search)
+-            {
+-              grub_size_t config_len;
+-              config_len = grub_strlen (prefix) +
+-                sizeof ("/grub.cfg-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX");
+-              config = grub_malloc (config_len);
+-
+-              if (! config)
+-                goto quit;
+-
+-              grub_snprintf (config, config_len, "%s/grub.cfg", prefix);
+-
+-              grub_net_search_configfile (config);
+-
+-              grub_enter_normal_mode (config);
+-              grub_free (config);
+-              config = NULL;
+-            }
+-
+-          if (!config)
+-            {
+-              config = grub_xasprintf ("%s/grub.cfg", prefix);
+-              if (config)
+-                {
+-                  grub_file_t file;
+-
+-                  file = grub_file_open (config, GRUB_FILE_TYPE_CONFIG);
+-                  if (file)
+-                    {
+-                      grub_file_close (file);
+-                      grub_enter_normal_mode (config);
+-                    }
+-                  else
+-                    {
+-                      /*  Ignore all errors.  */
+-                      grub_errno = 0;
+-                    }
+-                  grub_free (config);
+-                }
+-            }
+-        }
+-      else
+-        {
+-          grub_enter_normal_mode (0);
+-        }
+-    }
+-  else
++  if (argc)
+     grub_enter_normal_mode (argv[0]);
++  else
++    {
++      /* Guess the config filename. */
++      grub_err_t err;
++      err = grub_try_normal ("fw_path");
++      if (err == GRUB_ERR_FILE_NOT_FOUND)
++        err = grub_try_normal ("prefix");
++      if (err == GRUB_ERR_FILE_NOT_FOUND)
++        grub_enter_normal_mode (0);
++    }
+ 
+-quit:
+   return 0;
+ }
+ 
diff --git a/SOURCES/0046-Use-Distribution-Package-Sort-for-grub2-mkconfig-112.patch b/SOURCES/0046-Use-Distribution-Package-Sort-for-grub2-mkconfig-112.patch
new file mode 100644
index 0000000..78a6fa1
--- /dev/null
+++ b/SOURCES/0046-Use-Distribution-Package-Sort-for-grub2-mkconfig-112.patch
@@ -0,0 +1,458 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Robert Marshall <rmarshall@redhat.com>
+Date: Mon, 16 Mar 2015 14:14:19 -0400
+Subject: [PATCH] Use Distribution Package Sort for grub2-mkconfig (#1124074)
+
+Users reported that newly installed kernels on their systems installed
+with grub-mkconfig would not appear on the grub boot list in order
+starting with the most recent. Added an option for rpm-based systems to
+use the rpm-sort library to sort kernels instead.
+
+Resolves rhbz#1124074
+
+Signed-off-by: Robert Marshall <rmarshall@redhat.com>
+[pjones: fix --enable-rpm-sort configure option]
+Signed-off-by: Peter Jones <pjones@redhat.com>
+[thierry.vignaud: fix build with rpm-4.16]
+Signed-off-by: Thierry Vignaud <thierry.vignaud@gmail.com>
+---
+ configure.ac              |  38 +++++++
+ Makefile.util.def         |  16 +++
+ util/grub-rpm-sort.c      | 281 ++++++++++++++++++++++++++++++++++++++++++++++
+ util/grub-mkconfig_lib.in |  11 +-
+ util/grub-rpm-sort.8      |  12 ++
+ 5 files changed, 357 insertions(+), 1 deletion(-)
+ create mode 100644 util/grub-rpm-sort.c
+ create mode 100644 util/grub-rpm-sort.8
+
+diff --git a/configure.ac b/configure.ac
+index bec8535af70..fdcb452581c 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -72,6 +72,7 @@ grub_TRANSFORM([grub-mkrelpath])
+ grub_TRANSFORM([grub-mkrescue])
+ grub_TRANSFORM([grub-probe])
+ grub_TRANSFORM([grub-reboot])
++grub_TRANSFORM([grub-rpm-sort])
+ grub_TRANSFORM([grub-script-check])
+ grub_TRANSFORM([grub-set-default])
+ grub_TRANSFORM([grub-sparc64-setup])
+@@ -95,6 +96,7 @@ grub_TRANSFORM([grub-mkrescue.1])
+ grub_TRANSFORM([grub-mkstandalone.3])
+ grub_TRANSFORM([grub-ofpathname.3])
+ grub_TRANSFORM([grub-probe.3])
++grub_TRANSFORM([grub-rpm-sort.8])
+ grub_TRANSFORM([grub-reboot.3])
+ grub_TRANSFORM([grub-render-label.3])
+ grub_TRANSFORM([grub-script-check.3])
+@@ -1860,6 +1862,42 @@ fi
+ 
+ AC_SUBST([LIBDEVMAPPER])
+ 
++AC_ARG_ENABLE([rpm-sort],
++              [AS_HELP_STRING([--enable-rpm-sort],
++                              [enable native rpm sorting of kernels in grub (default=guessed)])])
++if test x"$enable_rpm_sort" = xno ; then
++  rpm_sort_excuse="explicitly disabled"
++fi
++
++if test x"$rpm_sort_excuse" = x ; then
++  # Check for rpmlib header.
++  AC_CHECK_HEADER([rpm/rpmlib.h], [],
++               [rpm_sort_excuse="need rpm/rpmlib header"])
++fi
++
++if test x"$rpm_sort_excuse" = x ; then
++  # Check for rpm library.
++  AC_CHECK_LIB([rpm], [rpmvercmp], [],
++               [rpm_sort_excuse="rpmlib missing rpmvercmp"])
++fi
++
++if test x"$rpm_sort_excuse" = x ; then
++   LIBRPM="-lrpm";
++   AC_DEFINE([HAVE_RPM], [1],
++             [Define to 1 if you have the rpm library.])
++fi
++
++if test x"$LIBRPM" = x ; then
++  # Check for rpm library.
++  AC_CHECK_LIB([rpmio], [rpmvercmp], [],
++               [rpm_sort_excuse="rpmio missing rpmvercmp"])
++   LIBRPM="-lrpmio";
++   AC_DEFINE([HAVE_RPMIO], [1],
++             [Define to 1 if you have the rpm library.])
++fi
++
++AC_SUBST([LIBRPM])
++
+ LIBGEOM=
+ if test x$host_kernel = xkfreebsd; then
+   AC_CHECK_LIB([geom], [geom_gettree], [],
+diff --git a/Makefile.util.def b/Makefile.util.def
+index 2c9b283a230..ba4cf4b29b0 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -703,6 +703,22 @@ program = {
+   ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
+ };
+ 
++program = {
++  name = grub-rpm-sort;
++  mansection = 8;
++  installdir = sbin;
++
++  common = grub-core/kern/emu/misc.c;
++  common = grub-core/kern/emu/argp_common.c;
++  common = grub-core/osdep/init.c;
++  common = util/misc.c;
++  common = util/grub-rpm-sort.c;
++
++  ldadd = libgrubkern.a;
++  ldadd = grub-core/lib/gnulib/libgnu.a;
++  ldadd = '$(LIBDEVMAPPER) $(LIBRPM)';
++};
++
+ script = {
+   name = grub-mkconfig;
+   common = util/grub-mkconfig.in;
+diff --git a/util/grub-rpm-sort.c b/util/grub-rpm-sort.c
+new file mode 100644
+index 00000000000..f33bd1ed568
+--- /dev/null
++++ b/util/grub-rpm-sort.c
+@@ -0,0 +1,281 @@
++#include <config.h>
++#include <grub/types.h>
++#include <grub/util/misc.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <errno.h>
++#include <assert.h>
++#include <argp.h>
++#include <rpm/rpmlib.h>
++
++static size_t
++read_file (const char *input, char **ret)
++{
++  FILE *in;
++  size_t s;
++  size_t sz = 2048;
++  size_t offset = 0;
++  char *text;
++
++  if (!strcmp(input, "-"))
++    in = stdin;
++  else
++    in = grub_util_fopen(input, "r");
++
++  text = xmalloc (sz);
++
++  if (!in)
++    grub_util_error (_("cannot open `%s': %s"), input, strerror (errno));
++
++  while ((s = fread (text + offset, 1, sz - offset, in)) != 0)
++    {
++      offset += s;
++      if (sz - offset == 0)
++	{
++	  sz += 2048;
++	  text = xrealloc (text, sz);
++	}
++    }
++
++  text[offset] = '\0';
++  *ret = text;
++
++  if (in != stdin)
++    fclose(in);
++
++  return offset + 1;
++}
++
++/* returns name/version/release */
++/* NULL string pointer returned if nothing found */
++static void
++split_package_string (char *package_string, char **name,
++                     char **version, char **release)
++{
++  char *package_version, *package_release;
++
++  /* Release */
++  package_release = strrchr (package_string, '-');
++
++  if (package_release != NULL)
++      *package_release++ = '\0';
++
++  *release = package_release;
++
++  /* Version */
++  package_version = strrchr(package_string, '-');
++
++  if (package_version != NULL)
++      *package_version++ = '\0';
++
++  *version = package_version;
++  /* Name */
++  *name = package_string;
++
++  /* Bubble up non-null values from release to name */
++  if (*name == NULL)
++    {
++      *name = (*version == NULL ? *release : *version);
++      *version = *release;
++      *release = NULL;
++    }
++  if (*version == NULL)
++    {
++      *version = *release;
++      *release = NULL;
++    }
++}
++
++/*
++ * package name-version-release comparator for qsort
++ * expects p, q which are pointers to character strings (char *)
++ * which will not be altered in this function
++ */
++static int
++package_version_compare (const void *p, const void *q)
++{
++  char *local_p, *local_q;
++  char *lhs_name, *lhs_version, *lhs_release;
++  char *rhs_name, *rhs_version, *rhs_release;
++  int vercmpflag = 0;
++
++  local_p = alloca (strlen (*(char * const *)p) + 1);
++  local_q = alloca (strlen (*(char * const *)q) + 1);
++
++  /* make sure these allocated */
++  assert (local_p);
++  assert (local_q);
++
++  strcpy (local_p, *(char * const *)p);
++  strcpy (local_q, *(char * const *)q);
++
++  split_package_string (local_p, &lhs_name, &lhs_version, &lhs_release);
++  split_package_string (local_q, &rhs_name, &rhs_version, &rhs_release);
++
++  /* Check Name and return if unequal */
++  vercmpflag = rpmvercmp ((lhs_name == NULL ? "" : lhs_name),
++                          (rhs_name == NULL ? "" : rhs_name));
++  if (vercmpflag != 0)
++    return vercmpflag;
++
++  /* Check version and return if unequal */
++  vercmpflag = rpmvercmp ((lhs_version == NULL ? "" : lhs_version),
++                          (rhs_version == NULL ? "" : rhs_version));
++  if (vercmpflag != 0)
++    return vercmpflag;
++
++  /* Check release and return the version compare value */
++  vercmpflag = rpmvercmp ((lhs_release == NULL ? "" : lhs_release),
++                          (rhs_release == NULL ? "" : rhs_release));
++
++  return vercmpflag;
++}
++
++static void
++add_input (const char *filename, char ***package_names, size_t *n_package_names)
++{
++  char *orig_input_buffer = NULL;
++  char *input_buffer;
++  char *position_of_newline;
++  char **names = *package_names;
++  char **new_names = NULL;
++  size_t n_names = *n_package_names;
++
++  if (!*package_names)
++    new_names = names = xmalloc (sizeof (char *) * 2);
++
++  if (read_file (filename, &orig_input_buffer) < 2)
++    {
++      if (new_names)
++	free (new_names);
++      if (orig_input_buffer)
++	free (orig_input_buffer);
++      return;
++    }
++
++  input_buffer = orig_input_buffer;
++  while (input_buffer && *input_buffer &&
++	 (position_of_newline = strchrnul (input_buffer, '\n')))
++    {
++      size_t sz = position_of_newline - input_buffer;
++      char *new;
++
++      if (sz == 0)
++	{
++	  input_buffer = position_of_newline + 1;
++	  continue;
++	}
++
++      new = xmalloc (sz+1);
++      strncpy (new, input_buffer, sz);
++      new[sz] = '\0';
++
++      names = xrealloc (names, sizeof (char *) * (n_names + 1));
++      names[n_names] = new;
++      n_names++;
++
++      /* move buffer ahead to next line */
++      input_buffer = position_of_newline + 1;
++      if (*position_of_newline == '\0')
++	input_buffer = NULL;
++    }
++
++  free (orig_input_buffer);
++
++  *package_names = names;
++  *n_package_names = n_names;
++}
++
++static char *
++help_filter (int key, const char *text, void *input __attribute__ ((unused)))
++{
++  return (char *)text;
++}
++
++static struct argp_option options[] = {
++  { 0, }
++};
++
++struct arguments
++{
++  size_t ninputs;
++  size_t input_max;
++  char **inputs;
++};
++
++static error_t
++argp_parser (int key, char *arg, struct argp_state *state)
++{
++  struct arguments *arguments = state->input;
++  switch (key)
++    {
++    case ARGP_KEY_ARG:
++      assert (arguments->ninputs < arguments->input_max);
++      arguments->inputs[arguments->ninputs++] = xstrdup (arg);
++      break;
++    default:
++      return ARGP_ERR_UNKNOWN;
++    }
++  return 0;
++}
++
++static struct argp argp = {
++  options, argp_parser, N_("[INPUT_FILES]"),
++  N_("Sort a list of strings in RPM version sort order."),
++  NULL, help_filter, NULL
++};
++
++int
++main (int argc, char *argv[])
++{
++  struct arguments arguments;
++  char **package_names = NULL;
++  size_t n_package_names = 0;
++  int i;
++
++  grub_util_host_init (&argc, &argv);
++
++  memset (&arguments, 0, sizeof (struct arguments));
++  arguments.input_max = argc+1;
++  arguments.inputs = xmalloc ((arguments.input_max + 1)
++			      * sizeof (arguments.inputs[0]));
++  memset (arguments.inputs, 0, (arguments.input_max + 1)
++	  * sizeof (arguments.inputs[0]));
++
++  /* Parse our arguments */
++  if (argp_parse (&argp, argc, argv, 0, 0, &arguments) != 0)
++    grub_util_error ("%s", _("Error in parsing command line arguments\n"));
++
++  /* If there's no inputs in argv, add one for stdin */
++  if (!arguments.ninputs)
++    {
++      arguments.ninputs = 1;
++      arguments.inputs[0] = xmalloc (2);
++      strcpy(arguments.inputs[0], "-");
++    }
++
++  for (i = 0; i < arguments.ninputs; i++)
++    add_input(arguments.inputs[i], &package_names, &n_package_names);
++
++  if (package_names == NULL || n_package_names < 1)
++    grub_util_error ("%s", _("Invalid input\n"));
++
++  qsort (package_names, n_package_names, sizeof (char *),
++	 package_version_compare);
++
++  /* send sorted list to stdout */
++  for (i = 0; i < n_package_names; i++)
++    {
++      fprintf (stdout, "%s\n", package_names[i]);
++      free (package_names[i]);
++    }
++
++  free (package_names);
++  for (i = 0; i < arguments.ninputs; i++)
++    free (arguments.inputs[i]);
++
++  free (arguments.inputs);
++
++  return 0;
++}
+diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in
+index 0f6505bf3b6..42c2ea9ba50 100644
+--- a/util/grub-mkconfig_lib.in
++++ b/util/grub-mkconfig_lib.in
+@@ -33,6 +33,9 @@ fi
+ if test "x$grub_mkrelpath" = x; then
+   grub_mkrelpath="${bindir}/@grub_mkrelpath@"
+ fi
++if test "x$grub_rpm_sort" = x; then
++  grub_rpm_sort="${sbindir}/@grub_rpm_sort@"
++fi
+ 
+ if command -v gettext >/dev/null; then
+   :
+@@ -218,6 +221,12 @@ version_sort ()
+    esac
+ }
+ 
++if [ "x$grub_rpm_sort" != x -a -x "$grub_rpm_sort" ]; then
++  kernel_sort="$grub_rpm_sort"
++else
++  kernel_sort=version_sort
++fi
++
+ version_test_numeric ()
+ {
+   version_test_numeric_a="$1"
+@@ -234,7 +243,7 @@ version_test_numeric ()
+     version_test_numeric_a="$version_test_numeric_b"
+     version_test_numeric_b="$version_test_numeric_c"
+   fi
+-  if (echo "$version_test_numeric_a" ; echo "$version_test_numeric_b") | version_sort | head -n 1 | grep -qx "$version_test_numeric_b" ; then
++  if (echo "$version_test_numeric_a" ; echo "$version_test_numeric_b") | "$kernel_sort" | head -n 1 | grep -qx "$version_test_numeric_b" ; then
+     return 0
+   else
+     return 1
+diff --git a/util/grub-rpm-sort.8 b/util/grub-rpm-sort.8
+new file mode 100644
+index 00000000000..8ce21488448
+--- /dev/null
++++ b/util/grub-rpm-sort.8
+@@ -0,0 +1,12 @@
++.TH GRUB-RPM-SORT 8 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-rpm-sort\fR \(em Sort input according to RPM version compare.
++
++.SH SYNOPSIS
++\fBgrub-rpm-sort\fR [OPTIONS].
++
++.SH DESCRIPTION
++You should not normally run this program directly.  Use grub-mkconfig instead.
++
++.SH SEE ALSO
++.BR "info grub"
diff --git a/SOURCES/0047-Make-grub2-mkconfig-construct-titles-that-look-like-.patch b/SOURCES/0047-Make-grub2-mkconfig-construct-titles-that-look-like-.patch
new file mode 100644
index 0000000..4c474e4
--- /dev/null
+++ b/SOURCES/0047-Make-grub2-mkconfig-construct-titles-that-look-like-.patch
@@ -0,0 +1,69 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 28 Apr 2015 11:15:03 -0400
+Subject: [PATCH] Make grub2-mkconfig construct titles that look like the ones
+ we want elsewhere.
+
+Resolves: rhbz#1215839
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ util/grub.d/10_linux.in | 34 +++++++++++++++++++++++++++-------
+ 1 file changed, 27 insertions(+), 7 deletions(-)
+
+diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
+index fed73271478..2e59f3b4197 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -78,6 +78,32 @@ case x"$GRUB_FS" in
+ 	;;
+ esac
+ 
++mktitle ()
++{
++  local title_type
++  local version
++  local OS_NAME
++  local OS_VERS
++
++  title_type=$1 && shift
++  version=$1 && shift
++
++  OS_NAME="$(eval $(grep ^NAME= /etc/os-release) ; echo ${NAME})"
++  OS_VERS="$(eval $(grep ^VERSION= /etc/os-release) ; echo ${VERSION})"
++
++  case $title_type in
++    recovery)
++      title=$(printf '%s (%s) %s (recovery mode)' \
++                     "${OS_NAME}" "${version}" "${OS_VERS}")
++      ;;
++    *)
++      title=$(printf '%s (%s) %s' \
++                     "${OS_NAME}" "${version}" "${OS_VERS}")
++      ;;
++  esac
++  echo -n ${title}
++}
++
+ title_correction_code=
+ 
+ linux_entry ()
+@@ -91,17 +117,11 @@ linux_entry ()
+       boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")"
+   fi
+   if [ x$type != xsimple ] ; then
+-      case $type in
+-	  recovery)
+-	      title="$(gettext_printf "%s, with Linux %s (recovery mode)" "${os}" "${version}")" ;;
+-	  *)
+-	      title="$(gettext_printf "%s, with Linux %s" "${os}" "${version}")" ;;
+-      esac
++      title=$(mktitle "$type" "$version")
+       if [ x"$title" = x"$GRUB_ACTUAL_DEFAULT" ] || [ x"Previous Linux versions>$title" = x"$GRUB_ACTUAL_DEFAULT" ]; then
+ 	  replacement_title="$(echo "Advanced options for ${OS}" | sed 's,>,>>,g')>$(echo "$title" | sed 's,>,>>,g')"
+ 	  quoted="$(echo "$GRUB_ACTUAL_DEFAULT" | grub_quote)"
+ 	  title_correction_code="${title_correction_code}if [ \"x\$default\" = '$quoted' ]; then default='$(echo "$replacement_title" | grub_quote)'; fi;"
+-	  grub_warn "$(gettext_printf "Please don't use old title \`%s' for GRUB_DEFAULT, use \`%s' (for versions before 2.00) or \`%s' (for 2.00 or later)" "$GRUB_ACTUAL_DEFAULT" "$replacement_title" "gnulinux-advanced-$boot_device_id>gnulinux-$version-$type-$boot_device_id")"
+       fi
+       echo "menuentry '$(echo "$title" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {" | sed "s/^/$submenu_indentation/"
+   else
diff --git a/SOURCES/0048-Add-friendly-grub2-password-config-tool-985962.patch b/SOURCES/0048-Add-friendly-grub2-password-config-tool-985962.patch
new file mode 100644
index 0000000..9807d0e
--- /dev/null
+++ b/SOURCES/0048-Add-friendly-grub2-password-config-tool-985962.patch
@@ -0,0 +1,269 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Robert Marshall <rmarshall@redhat.com>
+Date: Thu, 25 Jun 2015 11:13:11 -0400
+Subject: [PATCH] Add friendly grub2 password config tool (#985962)
+
+Provided a tool for users to reset the grub2 root user password
+without having to alter the grub.cfg. The hashed password now
+lives in a root-only-readable configuration file.
+
+Resolves: rhbz#985962
+
+Signed-off-by: Robert Marshall <rmarshall@redhat.com>
+[pjones: fix the efidir in grub-setpassword and rename tool]
+Signed-off-by: Peter Jones <pjones@redhat.com>
+[luto: fix grub-setpassword -o's output path]
+Andy Lutomirski <luto@kernel.org>
+---
+ configure.ac              |   1 +
+ Makefile.util.def         |  13 +++++
+ util/grub-mkconfig.in     |   2 +
+ util/grub-set-password.8  |  28 ++++++++++
+ util/grub-set-password.in | 128 ++++++++++++++++++++++++++++++++++++++++++++++
+ util/grub.d/01_users.in   |  11 ++++
+ 6 files changed, 183 insertions(+)
+ create mode 100644 util/grub-set-password.8
+ create mode 100644 util/grub-set-password.in
+ create mode 100644 util/grub.d/01_users.in
+
+diff --git a/configure.ac b/configure.ac
+index fdcb452581c..30fd84d8067 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -72,6 +72,7 @@ grub_TRANSFORM([grub-mkrelpath])
+ grub_TRANSFORM([grub-mkrescue])
+ grub_TRANSFORM([grub-probe])
+ grub_TRANSFORM([grub-reboot])
++grub_TRANSFORM([grub-set-password])
+ grub_TRANSFORM([grub-rpm-sort])
+ grub_TRANSFORM([grub-script-check])
+ grub_TRANSFORM([grub-set-default])
+diff --git a/Makefile.util.def b/Makefile.util.def
+index ba4cf4b29b0..1a7dd433e33 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -452,6 +452,12 @@ script = {
+   installdir = grubconf;
+ };
+ 
++script = {
++  name = '01_users';
++  common = util/grub.d/01_users.in;
++  installdir = grubconf;
++};
++
+ script = {
+   name = '10_windows';
+   common = util/grub.d/10_windows.in;
+@@ -740,6 +746,13 @@ script = {
+   installdir = sbin;
+ };
+ 
++script = {
++  name = grub-set-password;
++  common = util/grub-set-password.in;
++  mansection = 8;
++  installdir = sbin;
++};
++
+ script = {
+   name = grub-mkconfig_lib;
+   common = util/grub-mkconfig_lib.in;
+diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in
+index 8ea2315ebc2..ba14cf6261c 100644
+--- a/util/grub-mkconfig.in
++++ b/util/grub-mkconfig.in
+@@ -276,6 +276,8 @@ for i in "${grub_mkconfig_dir}"/* ; do
+     *~) ;;
+     # emacsen autosave files. FIXME: support other editors
+     */\#*\#) ;;
++    # rpm config files of yore.
++    *.rpmsave|*.rpmnew|*.rpmorig) ;;
+     *)
+       if grub_file_is_not_garbage "$i" && test -x "$i" ; then
+         echo
+diff --git a/util/grub-set-password.8 b/util/grub-set-password.8
+new file mode 100644
+index 00000000000..9646546e43d
+--- /dev/null
++++ b/util/grub-set-password.8
+@@ -0,0 +1,28 @@
++.TH GRUB-SET-PASSWORD 3 "Thu Jun 25 2015"
++.SH NAME
++\fBgrub-set-password\fR \(em Generate the user.cfg file containing the hashed grub bootloader password.
++
++.SH SYNOPSIS
++\fBgrub-set-password\fR [OPTION]
++
++.SH DESCRIPTION
++\fBgrub-set-password\fR outputs the user.cfg file which contains the hashed GRUB bootloader password. This utility only supports configurations where there is a single root user.
++
++The file has the format:
++GRUB2_PASSWORD=<\fIhashed password\fR>.
++
++.SH OPTIONS
++.TP
++-h, --help
++Display program usage and exit.
++.TP
++-v, --version
++Display the current version.
++.TP
++-o, --output=<\fIDIRECTORY\fR>
++Choose the file path to which user.cfg will be written.
++
++.SH SEE ALSO
++.BR "info grub"
++
++.BR "info grub2-mkpasswd-pbkdf2"
+diff --git a/util/grub-set-password.in b/util/grub-set-password.in
+new file mode 100644
+index 00000000000..5ebf50576d6
+--- /dev/null
++++ b/util/grub-set-password.in
+@@ -0,0 +1,128 @@
++#!/bin/sh -e
++
++EFIDIR=$(grep ^ID= /etc/os-release | sed -e 's/^ID=//' -e 's/rhel/redhat/')
++if [ -d /sys/firmware/efi/efivars/ ]; then
++    grubdir=`echo "/@bootdirname@/efi/EFI/${EFIDIR}/" | sed 's,//*,/,g'`
++else
++    grubdir=`echo "/@bootdirname@/@grubdirname@" | sed 's,//*,/,g'`
++fi
++
++PACKAGE_VERSION="@PACKAGE_VERSION@"
++PACKAGE_NAME="@PACKAGE_NAME@"
++self=`basename $0`
++bindir="@bindir@"
++grub_mkpasswd="${bindir}/@grub_mkpasswd_pbkdf2@"
++
++# Usage: usage
++# Print the usage.
++usage () {
++    cat <<EOF
++Usage: $0 [OPTION]
++$0 prompts the user to set a password on the grub bootloader. The password
++is written to a file named user.cfg which lives in the GRUB directory
++located by default at ${grubdir}.
++
++  -h, --help                     print this message and exit
++  -v, --version                  print the version information and exit
++  -o, --output_path <DIRECTORY>  put user.cfg in a user-selected directory
++
++Report bugs at https://bugzilla.redhat.com.
++EOF
++}
++
++argument () {
++    opt=$1
++    shift
++
++    if test $# -eq 0; then
++        gettext_printf "%s: option requires an argument -- \`%s'\n" "$self" "$opt" 1>&2
++        exit 1
++    fi
++    echo $1
++}
++
++# Ensure that it's the root user running this script
++if [ "${EUID}" -ne 0 ]; then
++    echo "The grub bootloader password may only be set by root."
++    usage
++    exit 2
++fi
++
++# Check the arguments.
++while test $# -gt 0
++do
++    option=$1
++    shift
++
++    case "$option" in
++    -h | --help)
++	usage
++	exit 0 ;;
++    -v | --version)
++	echo "$self (${PACKAGE_NAME}) ${PACKAGE_VERSION}"
++	exit 0 ;;
++    -o | --output)
++        OUTPUT_PATH=`argument $option "$@"`; shift ;;
++    --output=*)
++        OUTPUT_PATH=`echo "$option" | sed 's/--output=//'` ;;
++    -o=*)
++        OUTPUT_PATH=`echo "$option" | sed 's/-o=//'` ;;
++    esac
++done
++
++# set user input or default path for user.cfg file
++if [ -z "${OUTPUT_PATH}" ]; then
++    OUTPUT_PATH="${grubdir}"
++fi
++
++if [ ! -d "${OUTPUT_PATH}" ]; then
++    echo "${OUTPUT_PATH} does not exist."
++    usage
++    exit 2;
++fi
++
++ttyopt=$(stty -g)
++fixtty() {
++      stty ${ttyopt}
++}
++
++trap fixtty EXIT
++stty -echo
++
++# prompt & confirm new grub2 root user password
++echo -n "Enter password: "
++read PASSWORD
++echo
++echo -n "Confirm password: "
++read PASSWORD_CONFIRM
++echo
++stty ${ttyopt}
++
++getpass() {
++    local P0
++    local P1
++    P0="$1" && shift
++    P1="$1" && shift
++
++    ( echo ${P0} ; echo ${P1} ) | \
++        LC_ALL=C ${grub_mkpasswd} | \
++        grep -v '[eE]nter password:' | \
++        sed -e "s/PBKDF2 hash of your password is //"
++}
++
++MYPASS="$(getpass "${PASSWORD}" "${PASSWORD_CONFIRM}")"
++if [ -z "${MYPASS}" ]; then
++      echo "${self}: error: empty password" 1>&2
++      exit 1
++fi
++
++# on the ESP, these will fail to set the permissions, but it's okay because
++# the directory is protected.
++install -m 0600 /dev/null "${OUTPUT_PATH}/user.cfg" 2>/dev/null || :
++chmod 0600 "${OUTPUT_PATH}/user.cfg" 2>/dev/null || :
++echo "GRUB2_PASSWORD=${MYPASS}" > "${OUTPUT_PATH}/user.cfg"
++
++if ! grep -q "^### BEGIN /etc/grub.d/01_users ###$" "${OUTPUT_PATH}/grub.cfg"; then
++    echo "WARNING: The current configuration lacks password support!"
++    echo "Update your configuration with @grub_mkconfig@ to support this feature."
++fi
+diff --git a/util/grub.d/01_users.in b/util/grub.d/01_users.in
+new file mode 100644
+index 00000000000..db2f44bfb78
+--- /dev/null
++++ b/util/grub.d/01_users.in
+@@ -0,0 +1,11 @@
++#!/bin/sh -e
++cat << EOF
++if [ -f \${prefix}/user.cfg ]; then
++  source \${prefix}/user.cfg
++  if [ -n "\${GRUB2_PASSWORD}" ]; then
++    set superusers="root"
++    export superusers
++    password_pbkdf2 root \${GRUB2_PASSWORD}
++  fi
++fi
++EOF
diff --git a/SOURCES/0049-tcp-add-window-scaling-support.patch b/SOURCES/0049-tcp-add-window-scaling-support.patch
new file mode 100644
index 0000000..7d1996c
--- /dev/null
+++ b/SOURCES/0049-tcp-add-window-scaling-support.patch
@@ -0,0 +1,87 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Josef Bacik <jbacik@fb.com>
+Date: Wed, 12 Aug 2015 08:57:55 -0700
+Subject: [PATCH] tcp: add window scaling support
+
+Sometimes we have to provision boxes across regions, such as California to
+Sweden.  The http server has a 10 minute timeout, so if we can't get our 250mb
+image transferred fast enough our provisioning fails, which is not ideal.  So
+add tcp window scaling on open connections and set the window size to 1mb.  With
+this change we're able to get higher sustained transfers between regions and can
+transfer our image in well below 10 minutes.  Without this patch we'd time out
+every time halfway through the transfer.  Thanks,
+
+Signed-off-by: Josef Bacik <jbacik@fb.com>
+---
+ grub-core/net/tcp.c | 42 +++++++++++++++++++++++++++++-------------
+ 1 file changed, 29 insertions(+), 13 deletions(-)
+
+diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c
+index e8ad34b84d4..7d4b822626d 100644
+--- a/grub-core/net/tcp.c
++++ b/grub-core/net/tcp.c
+@@ -106,6 +106,18 @@ struct tcphdr
+   grub_uint16_t urgent;
+ } GRUB_PACKED;
+ 
++struct tcp_scale_opt {
++  grub_uint8_t kind;
++  grub_uint8_t length;
++  grub_uint8_t scale;
++} GRUB_PACKED;
++
++struct tcp_synhdr {
++  struct tcphdr tcphdr;
++  struct tcp_scale_opt scale_opt;
++  grub_uint8_t padding;
++};
++
+ struct tcp_pseudohdr
+ {
+   grub_uint32_t src;
+@@ -566,7 +578,7 @@ grub_net_tcp_open (char *server,
+   grub_net_tcp_socket_t socket;
+   static grub_uint16_t in_port = 21550;
+   struct grub_net_buff *nb;
+-  struct tcphdr *tcph;
++  struct tcp_synhdr *tcph;
+   int i;
+   grub_uint8_t *nbd;
+   grub_net_link_level_address_t ll_target_addr;
+@@ -635,20 +647,24 @@ grub_net_tcp_open (char *server,
+     }
+ 
+   tcph = (void *) nb->data;
++  grub_memset(tcph, 0, sizeof (*tcph));
+   socket->my_start_seq = grub_get_time_ms ();
+   socket->my_cur_seq = socket->my_start_seq + 1;
+-  socket->my_window = 8192;
+-  tcph->seqnr = grub_cpu_to_be32 (socket->my_start_seq);
+-  tcph->ack = grub_cpu_to_be32_compile_time (0);
+-  tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_SYN);
+-  tcph->window = grub_cpu_to_be16 (socket->my_window);
+-  tcph->urgent = 0;
+-  tcph->src = grub_cpu_to_be16 (socket->in_port);
+-  tcph->dst = grub_cpu_to_be16 (socket->out_port);
+-  tcph->checksum = 0;
+-  tcph->checksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_TCP,
+-						   &socket->inf->address,
+-						   &socket->out_nla);
++  socket->my_window = 32768;
++  tcph->tcphdr.seqnr = grub_cpu_to_be32 (socket->my_start_seq);
++  tcph->tcphdr.ack = grub_cpu_to_be32_compile_time (0);
++  tcph->tcphdr.flags = grub_cpu_to_be16_compile_time ((6 << 12) | TCP_SYN);
++  tcph->tcphdr.window = grub_cpu_to_be16 (socket->my_window);
++  tcph->tcphdr.urgent = 0;
++  tcph->tcphdr.src = grub_cpu_to_be16 (socket->in_port);
++  tcph->tcphdr.dst = grub_cpu_to_be16 (socket->out_port);
++  tcph->tcphdr.checksum = 0;
++  tcph->scale_opt.kind = 3;
++  tcph->scale_opt.length = 3;
++  tcph->scale_opt.scale = 5;
++  tcph->tcphdr.checksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_TCP,
++							  &socket->inf->address,
++							  &socket->out_nla);
+ 
+   tcp_socket_register (socket);
+ 
diff --git a/SOURCES/0050-efinet-and-bootp-add-support-for-dhcpv6.patch b/SOURCES/0050-efinet-and-bootp-add-support-for-dhcpv6.patch
new file mode 100644
index 0000000..a9844b8
--- /dev/null
+++ b/SOURCES/0050-efinet-and-bootp-add-support-for-dhcpv6.patch
@@ -0,0 +1,651 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 9 Jul 2019 11:47:37 +0200
+Subject: [PATCH] efinet and bootp: add support for dhcpv6
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/net/bootp.c              | 173 +++++++++++++++++++++++++++++++++++++
+ grub-core/net/drivers/efi/efinet.c |  53 ++++++++++--
+ grub-core/net/net.c                |  72 +++++++++++++++
+ grub-core/net/tftp.c               |   4 +
+ include/grub/efi/api.h             | 129 +++++++++++++++++++++++++--
+ include/grub/net.h                 |  60 +++++++++++++
+ 6 files changed, 477 insertions(+), 14 deletions(-)
+
+diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c
+index 6fb5627025d..e28fb6a09f9 100644
+--- a/grub-core/net/bootp.c
++++ b/grub-core/net/bootp.c
+@@ -902,6 +902,179 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
+ 
+ static grub_command_t cmd_getdhcp, cmd_bootp, cmd_dhcp;
+ 
++struct grub_net_network_level_interface *
++grub_net_configure_by_dhcpv6_ack (const char *name,
++				  struct grub_net_card *card,
++				  grub_net_interface_flags_t flags
++				    __attribute__((__unused__)),
++				  const grub_net_link_level_address_t *hwaddr,
++				  const struct grub_net_dhcpv6_packet *packet,
++				  int is_def, char **device, char **path)
++{
++  struct grub_net_network_level_interface *inter = NULL;
++  struct grub_net_network_level_address addr;
++  int mask = -1;
++
++  if (!device || !path)
++    return NULL;
++
++  *device = 0;
++  *path = 0;
++
++  grub_dprintf ("net", "mac address is %02x:%02x:%02x:%02x:%02x:%02x\n",
++		hwaddr->mac[0], hwaddr->mac[1], hwaddr->mac[2],
++		hwaddr->mac[3], hwaddr->mac[4], hwaddr->mac[5]);
++
++  if (is_def)
++    grub_net_default_server = 0;
++
++  if (is_def && !grub_net_default_server && packet)
++    {
++      const grub_uint8_t *options = packet->dhcp_options;
++      unsigned int option_max = 1024 - OFFSET_OF (dhcp_options, packet);
++      unsigned int i;
++
++      for (i = 0; i < option_max - sizeof (grub_net_dhcpv6_option_t); )
++	{
++	  grub_uint16_t num, len;
++	  grub_net_dhcpv6_option_t *opt =
++	    (grub_net_dhcpv6_option_t *)(options + i);
++
++	  num = grub_be_to_cpu16(opt->option_num);
++	  len = grub_be_to_cpu16(opt->option_len);
++
++	  grub_dprintf ("net", "got dhcpv6 option %d len %d\n", num, len);
++
++	  if (len == 0)
++	    break;
++
++	  if (len + i > 1024)
++	    break;
++
++	  if (num == GRUB_NET_DHCP6_BOOTFILE_URL)
++	    {
++	      char *scheme, *userinfo, *host, *file;
++	      char *tmp;
++	      int hostlen;
++	      int port;
++	      int rc = extract_url_info ((const char *)opt->option_data,
++					 (grub_size_t)len,
++					 &scheme, &userinfo, &host, &port,
++					 &file);
++	      if (rc < 0)
++		continue;
++
++	      /* right now this only handles tftp. */
++	      if (grub_strcmp("tftp", scheme))
++		{
++		  grub_free (scheme);
++		  grub_free (userinfo);
++		  grub_free (host);
++		  grub_free (file);
++		  continue;
++		}
++	      grub_free (userinfo);
++
++	      hostlen = grub_strlen (host);
++	      if (hostlen > 2 && host[0] == '[' && host[hostlen-1] == ']')
++		{
++		  tmp = host+1;
++		  host[hostlen-1] = '\0';
++		}
++	      else
++		tmp = host;
++
++	      *device = grub_xasprintf ("%s,%s", scheme, tmp);
++	      grub_free (scheme);
++	      grub_free (host);
++
++	      if (file && *file)
++		{
++		  tmp = grub_strrchr (file, '/');
++		  if (tmp)
++		    *(tmp+1) = '\0';
++		  else
++		    file[0] = '\0';
++		}
++	      else if (!file)
++		file = grub_strdup ("");
++
++	      if (file[0] == '/')
++		{
++		  *path = grub_strdup (file+1);
++		  grub_free (file);
++		}
++	      else
++		*path = file;
++	    }
++	  else if (num == GRUB_NET_DHCP6_IA_NA)
++	    {
++	      const grub_net_dhcpv6_option_t *ia_na_opt;
++	      const grub_net_dhcpv6_opt_ia_na_t *ia_na =
++		(const grub_net_dhcpv6_opt_ia_na_t *)opt;
++	      unsigned int left = len - OFFSET_OF (options, ia_na);
++	      unsigned int j;
++
++	      if ((grub_uint8_t *)ia_na + left >
++		  (grub_uint8_t *)options + option_max)
++		left -= ((grub_uint8_t *)ia_na + left)
++		        - ((grub_uint8_t *)options + option_max);
++
++	      if (len < OFFSET_OF (option_data, opt)
++			+ sizeof (grub_net_dhcpv6_option_t))
++		{
++		  grub_dprintf ("net",
++				"found dhcpv6 ia_na option with no address\n");
++		  continue;
++		}
++
++	      for (j = 0; left > sizeof (grub_net_dhcpv6_option_t); )
++		{
++		  ia_na_opt = (const grub_net_dhcpv6_option_t *)
++			       (ia_na->options + j);
++		  grub_uint16_t ia_na_opt_num, ia_na_opt_len;
++
++		  ia_na_opt_num = grub_be_to_cpu16 (ia_na_opt->option_num);
++		  ia_na_opt_len = grub_be_to_cpu16 (ia_na_opt->option_len);
++		  if (ia_na_opt_len == 0)
++		    break;
++		  if (j + ia_na_opt_len > left)
++		    break;
++		  if (ia_na_opt_num == GRUB_NET_DHCP6_IA_ADDRESS)
++		    {
++		      const grub_net_dhcpv6_opt_ia_address_t *ia_addr;
++
++		      ia_addr = (const grub_net_dhcpv6_opt_ia_address_t *)
++				 ia_na_opt;
++		      addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
++		      grub_memcpy(addr.ipv6, ia_addr->ipv6_address,
++				  sizeof (ia_addr->ipv6_address));
++		      inter = grub_net_add_addr (name, card, &addr, hwaddr, 0);
++		    }
++
++		  j += ia_na_opt_len;
++		  left -= ia_na_opt_len;
++		}
++	    }
++
++	  i += len + 4;
++	}
++
++      grub_print_error ();
++    }
++
++  if (is_def)
++    {
++      grub_env_set ("net_default_interface", name);
++      grub_env_export ("net_default_interface");
++    }
++
++    if (inter)
++      grub_net_add_ipv6_local (inter, mask);
++    return inter;
++}
++
++
+ void
+ grub_bootp_init (void)
+ {
+diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
+index 5388f952ba9..173fb63153c 100644
+--- a/grub-core/net/drivers/efi/efinet.c
++++ b/grub-core/net/drivers/efi/efinet.c
+@@ -18,11 +18,14 @@
+ 
+ #include <grub/net/netbuff.h>
+ #include <grub/dl.h>
++#include <grub/env.h>
+ #include <grub/net.h>
+ #include <grub/time.h>
+ #include <grub/efi/api.h>
+ #include <grub/efi/efi.h>
+ #include <grub/i18n.h>
++#include <grub/lib/hexdump.h>
++#include <grub/types.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -329,7 +332,7 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+ 			  char **path)
+ {
+   struct grub_net_card *card;
+-  grub_efi_device_path_t *dp;
++  grub_efi_device_path_t *dp, *ldp = NULL;
+ 
+   dp = grub_efi_get_device_path (hnd);
+   if (! dp)
+@@ -340,14 +343,19 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+     grub_efi_device_path_t *cdp;
+     struct grub_efi_pxe *pxe;
+     struct grub_efi_pxe_mode *pxe_mode;
++
+     if (card->driver != &efidriver)
+       continue;
++
+     cdp = grub_efi_get_device_path (card->efi_handle);
+     if (! cdp)
+       continue;
++
++    ldp = grub_efi_find_last_device_path (dp);
++
+     if (grub_efi_compare_device_paths (dp, cdp) != 0)
+       {
+-	grub_efi_device_path_t *ldp, *dup_dp, *dup_ldp;
++	grub_efi_device_path_t *dup_dp, *dup_ldp;
+ 	int match;
+ 
+ 	/* EDK2 UEFI PXE driver creates pseudo devices with type IPv4/IPv6
+@@ -356,7 +364,6 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+ 	   devices. We skip them when enumerating cards, so here we need to
+ 	   find matching MAC device.
+          */
+-	ldp = grub_efi_find_last_device_path (dp);
+ 	if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
+ 	    || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE
+ 		&& GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE))
+@@ -373,16 +380,46 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+ 	if (!match)
+ 	  continue;
+       }
++
+     pxe = grub_efi_open_protocol (hnd, &pxe_io_guid,
+ 				  GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+     if (! pxe)
+       continue;
++
+     pxe_mode = pxe->mode;
+-    grub_net_configure_by_dhcp_ack (card->name, card, 0,
+-				    (struct grub_net_bootp_packet *)
+-				    &pxe_mode->dhcp_ack,
+-				    sizeof (pxe_mode->dhcp_ack),
+-				    1, device, path);
++    if (pxe_mode->using_ipv6)
++      {
++	grub_net_link_level_address_t hwaddr;
++	struct grub_net_network_level_interface *intf;
++
++	grub_dprintf ("efinet", "using ipv6 and dhcpv6\n");
++	grub_dprintf ("efinet", "dhcp_ack_received: %s%s\n",
++		      pxe_mode->dhcp_ack_received ? "yes" : "no",
++		      pxe_mode->dhcp_ack_received ? "" : " cannot continue");
++	if (!pxe_mode->dhcp_ack_received)
++	  continue;
++
++	hwaddr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
++	grub_memcpy (hwaddr.mac,
++		     card->efi_net->mode->current_address,
++		     sizeof (hwaddr.mac));
++
++	intf = grub_net_configure_by_dhcpv6_ack (card->name, card, 0, &hwaddr,
++	      (const struct grub_net_dhcpv6_packet *)&pxe_mode->dhcp_ack.dhcpv6,
++	      1, device, path);
++	if (intf && device && path)
++	  grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path);
++      }
++    else
++      {
++	grub_dprintf ("efinet", "using ipv4 and dhcp\n");
++	grub_net_configure_by_dhcp_ack (card->name, card, 0,
++					(struct grub_net_bootp_packet *)
++					&pxe_mode->dhcp_ack,
++					sizeof (pxe_mode->dhcp_ack),
++					1, device, path);
++	grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path);
++      }
+     return;
+   }
+ }
+diff --git a/grub-core/net/net.c b/grub-core/net/net.c
+index 0ef148f4adc..22f2689aaeb 100644
+--- a/grub-core/net/net.c
++++ b/grub-core/net/net.c
+@@ -960,6 +960,78 @@ grub_net_network_level_interface_register (struct grub_net_network_level_interfa
+   grub_net_network_level_interfaces = inter;
+ }
+ 
++int
++grub_ipv6_get_masksize (grub_uint16_t be_mask[8])
++{
++  grub_uint8_t *mask;
++  grub_uint16_t mask16[8];
++  int x, y;
++  int ret = 128;
++
++  grub_memcpy (mask16, be_mask, sizeof (mask16));
++  for (x = 0; x < 8; x++)
++    mask16[x] = grub_be_to_cpu16 (mask16[x]);
++
++  mask = (grub_uint8_t *)mask16;
++
++  for (x = 15; x >= 0; x--)
++    {
++      grub_uint8_t octet = mask[x];
++      if (!octet)
++	{
++	  ret -= 8;
++	  continue;
++	}
++      for (y = 0; y < 8; y++)
++	{
++	  if (octet & (1 << y))
++	    break;
++	  else
++	    ret--;
++	}
++      break;
++    }
++
++  return ret;
++}
++
++grub_err_t
++grub_net_add_ipv6_local (struct grub_net_network_level_interface *inter,
++			 int mask)
++{
++  struct grub_net_route *route;
++
++  if (inter->address.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6)
++    return 0;
++
++  if (mask == -1)
++      mask = grub_ipv6_get_masksize ((grub_uint16_t *)inter->address.ipv6);
++
++  if (mask == -1)
++    return 0;
++
++  route = grub_zalloc (sizeof (*route));
++  if (!route)
++    return grub_errno;
++
++  route->name = grub_xasprintf ("%s:local", inter->name);
++  if (!route->name)
++    {
++      grub_free (route);
++      return grub_errno;
++    }
++
++  route->target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
++  grub_memcpy (route->target.ipv6.base, inter->address.ipv6,
++	       sizeof (inter->address.ipv6));
++  route->target.ipv6.masksize = mask;
++  route->is_gateway = 0;
++  route->interface = inter;
++
++  grub_net_route_register (route);
++
++  return 0;
++}
+ 
+ grub_err_t
+ grub_net_add_ipv4_local (struct grub_net_network_level_interface *inter,
+diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c
+index 7f44b30f521..4ab2f5c7357 100644
+--- a/grub-core/net/tftp.c
++++ b/grub-core/net/tftp.c
+@@ -358,18 +358,22 @@ tftp_open (struct grub_file *file, const char *filename)
+   file->not_easily_seekable = 1;
+   file->data = data;
+ 
++  grub_dprintf("tftp", "resolving address for %s\n", file->device->net->server);
+   err = grub_net_resolve_address (file->device->net->server, &addr);
+   if (err)
+     {
++      grub_dprintf("tftp", "Address resolution failed: %d\n", err);
+       grub_free (data);
+       return err;
+     }
+ 
++  grub_dprintf("tftp", "opening connection\n");
+   data->sock = grub_net_udp_open (addr,
+ 				  TFTP_SERVER_PORT, tftp_receive,
+ 				  file);
+   if (!data->sock)
+     {
++      grub_dprintf("tftp", "connection failed\n");
+       grub_free (data);
+       return grub_errno;
+     }
+diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
+index f1a52210c0c..117469450d3 100644
+--- a/include/grub/efi/api.h
++++ b/include/grub/efi/api.h
+@@ -592,10 +592,16 @@ typedef void *grub_efi_handle_t;
+ typedef void *grub_efi_event_t;
+ typedef grub_efi_uint64_t grub_efi_lba_t;
+ typedef grub_efi_uintn_t grub_efi_tpl_t;
+-typedef grub_uint8_t grub_efi_mac_address_t[32];
+-typedef grub_uint8_t grub_efi_ipv4_address_t[4];
+-typedef grub_uint16_t grub_efi_ipv6_address_t[8];
+-typedef grub_uint8_t grub_efi_ip_address_t[8] __attribute__ ((aligned(4)));
++typedef grub_efi_uint8_t grub_efi_mac_address_t[32];
++typedef grub_efi_uint8_t grub_efi_ipv4_address_t[4];
++typedef grub_efi_uint8_t grub_efi_ipv6_address_t[16];
++typedef union
++{
++  grub_efi_uint32_t addr[4];
++  grub_efi_ipv4_address_t v4;
++  grub_efi_ipv6_address_t v6;
++} grub_efi_ip_address_t __attribute__ ((aligned(4)));
++
+ typedef grub_efi_uint64_t grub_efi_physical_address_t;
+ typedef grub_efi_uint64_t grub_efi_virtual_address_t;
+ 
+@@ -1474,16 +1480,127 @@ struct grub_efi_simple_text_output_interface
+ };
+ typedef struct grub_efi_simple_text_output_interface grub_efi_simple_text_output_interface_t;
+ 
+-typedef grub_uint8_t grub_efi_pxe_packet_t[1472];
++typedef struct grub_efi_pxe_dhcpv4_packet
++{
++  grub_efi_uint8_t bootp_opcode;
++  grub_efi_uint8_t bootp_hwtype;
++  grub_efi_uint8_t bootp_hwaddr_len;
++  grub_efi_uint8_t bootp_gate_hops;
++  grub_efi_uint32_t bootp_ident;
++  grub_efi_uint16_t bootp_seconds;
++  grub_efi_uint16_t bootp_flags;
++  grub_efi_uint8_t bootp_ci_addr[4];
++  grub_efi_uint8_t bootp_yi_addr[4];
++  grub_efi_uint8_t bootp_si_addr[4];
++  grub_efi_uint8_t bootp_gi_addr[4];
++  grub_efi_uint8_t bootp_hw_addr[16];
++  grub_efi_uint8_t bootp_srv_name[64];
++  grub_efi_uint8_t bootp_boot_file[128];
++  grub_efi_uint32_t dhcp_magik;
++  grub_efi_uint8_t dhcp_options[56];
++} grub_efi_pxe_dhcpv4_packet_t;
++
++struct grub_efi_pxe_dhcpv6_packet
++{
++  grub_efi_uint32_t message_type:8;
++  grub_efi_uint32_t transaction_id:24;
++  grub_efi_uint8_t dhcp_options[1024];
++} GRUB_PACKED;
++typedef struct grub_efi_pxe_dhcpv6_packet grub_efi_pxe_dhcpv6_packet_t;
++
++typedef union
++{
++  grub_efi_uint8_t raw[1472];
++  grub_efi_pxe_dhcpv4_packet_t dhcpv4;
++  grub_efi_pxe_dhcpv6_packet_t dhcpv6;
++} grub_efi_pxe_packet_t;
++
++#define GRUB_EFI_PXE_MAX_IPCNT 8
++#define GRUB_EFI_PXE_MAX_ARP_ENTRIES 8
++#define GRUB_EFI_PXE_MAX_ROUTE_ENTRIES 8
++
++typedef struct grub_efi_pxe_ip_filter
++{
++  grub_efi_uint8_t filters;
++  grub_efi_uint8_t ip_count;
++  grub_efi_uint8_t reserved;
++  grub_efi_ip_address_t ip_list[GRUB_EFI_PXE_MAX_IPCNT];
++} grub_efi_pxe_ip_filter_t;
++
++typedef struct grub_efi_pxe_arp_entry
++{
++  grub_efi_ip_address_t ip_addr;
++  grub_efi_mac_address_t mac_addr;
++} grub_efi_pxe_arp_entry_t;
++
++typedef struct grub_efi_pxe_route_entry
++{
++  grub_efi_ip_address_t ip_addr;
++  grub_efi_ip_address_t subnet_mask;
++  grub_efi_ip_address_t gateway_addr;
++} grub_efi_pxe_route_entry_t;
++
++typedef struct grub_efi_pxe_icmp_error
++{
++  grub_efi_uint8_t type;
++  grub_efi_uint8_t code;
++  grub_efi_uint16_t checksum;
++  union
++    {
++      grub_efi_uint32_t reserved;
++      grub_efi_uint32_t mtu;
++      grub_efi_uint32_t pointer;
++      struct
++	{
++	  grub_efi_uint16_t identifier;
++	  grub_efi_uint16_t sequence;
++	} echo;
++    } u;
++  grub_efi_uint8_t data[494];
++} grub_efi_pxe_icmp_error_t;
++
++typedef struct grub_efi_pxe_tftp_error
++{
++  grub_efi_uint8_t error_code;
++  grub_efi_char8_t error_string[127];
++} grub_efi_pxe_tftp_error_t;
+ 
+ typedef struct grub_efi_pxe_mode
+ {
+-  grub_uint8_t unused[52];
++  grub_efi_boolean_t started;
++  grub_efi_boolean_t ipv6_available;
++  grub_efi_boolean_t ipv6_supported;
++  grub_efi_boolean_t using_ipv6;
++  grub_efi_boolean_t bis_supported;
++  grub_efi_boolean_t bis_detected;
++  grub_efi_boolean_t auto_arp;
++  grub_efi_boolean_t send_guid;
++  grub_efi_boolean_t dhcp_discover_valid;
++  grub_efi_boolean_t dhcp_ack_received;
++  grub_efi_boolean_t proxy_offer_received;
++  grub_efi_boolean_t pxe_discover_valid;
++  grub_efi_boolean_t pxe_reply_received;
++  grub_efi_boolean_t pxe_bis_reply_received;
++  grub_efi_boolean_t icmp_error_received;
++  grub_efi_boolean_t tftp_error_received;
++  grub_efi_boolean_t make_callbacks;
++  grub_efi_uint8_t ttl;
++  grub_efi_uint8_t tos;
++  grub_efi_ip_address_t station_ip;
++  grub_efi_ip_address_t subnet_mask;
+   grub_efi_pxe_packet_t dhcp_discover;
+   grub_efi_pxe_packet_t dhcp_ack;
+   grub_efi_pxe_packet_t proxy_offer;
+   grub_efi_pxe_packet_t pxe_discover;
+   grub_efi_pxe_packet_t pxe_reply;
++  grub_efi_pxe_packet_t pxe_bis_reply;
++  grub_efi_pxe_ip_filter_t ip_filter;
++  grub_efi_uint32_t arp_cache_entries;
++  grub_efi_pxe_arp_entry_t arp_cache[GRUB_EFI_PXE_MAX_ARP_ENTRIES];
++  grub_efi_uint32_t route_table_entries;
++  grub_efi_pxe_route_entry_t route_table[GRUB_EFI_PXE_MAX_ROUTE_ENTRIES];
++  grub_efi_pxe_icmp_error_t icmp_error;
++  grub_efi_pxe_tftp_error_t tftp_error;
+ } grub_efi_pxe_mode_t;
+ 
+ typedef struct grub_efi_pxe
+diff --git a/include/grub/net.h b/include/grub/net.h
+index 7ae4b6bd805..8a05ec4fe7a 100644
+--- a/include/grub/net.h
++++ b/include/grub/net.h
+@@ -447,6 +447,51 @@ struct grub_net_bootp_packet
+   grub_uint8_t vendor[0];
+ } GRUB_PACKED;
+ 
++enum
++  {
++    GRUB_NET_DHCP6_IA_NA = 3,
++    GRUB_NET_DHCP6_IA_ADDRESS = 5,
++    GRUB_NET_DHCP6_BOOTFILE_URL = 59,
++  };
++
++struct grub_net_dhcpv6_option
++{
++  grub_uint16_t option_num;
++  grub_uint16_t option_len;
++  grub_uint8_t option_data[];
++} GRUB_PACKED;
++typedef struct grub_net_dhcpv6_option grub_net_dhcpv6_option_t;
++
++struct grub_net_dhcpv6_opt_ia_na
++{
++  grub_uint16_t option_num;
++  grub_uint16_t option_len;
++  grub_uint32_t iaid;
++  grub_uint32_t t1;
++  grub_uint32_t t2;
++  grub_uint8_t options[];
++} GRUB_PACKED;
++typedef struct grub_net_dhcpv6_opt_ia_na grub_net_dhcpv6_opt_ia_na_t;
++
++struct grub_net_dhcpv6_opt_ia_address
++{
++  grub_uint16_t option_num;
++  grub_uint16_t option_len;
++  grub_uint64_t ipv6_address[2];
++  grub_uint32_t preferred_lifetime;
++  grub_uint32_t valid_lifetime;
++  grub_uint8_t options[];
++} GRUB_PACKED;
++typedef struct grub_net_dhcpv6_opt_ia_address grub_net_dhcpv6_opt_ia_address_t;
++
++struct grub_net_dhcpv6_packet
++{
++  grub_uint32_t message_type:8;
++  grub_uint32_t transaction_id:24;
++  grub_uint8_t dhcp_options[1024];
++} GRUB_PACKED;
++typedef struct grub_net_dhcpv6_packet grub_net_dhcpv6_packet_t;
++
+ #define	GRUB_NET_BOOTP_RFC1048_MAGIC_0	0x63
+ #define	GRUB_NET_BOOTP_RFC1048_MAGIC_1	0x82
+ #define	GRUB_NET_BOOTP_RFC1048_MAGIC_2	0x53
+@@ -482,6 +527,21 @@ grub_net_configure_by_dhcp_ack (const char *name,
+ 				grub_size_t size,
+ 				int is_def, char **device, char **path);
+ 
++struct grub_net_network_level_interface *
++grub_net_configure_by_dhcpv6_ack (const char *name,
++				 struct grub_net_card *card,
++				 grub_net_interface_flags_t flags,
++				 const grub_net_link_level_address_t *hwaddr,
++				 const struct grub_net_dhcpv6_packet *packet,
++				 int is_def, char **device, char **path);
++
++int
++grub_ipv6_get_masksize(grub_uint16_t *mask);
++
++grub_err_t
++grub_net_add_ipv6_local (struct grub_net_network_level_interface *inf,
++			 int mask);
++
+ grub_err_t
+ grub_net_add_ipv4_local (struct grub_net_network_level_interface *inf,
+ 			 int mask);
diff --git a/SOURCES/0051-Add-grub-get-kernel-settings-and-use-it-in-10_linux.patch b/SOURCES/0051-Add-grub-get-kernel-settings-and-use-it-in-10_linux.patch
new file mode 100644
index 0000000..e799ac1
--- /dev/null
+++ b/SOURCES/0051-Add-grub-get-kernel-settings-and-use-it-in-10_linux.patch
@@ -0,0 +1,296 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 23 Jun 2016 11:01:39 -0400
+Subject: [PATCH] Add grub-get-kernel-settings and use it in 10_linux
+
+This patch adds grub-get-kernel-settings, which reads the system kernel
+installation configuration from /etc/sysconfig/kernel, and outputs
+${GRUB_...} variables suitable for evaluation by grub-mkconfig.  Those
+variables are then used by 10_linux to choose whether or not to create
+debug stanzas.
+
+Resolves: rhbz#1226325
+---
+ configure.ac                                   |  2 +
+ Makefile.util.def                              |  7 ++
+ util/bash-completion.d/grub-completion.bash.in | 22 +++++++
+ util/grub-get-kernel-settings.3                | 20 ++++++
+ util/grub-get-kernel-settings.in               | 88 ++++++++++++++++++++++++++
+ util/grub-mkconfig.in                          |  3 +
+ util/grub.d/10_linux.in                        | 23 +++++--
+ 7 files changed, 160 insertions(+), 5 deletions(-)
+ create mode 100644 util/grub-get-kernel-settings.3
+ create mode 100644 util/grub-get-kernel-settings.in
+
+diff --git a/configure.ac b/configure.ac
+index 30fd84d8067..ed31ea457d2 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -65,6 +65,7 @@ grub_TRANSFORM([grub-install])
+ grub_TRANSFORM([grub-mkconfig])
+ grub_TRANSFORM([grub-mkfont])
+ grub_TRANSFORM([grub-mkimage])
++grub_TRANSFORM([grub-get-kernel-settings])
+ grub_TRANSFORM([grub-glue-efi])
+ grub_TRANSFORM([grub-mklayout])
+ grub_TRANSFORM([grub-mkpasswd-pbkdf2])
+@@ -82,6 +83,7 @@ grub_TRANSFORM([grub-file])
+ grub_TRANSFORM([grub-bios-setup.3])
+ grub_TRANSFORM([grub-editenv.1])
+ grub_TRANSFORM([grub-fstest.3])
++grub_TRANSFORM([grub-get-kernel-settings.3])
+ grub_TRANSFORM([grub-glue-efi.3])
+ grub_TRANSFORM([grub-install.1])
+ grub_TRANSFORM([grub-kbdcomp.3])
+diff --git a/Makefile.util.def b/Makefile.util.def
+index 1a7dd433e33..cdd2f51fe4b 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -732,6 +732,13 @@ script = {
+   installdir = sbin;
+ };
+ 
++script = {
++  name = grub-get-kernel-settings;
++  common = util/grub-get-kernel-settings.in;
++  mansection = 3;
++  installdir = sbin;
++};
++
+ script = {
+   name = grub-set-default;
+   common = util/grub-set-default.in;
+diff --git a/util/bash-completion.d/grub-completion.bash.in b/util/bash-completion.d/grub-completion.bash.in
+index 44bf135b9f8..5c4acd496d4 100644
+--- a/util/bash-completion.d/grub-completion.bash.in
++++ b/util/bash-completion.d/grub-completion.bash.in
+@@ -264,6 +264,28 @@ have ${__grub_sparc64_setup_program} && \
+ unset __grub_sparc64_setup_program
+ 
+ 
++#
++# grub-get-kernel-settings
++#
++_grub_get_kernel_settings () {
++    local cur
++
++    COMPREPLY=()
++    cur=`_get_cword`
++
++    if [[ "$cur" == -* ]]; then
++        __grubcomp "$(__grub_get_options_from_help)"
++    else
++        # Default complete with a filename
++        _filedir
++    fi
++}
++__grub_get_kernel_settings_program="@grub_get_kernel_settings@"
++have ${__grub_get_kernel_settings_program} && \
++ complete -F _grub_get_kernel_settings -o filenames ${__grub_get_kernel_settings_program}
++unset __grub_get_kernel_settings_program
++
++
+ #
+ # grub-install
+ #
+diff --git a/util/grub-get-kernel-settings.3 b/util/grub-get-kernel-settings.3
+new file mode 100644
+index 00000000000..ba33330e28d
+--- /dev/null
++++ b/util/grub-get-kernel-settings.3
+@@ -0,0 +1,20 @@
++.TH GRUB-GET-KERNEL-SETTINGS 3 "Thu Jun 25 2015"
++.SH NAME
++\fBgrub-get-kernel-settings\fR \(em Evaluate the system's kernel installation settings for use while making a grub configuration file.
++
++.SH SYNOPSIS
++\fBgrub-get-kernel-settings\fR [OPTION]
++
++.SH DESCRIPTION
++\fBgrub-get-kernel-settings\fR reads the kernel installation settings on the host system, and emits a set of grub settings suitable for use when creating a grub configuration file.
++
++.SH OPTIONS
++.TP
++-h, --help
++Display program usage and exit.
++.TP
++-v, --version
++Display the current version.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-get-kernel-settings.in b/util/grub-get-kernel-settings.in
+new file mode 100644
+index 00000000000..7e87dfccc0e
+--- /dev/null
++++ b/util/grub-get-kernel-settings.in
+@@ -0,0 +1,88 @@
++#!/bin/sh
++set -e
++
++# Evaluate new-kernel-pkg's configuration file.
++# Copyright (C) 2016 Free Software Foundation, Inc.
++#
++# GRUB is free software: you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation, either version 3 of the License, or
++# (at your option) any later version.
++#
++# GRUB is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++
++PACKAGE_NAME=@PACKAGE_NAME@
++PACKAGE_VERSION=@PACKAGE_VERSION@
++datadir="@datadir@"
++if [ "x$pkgdatadir" = x ]; then
++    pkgdatadir="${datadir}/@PACKAGE@"
++fi
++
++self=`basename $0`
++
++export TEXTDOMAIN=@PACKAGE@
++export TEXTDOMAINDIR="@localedir@"
++
++. "${pkgdatadir}/grub-mkconfig_lib"
++
++# Usage: usage
++# Print the usage.
++usage () {
++    gettext_printf "Usage: %s [OPTION]\n" "$self"
++    gettext "Evaluate new-kernel-pkg configuration"; echo
++    echo
++    print_option_help "-h, --help" "$(gettext "print this message and exit")"
++    print_option_help "-v, --version" "$(gettext "print the version information and exit")"
++    echo
++}
++
++# Check the arguments.
++while test $# -gt 0
++do
++    option=$1
++    shift
++
++    case "$option" in
++    -h | --help)
++	usage
++	exit 0 ;;
++    -v | --version)
++	echo "$self (${PACKAGE_NAME}) ${PACKAGE_VERSION}"
++	exit 0 ;;
++    -*)
++	gettext_printf "Unrecognized option \`%s'\n" "$option" 1>&2
++	usage
++	exit 1
++	;;
++    # Explicitly ignore non-option arguments, for compatibility.
++    esac
++done
++
++if test -f /etc/sysconfig/kernel ; then
++    . /etc/sysconfig/kernel
++fi
++
++if [ "$MAKEDEBUG" = "yes" ]; then
++    echo GRUB_LINUX_MAKE_DEBUG=true
++    echo export GRUB_LINUX_MAKE_DEBUG
++    echo GRUB_CMDLINE_LINUX_DEBUG=\"systemd.log_level=debug systemd.log_target=kmsg\"
++    echo export GRUB_CMDLINE_LINUX_DEBUG
++    echo GRUB_LINUX_DEBUG_TITLE_POSTFIX=\" with debugging\"
++    echo export GRUB_LINUX_DEBUG_TITLE_POSTFIX
++fi
++if [ "$DEFAULTDEBUG" = "yes" ]; then
++    echo GRUB_DEFAULT_TO_DEBUG=true
++else
++    echo GRUB_DEFAULT_TO_DEBUG=false
++fi
++echo export GRUB_DEFAULT_TO_DEBUG
++if [ "$UPDATEDEFAULT" = "yes" ]; then
++    echo GRUB_UPDATE_DEFAULT_KERNEL=true
++    echo export GRUB_UPDATE_DEFAULT_KERNEL
++fi
+diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in
+index ba14cf6261c..005f093809b 100644
+--- a/util/grub-mkconfig.in
++++ b/util/grub-mkconfig.in
+@@ -45,6 +45,7 @@ grub_probe="${sbindir}/@grub_probe@"
+ grub_file="${bindir}/@grub_file@"
+ grub_editenv="${bindir}/@grub_editenv@"
+ grub_script_check="${bindir}/@grub_script_check@"
++grub_get_kernel_settings="${sbindir}/@grub_get_kernel_settings@"
+ 
+ export TEXTDOMAIN=@PACKAGE@
+ export TEXTDOMAINDIR="@localedir@"
+@@ -158,6 +159,8 @@ if test -f ${sysconfdir}/default/grub ; then
+   . ${sysconfdir}/default/grub
+ fi
+ 
++eval "$("${grub_get_kernel_settings}")" || true
++
+ if [ "x${GRUB_DISABLE_UUID}" = "xtrue" ]; then
+   if [ -z "${GRUB_DISABLE_LINUX_UUID}" ]; then
+     GRUB_DISABLE_LINUX_UUID="true"
+diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
+index 2e59f3b4197..0f3c19e30cc 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -111,7 +111,8 @@ linux_entry ()
+   os="$1"
+   version="$2"
+   type="$3"
+-  args="$4"
++  isdebug="$4"
++  args="$5"
+ 
+   if [ -z "$boot_device_id" ]; then
+       boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")"
+@@ -123,6 +124,9 @@ linux_entry ()
+ 	  quoted="$(echo "$GRUB_ACTUAL_DEFAULT" | grub_quote)"
+ 	  title_correction_code="${title_correction_code}if [ \"x\$default\" = '$quoted' ]; then default='$(echo "$replacement_title" | grub_quote)'; fi;"
+       fi
++      if [ x$isdebug = xdebug ]; then
++	  title="$title${GRUB_LINUX_DEBUG_TITLE_POSTFIX}"
++      fi
+       echo "menuentry '$(echo "$title" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {" | sed "s/^/$submenu_indentation/"
+   else
+       echo "menuentry '$(echo "$os" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-simple-$boot_device_id' {" | sed "s/^/$submenu_indentation/"
+@@ -299,11 +303,15 @@ while [ "x$list" != "x" ] ; do
+   fi
+ 
+   if [ "x$is_top_level" = xtrue ] && [ "x${GRUB_DISABLE_SUBMENU}" != xtrue ]; then
+-    linux_entry "${OS}" "${version}" simple \
++    linux_entry "${OS}" "${version}" simple standard \
+     "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}"
++    if [ "x$GRUB_LINUX_MAKE_DEBUG" = "xtrue" ]; then
++      linux_entry "${OS}" "${version}" simple debug \
++        "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT} ${GRUB_CMDLINE_LINUX_DEBUG}"
++    fi
+ 
+     submenu_indentation="$grub_tab"
+-    
++
+     if [ -z "$boot_device_id" ]; then
+ 	boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")"
+     fi
+@@ -312,10 +320,15 @@ while [ "x$list" != "x" ] ; do
+     is_top_level=false
+   fi
+ 
+-  linux_entry "${OS}" "${version}" advanced \
++  linux_entry "${OS}" "${version}" advanced standard \
+               "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}"
++  if [ "x$GRUB_LINUX_MAKE_DEBUG" = "xtrue" ]; then
++    linux_entry "${OS}" "${version}" advanced debug \
++                "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT} ${GRUB_CMDLINE_LINUX_DEBUG}"
++  fi
++
+   if [ "x${GRUB_DISABLE_RECOVERY}" != "xtrue" ]; then
+-    linux_entry "${OS}" "${version}" recovery \
++    linux_entry "${OS}" "${version}" recovery standard \
+                 "single ${GRUB_CMDLINE_LINUX}"
+   fi
+ 
diff --git a/SOURCES/0052-bz1374141-fix-incorrect-mask-for-ppc64.patch b/SOURCES/0052-bz1374141-fix-incorrect-mask-for-ppc64.patch
new file mode 100644
index 0000000..1bb2658
--- /dev/null
+++ b/SOURCES/0052-bz1374141-fix-incorrect-mask-for-ppc64.patch
@@ -0,0 +1,45 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Masahiro Matsuya <mmatsuya@redhat.com>
+Date: Sat, 29 Oct 2016 08:35:26 +0900
+Subject: [PATCH] bz1374141 fix incorrect mask for ppc64
+
+The netmask configured in firmware is not respected on ppc64 (big endian).
+When 255.255.252.0 is set as netmask in firmware, the following is the value of bootpath string in grub_ieee1275_parse_bootpath().
+
+ /vdevice/l-lan@30000002:speed=auto,duplex=auto,192.168.88.10,,192.168.89.113,192.168.88.1,5,5,255.255.252.0,512
+
+The netmask in this bootpath is no problem, since it's a value specified in firmware. But,
+The value of 'subnet_mask.ipv4' was set with 0xfffffc00, and __builtin_ctz (~grub_le_to_cpu32 (subnet_mask.ipv4)) returned 16 (not 22).
+As a result, 16 was used for netmask wrongly.
+
+1111 1111 1111 1111 1111 1100 0000 0000 # subnet_mask.ipv4 (=0xfffffc00)
+0000 0000 1111 1100 1111 1111 1111 1111 # grub_le_to_cpu32 (subnet_mask.ipv4)
+1111 1111 0000 0011 0000 0000 0000 0000 # ~grub_le_to_cpu32 (subnet_mask.ipv4)
+
+And, the count of zero with __builtin_ctz can be 16.
+This patch changes it as below.
+
+1111 1111 1111 1111 1111 1100 0000 0000 # subnet_mask.ipv4 (=0xfffffc00)
+0000 0000 1111 1100 1111 1111 1111 1111 # grub_le_to_cpu32 (subnet_mask.ipv4)
+1111 1111 1111 1111 1111 1100 0000 0000 # grub_swap_bytes32(grub_le_to_cpu32 (subnet_mask.ipv4))
+0000 0000 0000 0000 0000 0011 1111 1111 # ~grub_swap_bytes32(grub_le_to_cpu32 (subnet_mask.ipv4))
+
+The count of zero with __builtin_clz can be 22. (clz counts the number of one bits preceding the most significant zero bit)
+---
+ grub-core/net/drivers/ieee1275/ofnet.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/grub-core/net/drivers/ieee1275/ofnet.c b/grub-core/net/drivers/ieee1275/ofnet.c
+index ac4e62a95c9..3860b6f78d8 100644
+--- a/grub-core/net/drivers/ieee1275/ofnet.c
++++ b/grub-core/net/drivers/ieee1275/ofnet.c
+@@ -220,8 +220,7 @@ grub_ieee1275_parse_bootpath (const char *devpath, char *bootpath,
+                                  flags);
+       inter->vlantag = vlantag;
+       grub_net_add_ipv4_local (inter,
+-                          __builtin_ctz (~grub_le_to_cpu32 (subnet_mask.ipv4)));
+-
++                          __builtin_clz (~grub_swap_bytes32(grub_le_to_cpu32 (subnet_mask.ipv4))));
+     }
+ 
+   if (gateway_addr.ipv4 != 0)
diff --git a/SOURCES/0053-Make-grub_fatal-also-backtrace.patch b/SOURCES/0053-Make-grub_fatal-also-backtrace.patch
new file mode 100644
index 0000000..3534b05
--- /dev/null
+++ b/SOURCES/0053-Make-grub_fatal-also-backtrace.patch
@@ -0,0 +1,172 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Wed, 27 Jan 2016 09:22:42 -0500
+Subject: [PATCH] Make grub_fatal() also backtrace.
+
+---
+ grub-core/Makefile.core.def     |  3 ++
+ grub-core/kern/misc.c           |  8 +++++-
+ grub-core/lib/arm64/backtrace.c | 62 +++++++++++++++++++++++++++++++++++++++++
+ grub-core/lib/backtrace.c       |  2 ++
+ grub-core/lib/i386/backtrace.c  | 14 +++++++++-
+ 5 files changed, 87 insertions(+), 2 deletions(-)
+ create mode 100644 grub-core/lib/arm64/backtrace.c
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index c15e91943b9..058c88ac3af 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -188,6 +188,9 @@ kernel = {
+ 
+   softdiv = lib/division.c;
+ 
++  x86 = lib/i386/backtrace.c;
++  x86 = lib/backtrace.c;
++
+   i386 = kern/i386/dl.c;
+   i386_xen = kern/i386/dl.c;
+   i386_xen_pvh = kern/i386/dl.c;
+diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
+index 63b586d09cb..a3e215155bd 100644
+--- a/grub-core/kern/misc.c
++++ b/grub-core/kern/misc.c
+@@ -24,6 +24,7 @@
+ #include <grub/term.h>
+ #include <grub/env.h>
+ #include <grub/i18n.h>
++#include <grub/backtrace.h>
+ 
+ union printf_arg
+ {
+@@ -1199,8 +1200,13 @@ grub_printf_fmt_check (const char *fmt, const char *fmt_expected)
+ static void __attribute__ ((noreturn))
+ grub_abort (void)
+ {
++#ifndef GRUB_UTIL
++#if defined(__i386__) || defined(__x86_64__)
++  grub_backtrace();
++#endif
++#endif
+   grub_printf ("\nAborted.");
+-  
++
+ #ifndef GRUB_UTIL
+   if (grub_term_inputs)
+ #endif
+diff --git a/grub-core/lib/arm64/backtrace.c b/grub-core/lib/arm64/backtrace.c
+new file mode 100644
+index 00000000000..1079b5380e1
+--- /dev/null
++++ b/grub-core/lib/arm64/backtrace.c
+@@ -0,0 +1,62 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2009  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/misc.h>
++#include <grub/command.h>
++#include <grub/err.h>
++#include <grub/dl.h>
++#include <grub/mm.h>
++#include <grub/term.h>
++#include <grub/backtrace.h>
++
++#define MAX_STACK_FRAME 102400
++
++void
++grub_backtrace_pointer (int frame)
++{
++  while (1)
++    {
++      void *lp = __builtin_return_address (frame);
++      if (!lp)
++	break;
++
++      lp = __builtin_extract_return_addr (lp);
++
++      grub_printf ("%p: ", lp);
++      grub_backtrace_print_address (lp);
++      grub_printf (" (");
++      for (i = 0; i < 2; i++)
++	grub_printf ("%p,", ((void **)ptr) [i + 2]);
++      grub_printf ("%p)\n", ((void **)ptr) [i + 2]);
++      nptr = *(void **)ptr;
++      if (nptr < ptr || (void **) nptr - (void **) ptr > MAX_STACK_FRAME
++	  || nptr == ptr)
++	{
++	  grub_printf ("Invalid stack frame at %p (%p)\n", ptr, nptr);
++	  break;
++	}
++      ptr = nptr;
++    }
++}
++
++void
++grub_backtrace (void)
++{
++  grub_backtrace_pointer (1);
++}
++
+diff --git a/grub-core/lib/backtrace.c b/grub-core/lib/backtrace.c
+index 825a8800e25..c0ad6ab8be1 100644
+--- a/grub-core/lib/backtrace.c
++++ b/grub-core/lib/backtrace.c
+@@ -29,6 +29,7 @@ GRUB_MOD_LICENSE ("GPLv3+");
+ void
+ grub_backtrace_print_address (void *addr)
+ {
++#ifndef GRUB_UTIL
+   grub_dl_t mod;
+ 
+   FOR_DL_MODULES (mod)
+@@ -44,6 +45,7 @@ grub_backtrace_print_address (void *addr)
+ 	}
+   }
+ 
++#endif
+   grub_printf ("%p", addr);
+ }
+ 
+diff --git a/grub-core/lib/i386/backtrace.c b/grub-core/lib/i386/backtrace.c
+index c3e03c7275c..c67273db3ae 100644
+--- a/grub-core/lib/i386/backtrace.c
++++ b/grub-core/lib/i386/backtrace.c
+@@ -15,11 +15,23 @@
+  *  You should have received a copy of the GNU General Public License
+  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+  */
++#include <config.h>
++#ifdef GRUB_UTIL
++#define REALLY_GRUB_UTIL GRUB_UTIL
++#undef GRUB_UTIL
++#endif
++
++#include <grub/symbol.h>
++#include <grub/dl.h>
++
++#ifdef REALLY_GRUB_UTIL
++#define GRUB_UTIL REALLY_GRUB_UTIL
++#undef REALLY_GRUB_UTIL
++#endif
+ 
+ #include <grub/misc.h>
+ #include <grub/command.h>
+ #include <grub/err.h>
+-#include <grub/dl.h>
+ #include <grub/mm.h>
+ #include <grub/term.h>
+ #include <grub/backtrace.h>
diff --git a/SOURCES/0054-Fix-up-some-man-pages-rpmdiff-noticed.patch b/SOURCES/0054-Fix-up-some-man-pages-rpmdiff-noticed.patch
new file mode 100644
index 0000000..8016a62
--- /dev/null
+++ b/SOURCES/0054-Fix-up-some-man-pages-rpmdiff-noticed.patch
@@ -0,0 +1,150 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 23 Sep 2014 09:58:49 -0400
+Subject: [PATCH] Fix up some man pages rpmdiff noticed.
+
+---
+ configure.ac             |  2 ++
+ util/grub-macbless.8     | 26 +++++++++++++++++++
+ util/grub-mkimage.1      |  2 +-
+ util/grub-syslinux2cfg.1 | 65 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 94 insertions(+), 1 deletion(-)
+ create mode 100644 util/grub-macbless.8
+ create mode 100644 util/grub-syslinux2cfg.1
+
+diff --git a/configure.ac b/configure.ac
+index ed31ea457d2..537ed411469 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -87,6 +87,7 @@ grub_TRANSFORM([grub-get-kernel-settings.3])
+ grub_TRANSFORM([grub-glue-efi.3])
+ grub_TRANSFORM([grub-install.1])
+ grub_TRANSFORM([grub-kbdcomp.3])
++grub_TRANSFORM([grub-macbless.8])
+ grub_TRANSFORM([grub-menulst2cfg.1])
+ grub_TRANSFORM([grub-mkconfig.1])
+ grub_TRANSFORM([grub-mkfont.3])
+@@ -105,6 +106,7 @@ grub_TRANSFORM([grub-render-label.3])
+ grub_TRANSFORM([grub-script-check.3])
+ grub_TRANSFORM([grub-set-default.1])
+ grub_TRANSFORM([grub-sparc64-setup.3])
++grub_TRANSFORM([grub-syslinux2cfg.1])
+ 
+ # Optimization flag.  Allow user to override.
+ if test "x$TARGET_CFLAGS" = x; then
+diff --git a/util/grub-macbless.8 b/util/grub-macbless.8
+new file mode 100644
+index 00000000000..ae842f3a606
+--- /dev/null
++++ b/util/grub-macbless.8
+@@ -0,0 +1,26 @@
++.TH GRUB-MACBLESS 1 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-macbless\fR \(em Mac-style bless utility for HFS or HFS+
++
++.SH SYNOPSIS
++\fBgrub-macbless\fR [-p | --ppc] [-v | --verbose] [-x | --x86] \fIFILE\fR
++
++.SH DESCRIPTION
++\fBgrub-mkimage\fR blesses a file on an HFS or HFS+ file system, so that it
++can be used to boot a Mac.
++
++.SH OPTIONS
++.TP
++--ppc
++Bless the file for use on PPC-based Macs.
++
++.TP
++--verbose
++Print verbose messages.
++
++.TP
++--x86
++Bless the file for use on x86-based Macs.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-mkimage.1 b/util/grub-mkimage.1
+index 4dea4f54597..0eaaafe505b 100644
+--- a/util/grub-mkimage.1
++++ b/util/grub-mkimage.1
+@@ -17,7 +17,7 @@
+ [-v | --verbose] \fIMODULES\fR
+ 
+ .SH DESCRIPTION
+-\fBgrub-mkimage\fI builds a bootable image of GRUB.
++\fBgrub-mkimage\fR builds a bootable image of GRUB.
+ 
+ .SH OPTIONS
+ .TP
+diff --git a/util/grub-syslinux2cfg.1 b/util/grub-syslinux2cfg.1
+new file mode 100644
+index 00000000000..85309482718
+--- /dev/null
++++ b/util/grub-syslinux2cfg.1
+@@ -0,0 +1,65 @@
++.TH GRUB-SYSLINUX2CFG 1 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-syslinux2cfg\fR \(em Transform a syslinux config file into a GRUB config.
++
++.SH SYNOPSIS
++\fBgrub-syslinux2cfg\fR [-c | --cwd=\fRDIR\fI] [-r | --root=\fIDIR\fR] [-v | --verbose]
++.RE
++.RS 25
++[-t | --target-root=\fIDIR\fR] [-T | --target-cwd=\fIDIR\fR]
++.RE
++.RS 25
++[-o | --output=\fIFILE\fR] [[-i | --isolinux] |
++.RE
++.RS 46
++ [-s | --syslinux] |
++.RE
++.RS 46
++ [-p | --pxelinux]] \fIFILE\fR
++
++.SH DESCRIPTION
++\fBgrub-syslinux2cfg\fR builds a GRUB configuration file out of an existing
++syslinux configuration file.
++
++.SH OPTIONS
++.TP
++--cwd=\fIDIR\fR
++Set \fIDIR\fR as syslinux's working directory.  The default is to use the
++parent directory of the input file.
++
++.TP
++--root=\fIDIR\fR
++Set \fIDIR\fR as the root directory of the syslinux disk.  The default value
++is "/".
++
++.TP
++--verbose
++Print verbose messages.
++
++.TP
++--target-root=\fIDIR\fR
++Root directory as it will be seen at runtime.  The default value is "/".
++
++.TP
++--target-cwd=\fIDIR\fR
++Working directory of syslinux as it will be seen at runtime.  The default
++value is the parent directory of the input file.
++
++.TP
++--output=\fIFILE\fR
++Write the new config file to \fIFILE\fR.  The default value is standard output.
++
++.TP
++--isolinux
++Assume that the input file is an isolinux configuration file.
++
++.TP
++--pxelinux
++Assume that the input file is a pxelinux configuration file.
++
++.TP
++--syslinux
++Assume that the input file is a syslinux configuration file.
++
++.SH SEE ALSO
++.BR "info grub"
diff --git a/SOURCES/0055-Make-our-info-pages-say-grub2-where-appropriate.patch b/SOURCES/0055-Make-our-info-pages-say-grub2-where-appropriate.patch
new file mode 100644
index 0000000..be28efd
--- /dev/null
+++ b/SOURCES/0055-Make-our-info-pages-say-grub2-where-appropriate.patch
@@ -0,0 +1,1008 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 9 Jul 2019 12:59:58 +0200
+Subject: [PATCH] Make our info pages say "grub2" where appropriate.
+
+This needs to be hooked up to --program-transform=, but I haven't had
+time.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ docs/grub-dev.texi |   4 +-
+ docs/grub.texi     | 321 ++++++++++++++++++++++++++++-------------------------
+ 2 files changed, 171 insertions(+), 154 deletions(-)
+
+diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
+index 6c629a23e2d..19f708ee662 100644
+--- a/docs/grub-dev.texi
++++ b/docs/grub-dev.texi
+@@ -1,7 +1,7 @@
+ \input texinfo
+ @c -*-texinfo-*-
+ @c %**start of header
+-@setfilename grub-dev.info
++@setfilename grub2-dev.info
+ @include version-dev.texi
+ @settitle GNU GRUB Developers Manual @value{VERSION}
+ @c Unify all our little indices for now.
+@@ -32,7 +32,7 @@ Invariant Sections.
+ 
+ @dircategory Kernel
+ @direntry
+-* grub-dev: (grub-dev).                 The GRand Unified Bootloader Dev
++* grub2-dev: (grub2-dev).                 The GRand Unified Bootloader Dev
+ @end direntry
+ 
+ @setchapternewpage odd
+diff --git a/docs/grub.texi b/docs/grub.texi
+index 69f08d289f9..0615d0ed97e 100644
+--- a/docs/grub.texi
++++ b/docs/grub.texi
+@@ -1,7 +1,7 @@
+ \input texinfo
+ @c -*-texinfo-*-
+ @c %**start of header
+-@setfilename grub.info
++@setfilename grub2.info
+ @include version.texi
+ @settitle GNU GRUB Manual @value{VERSION}
+ @c Unify all our little indices for now.
+@@ -32,15 +32,15 @@ Invariant Sections.
+ 
+ @dircategory Kernel
+ @direntry
+-* GRUB: (grub).                 The GRand Unified Bootloader
+-* grub-install: (grub)Invoking grub-install.    Install GRUB on your drive
+-* grub-mkconfig: (grub)Invoking grub-mkconfig.  Generate GRUB configuration
+-* grub-mkpasswd-pbkdf2: (grub)Invoking grub-mkpasswd-pbkdf2.
+-* grub-mkrelpath: (grub)Invoking grub-mkrelpath.
+-* grub-mkrescue: (grub)Invoking grub-mkrescue.  Make a GRUB rescue image
+-* grub-mount: (grub)Invoking grub-mount.        Mount a file system using GRUB
+-* grub-probe: (grub)Invoking grub-probe.        Probe device information
+-* grub-script-check: (grub)Invoking grub-script-check.
++* GRUB2: (grub2).                 The GRand Unified Bootloader
++* grub2-install: (grub2)Invoking grub2-install.    Install GRUB on your drive
++* grub2-mkconfig: (grub2)Invoking grub2-mkconfig.  Generate GRUB configuration
++* grub2-mkpasswd-pbkdf2: (grub2)Invoking grub2-mkpasswd-pbkdf2.
++* grub2-mkrelpath: (grub2)Invoking grub2-mkrelpath.
++* grub2-mkrescue: (grub2)Invoking grub2-mkrescue.  Make a GRUB rescue image
++* grub2-mount: (grub2)Invoking grub2-mount.        Mount a file system using GRUB
++* grub2-probe: (grub2)Invoking grub2-probe.        Probe device information
++* grub2-script-check: (grub2)Invoking grub2-script-check.
+ @end direntry
+ 
+ @setchapternewpage odd
+@@ -103,15 +103,15 @@ This edition documents version @value{VERSION}.
+ * Platform-specific operations:: Platform-specific operations
+ * Supported kernels::           The list of supported kernels
+ * Troubleshooting::             Error messages produced by GRUB
+-* Invoking grub-install::       How to use the GRUB installer
+-* Invoking grub-mkconfig::      Generate a GRUB configuration file
+-* Invoking grub-mkpasswd-pbkdf2::
++* Invoking grub2-install::       How to use the GRUB installer
++* Invoking grub2-mkconfig::      Generate a GRUB configuration file
++* Invoking grub2-mkpasswd-pbkdf2::
+                                 Generate GRUB password hashes
+-* Invoking grub-mkrelpath::     Make system path relative to its root
+-* Invoking grub-mkrescue::      Make a GRUB rescue image
+-* Invoking grub-mount::         Mount a file system using GRUB
+-* Invoking grub-probe::         Probe device information for GRUB
+-* Invoking grub-script-check::  Check GRUB script file for syntax errors
++* Invoking grub2-mkrelpath::     Make system path relative to its root
++* Invoking grub2-mkrescue::      Make a GRUB rescue image
++* Invoking grub2-mount::         Mount a file system using GRUB
++* Invoking grub2-probe::         Probe device information for GRUB
++* Invoking grub2-script-check::  Check GRUB script file for syntax errors
+ * Obtaining and Building GRUB:: How to obtain and build GRUB
+ * Reporting bugs::              Where you should send a bug report
+ * Future::                      Some future plans on GRUB
+@@ -230,7 +230,7 @@ surprising.
+ 
+ @item
+ @file{grub.cfg} is typically automatically generated by
+-@command{grub-mkconfig} (@pxref{Simple configuration}).  This makes it
++@command{grub2-mkconfig} (@pxref{Simple configuration}).  This makes it
+ easier to handle versioned kernel upgrades.
+ 
+ @item
+@@ -244,7 +244,7 @@ scripting language: variables, conditionals, and loops are available.
+ @item
+ A small amount of persistent storage is available across reboots, using the
+ @command{save_env} and @command{load_env} commands in GRUB and the
+-@command{grub-editenv} utility.  This is not available in all configurations
++@command{grub2-editenv} utility.  This is not available in all configurations
+ (@pxref{Environment block}).
+ 
+ @item
+@@ -549,7 +549,7 @@ On OS which have device nodes similar to Unix-like OS GRUB tools use the
+ OS name. E.g. for GNU/Linux:
+ 
+ @example
+-# @kbd{grub-install /dev/sda}
++# @kbd{grub2-install /dev/sda}
+ @end example
+ 
+ On AROS we use another syntax. For volumes:
+@@ -572,7 +572,7 @@ For disks we use syntax:
+ E.g.
+ 
+ @example
+-# @kbd{grub-install //:ata.device/0/0}
++# @kbd{grub2-install //:ata.device/0/0}
+ @end example
+ 
+ On Windows we use UNC path. For volumes it's typically
+@@ -599,7 +599,7 @@ For disks it's
+ E.g.
+ 
+ @example
+-# @kbd{grub-install \\?\PhysicalDrive0}
++# @kbd{grub2-install \\?\PhysicalDrive0}
+ @end example
+ 
+ Beware that you may need to further escape the backslashes depending on your
+@@ -609,7 +609,7 @@ When compiled with cygwin support then cygwin drive names are automatically
+ when needed. E.g.
+ 
+ @example
+-# @kbd{grub-install /dev/sda}
++# @kbd{grub2-install /dev/sda}
+ @end example
+ 
+ @node Installation
+@@ -622,7 +622,7 @@ from the source tarball, or as a package for your OS.
+ 
+ After you have done that, you need to install the boot loader on a
+ drive (floppy or hard disk) by using the utility
+-@command{grub-install} (@pxref{Invoking grub-install}) on a UNIX-like OS.
++@command{grub2-install} (@pxref{Invoking grub2-install}) on a UNIX-like OS.
+ 
+ GRUB comes with boot images, which are normally put in the directory
+ @file{/usr/lib/grub/<cpu>-<platform>} (for BIOS-based machines
+@@ -633,22 +633,22 @@ loader needs to find them (usually @file{/boot}) will be called
+ the @dfn{boot directory}.
+ 
+ @menu
+-* Installing GRUB using grub-install::
++* Installing GRUB using grub2-install::
+ * Making a GRUB bootable CD-ROM::
+ * Device map::
+ * BIOS installation::
+ @end menu
+ 
+ 
+-@node Installing GRUB using grub-install
+-@section Installing GRUB using grub-install
++@node Installing GRUB using grub2-install
++@section Installing GRUB using grub2-install
+ 
+ For information on where GRUB should be installed on PC BIOS platforms,
+ @pxref{BIOS installation}.
+ 
+ In order to install GRUB under a UNIX-like OS (such
+-as @sc{gnu}), invoke the program @command{grub-install} (@pxref{Invoking
+-grub-install}) as the superuser (@dfn{root}).
++as @sc{gnu}), invoke the program @command{grub2-install} (@pxref{Invoking
++grub2-install}) as the superuser (@dfn{root}).
+ 
+ The usage is basically very simple. You only need to specify one
+ argument to the program, namely, where to install the boot loader. The
+@@ -657,13 +657,13 @@ For example, under Linux the following will install GRUB into the MBR
+ of the first IDE disk:
+ 
+ @example
+-# @kbd{grub-install /dev/sda}
++# @kbd{grub2-install /dev/sda}
+ @end example
+ 
+ Likewise, under GNU/Hurd, this has the same effect:
+ 
+ @example
+-# @kbd{grub-install /dev/hd0}
++# @kbd{grub2-install /dev/hd0}
+ @end example
+ 
+ But all the above examples assume that GRUB should put images under
+@@ -677,7 +677,7 @@ boot floppy with a filesystem. Here is an example:
+ # @kbd{mke2fs /dev/fd0}
+ # @kbd{mount -t ext2 /dev/fd0 /mnt}
+ # @kbd{mkdir /mnt/boot}
+-# @kbd{grub-install --boot-directory=/mnt/boot /dev/fd0}
++# @kbd{grub2-install --boot-directory=/mnt/boot /dev/fd0}
+ # @kbd{umount /mnt}
+ @end group
+ @end example
+@@ -689,30 +689,37 @@ floppy instead of exposing the USB drive as a hard disk (they call it
+ @example
+ # @kbd{losetup /dev/loop0 /dev/sdb1}
+ # @kbd{mount /dev/loop0 /mnt/usb}
+-# @kbd{grub-install --boot-directory=/mnt/usb/bugbios --force --allow-floppy /dev/loop0}
++# @kbd{grub2-install --boot-directory=/mnt/usb/bugbios --force --allow-floppy /dev/loop0}
+ @end example
+ 
+ This install doesn't conflict with standard install as long as they are in
+ separate directories.
+ 
++Note that @command{grub2-install} is actually just a shell script and the
++real task is done by other tools such as @command{grub2-mkimage}. Therefore,
++you may run those commands directly to install GRUB, without using
++@command{grub2-install}. Don't do that, however, unless you are very familiar
++with the internals of GRUB. Installing a boot loader on a running OS may be
++extremely dangerous.
++
+ On EFI systems for fixed disk install you have to mount EFI System Partition.
+ If you mount it at @file{/boot/efi} then you don't need any special arguments:
+ 
+ @example
+-# @kbd{grub-install}
++# @kbd{grub2-install}
+ @end example
+ 
+ Otherwise you need to specify where your EFI System partition is mounted:
+ 
+ @example
+-# @kbd{grub-install --efi-directory=/mnt/efi}
++# @kbd{grub2-install --efi-directory=/mnt/efi}
+ @end example
+ 
+ For removable installs you have to use @option{--removable} and specify both
+ @option{--boot-directory} and @option{--efi-directory}:
+ 
+ @example
+-# @kbd{grub-install --efi-directory=/mnt/usb --boot-directory=/mnt/usb/boot --removable}
++# @kbd{grub2-install --efi-directory=/mnt/usb --boot-directory=/mnt/usb/boot --removable}
+ @end example
+ 
+ @node Making a GRUB bootable CD-ROM
+@@ -732,10 +739,10 @@ usually also need to include a configuration file @file{grub.cfg} and some
+ other GRUB modules.
+ 
+ To make a simple generic GRUB rescue CD, you can use the
+-@command{grub-mkrescue} program (@pxref{Invoking grub-mkrescue}):
++@command{grub2-mkrescue} program (@pxref{Invoking grub2-mkrescue}):
+ 
+ @example
+-$ @kbd{grub-mkrescue -o grub.iso}
++$ @kbd{grub2-mkrescue -o grub.iso}
+ @end example
+ 
+ You will often need to include other files in your image. To do this, first
+@@ -758,7 +765,7 @@ directory @file{iso/}.
+ Finally, make the image:
+ 
+ @example
+-$ @kbd{grub-mkrescue -o grub.iso iso}
++$ @kbd{grub2-mkrescue -o grub.iso iso}
+ @end example
+ 
+ This produces a file named @file{grub.iso}, which then can be burned
+@@ -774,7 +781,7 @@ storage devices.
+ @node Device map
+ @section The map between BIOS drives and OS devices
+ 
+-If the device map file exists, the GRUB utilities (@command{grub-probe},
++If the device map file exists, the GRUB utilities (@command{grub2-probe},
+ etc.) read it to map BIOS drives to OS devices.  This file consists of lines
+ like this:
+ 
+@@ -1254,23 +1261,23 @@ need to write the whole thing by hand.
+ @node Simple configuration
+ @section Simple configuration handling
+ 
+-The program @command{grub-mkconfig} (@pxref{Invoking grub-mkconfig})
++The program @command{grub2-mkconfig} (@pxref{Invoking grub2-mkconfig})
+ generates @file{grub.cfg} files suitable for most cases.  It is suitable for
+ use when upgrading a distribution, and will discover available kernels and
+ attempt to generate menu entries for them.
+ 
+-@command{grub-mkconfig} does have some limitations.  While adding extra
++@command{grub2-mkconfig} does have some limitations.  While adding extra
+ custom menu entries to the end of the list can be done by editing
+-@file{/etc/grub.d/40_custom} or creating @file{/boot/grub/custom.cfg},
++@file{/etc/grub.d/40_custom} or creating @file{/boot/grub2/custom.cfg},
+ changing the order of menu entries or changing their titles may require
+ making complex changes to shell scripts stored in @file{/etc/grub.d/}.  This
+ may be improved in the future.  In the meantime, those who feel that it
+ would be easier to write @file{grub.cfg} directly are encouraged to do so
+ (@pxref{Booting}, and @ref{Shell-like scripting}), and to disable any system
+-provided by their distribution to automatically run @command{grub-mkconfig}.
++provided by their distribution to automatically run @command{grub2-mkconfig}.
+ 
+ The file @file{/etc/default/grub} controls the operation of
+-@command{grub-mkconfig}.  It is sourced by a shell script, and so must be
++@command{grub2-mkconfig}.  It is sourced by a shell script, and so must be
+ valid POSIX shell input; normally, it will just be a sequence of
+ @samp{KEY=value} lines, but if the value contains spaces or other special
+ characters then it must be quoted.  For example:
+@@ -1308,7 +1315,7 @@ works it's not recommended since titles often contain unstable device names
+ and may be translated
+ 
+ If you set this to @samp{saved}, then the default menu entry will be that
+-saved by @samp{GRUB_SAVEDEFAULT} or @command{grub-set-default}.  This relies on
++saved by @samp{GRUB_SAVEDEFAULT} or @command{grub2-set-default}.  This relies on
+ the environment block, which may not be available in all situations
+ (@pxref{Environment block}).
+ 
+@@ -1319,7 +1326,7 @@ If this option is set to @samp{true}, then, when an entry is selected, save
+ it as a new default entry for use by future runs of GRUB.  This is only
+ useful if @samp{GRUB_DEFAULT=saved}; it is a separate option because
+ @samp{GRUB_DEFAULT=saved} is useful without this option, in conjunction with
+-@command{grub-set-default}.  Unset by default.
++@command{grub2-set-default}.  Unset by default.
+ This option relies on the environment block, which may not be available in
+ all situations (@pxref{Environment block}).
+ 
+@@ -1449,7 +1456,7 @@ intel-uc.img intel-ucode.img amd-uc.img amd-ucode.img early_ucode.cpio microcode
+ @end example
+ 
+ @item GRUB_DISABLE_LINUX_UUID
+-Normally, @command{grub-mkconfig} will generate menu entries that use
++Normally, @command{grub2-mkconfig} will generate menu entries that use
+ universally-unique identifiers (UUIDs) to identify the root filesystem to
+ the Linux kernel, using a @samp{root=UUID=...} kernel parameter.  This is
+ usually more reliable, but in some cases it may not be appropriate.  To
+@@ -1471,7 +1478,7 @@ If this option is set to @samp{true}, disable the generation of recovery
+ mode menu entries.
+ 
+ @item GRUB_DISABLE_UUID
+-Normally, @command{grub-mkconfig} will generate menu entries that use
++Normally, @command{grub2-mkconfig} will generate menu entries that use
+ universally-unique identifiers (UUIDs) to identify various filesystems to
+ search for files.  This is usually more reliable, but in some cases it may
+ not be appropriate.  To disable this use of UUIDs, set this option to
+@@ -1482,12 +1489,12 @@ not be appropriate.  To disable this use of UUIDs, set this option to
+ @item GRUB_VIDEO_BACKEND
+ If graphical video support is required, either because the @samp{gfxterm}
+ graphical terminal is in use or because @samp{GRUB_GFXPAYLOAD_LINUX} is set,
+-then @command{grub-mkconfig} will normally load all available GRUB video
++then @command{grub2-mkconfig} will normally load all available GRUB video
+ drivers and use the one most appropriate for your hardware.  If you need to
+ override this for some reason, then you can set this option.
+ 
+-After @command{grub-install} has been run, the available video drivers are
+-listed in @file{/boot/grub/video.lst}.
++After @command{grub2-install} has been run, the available video drivers are
++listed in @file{/boot/grub2/video.lst}.
+ 
+ @item GRUB_GFXMODE
+ Set the resolution used on the @samp{gfxterm} graphical terminal.  Note that
+@@ -1519,7 +1526,7 @@ boot sequence.  If you have problems, set this option to @samp{text} and
+ GRUB will tell Linux to boot in normal text mode.
+ 
+ @item GRUB_DISABLE_OS_PROBER
+-Normally, @command{grub-mkconfig} will try to use the external
++Normally, @command{grub2-mkconfig} will try to use the external
+ @command{os-prober} program, if installed, to discover other operating
+ systems installed on the same system and generate appropriate menu entries
+ for them.  Set this option to @samp{true} to disable this.
+@@ -1529,7 +1536,7 @@ List of space-separated FS UUIDs of filesystems to be ignored from os-prober
+ output. For efi chainloaders it's <UUID>@@<EFI FILE>
+ 
+ @item GRUB_DISABLE_SUBMENU
+-Normally, @command{grub-mkconfig} will generate top level menu entry for
++Normally, @command{grub2-mkconfig} will generate top level menu entry for
+ the kernel with highest version number and put all other found kernels
+ or alternative menu entries for recovery mode in submenu. For entries returned
+ by @command{os-prober} first entry will be put on top level and all others
+@@ -1537,11 +1544,11 @@ in submenu. If this option is set to @samp{true}, flat menu with all entries
+ on top level will be generated instead. Changing this option will require
+ changing existing values of @samp{GRUB_DEFAULT}, @samp{fallback} (@pxref{fallback})
+ and @samp{default} (@pxref{default}) environment variables as well as saved
+-default entry using @command{grub-set-default} and value used with
+-@command{grub-reboot}.
++default entry using @command{grub2-set-default} and value used with
++@command{grub2-reboot}.
+ 
+ @item GRUB_ENABLE_CRYPTODISK
+-If set to @samp{y}, @command{grub-mkconfig} and @command{grub-install} will
++If set to @samp{y}, @command{grub2-mkconfig} and @command{grub2-install} will
+ check for encrypted disks and generate additional commands needed to access
+ them during boot.  Note that in this case unattended boot is not possible
+ because GRUB will wait for passphrase to unlock encrypted container.
+@@ -1600,7 +1607,7 @@ confusing @samp{GRUB_TIMEOUT_STYLE=countdown} or
+ 
+ @end table
+ 
+-For more detailed customisation of @command{grub-mkconfig}'s output, you may
++For more detailed customisation of @command{grub2-mkconfig}'s output, you may
+ edit the scripts in @file{/etc/grub.d} directly.
+ @file{/etc/grub.d/40_custom} is particularly useful for adding entire custom
+ menu entries; simply type the menu entries you want to add at the end of
+@@ -1862,7 +1869,7 @@ images as well.
+ Mount this partition on/mnt/boot and disable GRUB in all OSes and manually
+ install self-compiled latest GRUB with:
+ 
+-@code{grub-install --boot-directory=/mnt/boot /dev/sda}
++@code{grub2-install --boot-directory=/mnt/boot /dev/sda}
+ 
+ In all the OSes install GRUB tools but disable installing GRUB in bootsector,
+ so you'll have menu.lst and grub.cfg available for use. Also disable os-prober
+@@ -1872,20 +1879,20 @@ use by setting:
+ 
+ in /etc/default/grub
+ 
+-Then write a grub.cfg (/mnt/boot/grub/grub.cfg):
++Then write a grub.cfg (/mnt/boot/grub2/grub.cfg):
+ 
+ @example
+ 
+ menuentry "OS using grub2" @{
+    insmod xfs
+    search --set=root --label OS1 --hint hd0,msdos8
+-   configfile /boot/grub/grub.cfg
++   configfile /boot/grub2/grub.cfg
+ @}
+ 
+ menuentry "OS using grub2-legacy" @{
+    insmod ext2
+    search --set=root --label OS2 --hint hd0,msdos6
+-   legacy_configfile /boot/grub/menu.lst
++   legacy_configfile /boot/grub2/menu.lst
+ @}
+ 
+ menuentry "Windows XP" @{
+@@ -1948,15 +1955,15 @@ GRUB supports embedding a configuration file directly into the core image,
+ so that it is loaded before entering normal mode.  This is useful, for
+ example, when it is not straightforward to find the real configuration file,
+ or when you need to debug problems with loading that file.
+-@command{grub-install} uses this feature when it is not using BIOS disk
++@command{grub2-install} uses this feature when it is not using BIOS disk
+ functions or when installing to a different disk from the one containing
+ @file{/boot/grub}, in which case it needs to use the @command{search}
+ command (@pxref{search}) to find @file{/boot/grub}.
+ 
+ To embed a configuration file, use the @option{-c} option to
+-@command{grub-mkimage}.  The file is copied into the core image, so it may
++@command{grub2-mkimage}.  The file is copied into the core image, so it may
+ reside anywhere on the file system, and may be removed after running
+-@command{grub-mkimage}.
++@command{grub2-mkimage}.
+ 
+ After the embedded configuration file (if any) is executed, GRUB will load
+ the @samp{normal} module (@pxref{normal}), which will then read the real
+@@ -1991,13 +1998,13 @@ included in the core image:
+ @example
+ @group
+ search.fs_label grub root
+-if [ -e /boot/grub/example/test1.cfg ]; then
++if [ -e /boot/grub2/example/test1.cfg ]; then
+     set prefix=($root)/boot/grub
+-    configfile /boot/grub/example/test1.cfg
++    configfile /boot/grub2/example/test1.cfg
+ else
+-    if [ -e /boot/grub/example/test2.cfg ]; then
++    if [ -e /boot/grub2/example/test2.cfg ]; then
+         set prefix=($root)/boot/grub
+-        configfile /boot/grub/example/test2.cfg
++        configfile /boot/grub2/example/test2.cfg
+     else
+         echo "Could not find an example configuration file!"
+     fi
+@@ -2521,7 +2528,7 @@ grub-mknetdir --net-directory=/srv/tftp --subdir=/boot/grub -d /usr/lib/grub/i38
+ @end group
+ @end example
+ 
+-Then follow instructions printed out by grub-mknetdir on configuring your DHCP
++Then follow instructions printed out by grub2-mknetdir on configuring your DHCP
+ server.
+ 
+ The grub.cfg file is placed in the same directory as the path output by
+@@ -2715,7 +2722,7 @@ team are:
+ @end table
+ 
+ To take full advantage of this function, install GRUB into the MBR
+-(@pxref{Installing GRUB using grub-install}).
++(@pxref{Installing GRUB using grub2-install}).
+ 
+ If you have a laptop which has a similar feature and not in the above list
+ could you figure your address and contribute?
+@@ -2776,7 +2783,7 @@ bytes.
+ The sole function of @file{boot.img} is to read the first sector of the core
+ image from a local disk and jump to it.  Because of the size restriction,
+ @file{boot.img} cannot understand any file system structure, so
+-@command{grub-install} hardcodes the location of the first sector of the
++@command{grub2-install} hardcodes the location of the first sector of the
+ core image into @file{boot.img} when installing GRUB.
+ 
+ @item diskboot.img
+@@ -2806,7 +2813,7 @@ images.
+ 
+ @item core.img
+ This is the core image of GRUB.  It is built dynamically from the kernel
+-image and an arbitrary list of modules by the @command{grub-mkimage}
++image and an arbitrary list of modules by the @command{grub2-mkimage}
+ program.  Usually, it contains enough modules to access @file{/boot/grub},
+ and loads everything else (including menu handling, the ability to load
+ target operating systems, and so on) from the file system at run-time.  The
+@@ -2858,7 +2865,7 @@ GRUB 2 has no single Stage 2 image.  Instead, it loads modules from
+ In GRUB 2, images for booting from CD-ROM drives are now constructed using
+ @file{cdboot.img} and @file{core.img}, making sure that the core image
+ contains the @samp{iso9660} module.  It is usually best to use the
+-@command{grub-mkrescue} program for this.
++@command{grub2-mkrescue} program for this.
+ 
+ @item nbgrub
+ There is as yet no equivalent for @file{nbgrub} in GRUB 2; it was used by
+@@ -3014,8 +3021,8 @@ There are two ways to specify files, by @dfn{absolute file name} and by
+ 
+ An absolute file name resembles a Unix absolute file name, using
+ @samp{/} for the directory separator (not @samp{\} as in DOS). One
+-example is @samp{(hd0,1)/boot/grub/grub.cfg}. This means the file
+-@file{/boot/grub/grub.cfg} in the first partition of the first hard
++example is @samp{(hd0,1)/boot/grub2/grub.cfg}. This means the file
++@file{/boot/grub2/grub.cfg} in the first partition of the first hard
+ disk. If you omit the device name in an absolute file name, GRUB uses
+ GRUB's @dfn{root device} implicitly. So if you set the root device to,
+ say, @samp{(hd1,1)} by the command @samp{set root=(hd1,1)} (@pxref{set}),
+@@ -3023,8 +3030,8 @@ then @code{/boot/kernel} is the same as @code{(hd1,1)/boot/kernel}.
+ 
+ On ZFS filesystem the first path component must be
+ @var{volume}@samp{@@}[@var{snapshot}].
+-So @samp{/rootvol@@snap-129/boot/grub/grub.cfg} refers to file
+-@samp{/boot/grub/grub.cfg} in snapshot of volume @samp{rootvol} with name
++So @samp{/rootvol@@snap-129/boot/grub2/grub.cfg} refers to file
++@samp{/boot/grub2/grub.cfg} in snapshot of volume @samp{rootvol} with name
+ @samp{snap-129}.  Trailing @samp{@@} after volume name is mandatory even if
+ snapshot name is omitted.
+ 
+@@ -3427,7 +3434,7 @@ The more recent release of Minix would then be identified as
+ @samp{other>minix>minix-3.4.0}.
+ 
+ This variable is often set by @samp{GRUB_DEFAULT} (@pxref{Simple
+-configuration}), @command{grub-set-default}, or @command{grub-reboot}.
++configuration}), @command{grub2-set-default}, or @command{grub2-reboot}.
+ 
+ 
+ @node fallback
+@@ -3517,7 +3524,7 @@ If this variable is set, it names the language code that the
+ example, French would be named as @samp{fr}, and Simplified Chinese as
+ @samp{zh_CN}.
+ 
+-@command{grub-mkconfig} (@pxref{Simple configuration}) will try to set a
++@command{grub2-mkconfig} (@pxref{Simple configuration}) will try to set a
+ reasonable default for this variable based on the system locale.
+ 
+ 
+@@ -3525,10 +3532,10 @@ reasonable default for this variable based on the system locale.
+ @subsection locale_dir
+ 
+ If this variable is set, it names the directory where translation files may
+-be found (@pxref{gettext}), usually @file{/boot/grub/locale}.  Otherwise,
++be found (@pxref{gettext}), usually @file{/boot/grub2/locale}.  Otherwise,
+ internationalization is disabled.
+ 
+-@command{grub-mkconfig} (@pxref{Simple configuration}) will set a reasonable
++@command{grub2-mkconfig} (@pxref{Simple configuration}) will set a reasonable
+ default for this variable if internationalization is needed and any
+ translation files are available.
+ 
+@@ -3646,7 +3653,7 @@ input.  The default is not to pause output.
+ 
+ The location of the @samp{/boot/grub} directory as an absolute file name
+ (@pxref{File name syntax}).  This is normally set by GRUB at startup based
+-on information provided by @command{grub-install}.  GRUB modules are
++on information provided by @command{grub2-install}.  GRUB modules are
+ dynamically loaded from this directory, so it must be set correctly in order
+ for many parts of GRUB to work.
+ 
+@@ -3737,17 +3744,17 @@ GRUB provides an ``environment block'' which can be used to save a small
+ amount of state.
+ 
+ The environment block is a preallocated 1024-byte file, which normally lives
+-in @file{/boot/grub/grubenv} (although you should not assume this).  At boot
++in @file{/boot/grub2/grubenv} (although you should not assume this).  At boot
+ time, the @command{load_env} command (@pxref{load_env}) loads environment
+ variables from it, and the @command{save_env} (@pxref{save_env}) command
+ saves environment variables to it.  From a running system, the
+-@command{grub-editenv} utility can be used to edit the environment block.
++@command{grub2-editenv} utility can be used to edit the environment block.
+ 
+ For safety reasons, this storage is only available when installed on a plain
+ disk (no LVM or RAID), using a non-checksumming filesystem (no ZFS), and
+ using BIOS or EFI functions (no ATA, USB or IEEE1275).
+ 
+-@command{grub-mkconfig} uses this facility to implement
++@command{grub2-mkconfig} uses this facility to implement
+ @samp{GRUB_SAVEDEFAULT} (@pxref{Simple configuration}).
+ 
+ 
+@@ -4476,7 +4483,7 @@ Translate @var{string} into the current language.
+ 
+ The current language code is stored in the @samp{lang} variable in GRUB's
+ environment (@pxref{lang}).  Translation files in MO format are read from
+-@samp{locale_dir} (@pxref{locale_dir}), usually @file{/boot/grub/locale}.
++@samp{locale_dir} (@pxref{locale_dir}), usually @file{/boot/grub2/locale}.
+ @end deffn
+ 
+ 
+@@ -4871,7 +4878,7 @@ Define a user named @var{user} with password @var{clear-password}.
+ 
+ @deffn Command password_pbkdf2 user hashed-password
+ Define a user named @var{user} with password hash @var{hashed-password}.
+-Use @command{grub-mkpasswd-pbkdf2} (@pxref{Invoking grub-mkpasswd-pbkdf2})
++Use @command{grub2-mkpasswd-pbkdf2} (@pxref{Invoking grub2-mkpasswd-pbkdf2})
+ to generate password hashes.  @xref{Security}.
+ @end deffn
+ 
+@@ -5814,8 +5821,8 @@ The @samp{password} (@pxref{password}) and @samp{password_pbkdf2}
+ which has an associated password.  @samp{password} sets the password in
+ plain text, requiring @file{grub.cfg} to be secure; @samp{password_pbkdf2}
+ sets the password hashed using the Password-Based Key Derivation Function
+-(RFC 2898), requiring the use of @command{grub-mkpasswd-pbkdf2}
+-(@pxref{Invoking grub-mkpasswd-pbkdf2}) to generate password hashes.
++(RFC 2898), requiring the use of @command{grub2-mkpasswd-pbkdf2}
++(@pxref{Invoking grub2-mkpasswd-pbkdf2}) to generate password hashes.
+ 
+ In order to enable authentication support, the @samp{superusers} environment
+ variable must be set to a list of usernames, separated by any of spaces,
+@@ -5860,7 +5867,7 @@ menuentry "May be run by user1 or a superuser" --users user1 @{
+ @end group
+ @end example
+ 
+-The @command{grub-mkconfig} program does not yet have built-in support for
++The @command{grub2-mkconfig} program does not yet have built-in support for
+ generating configuration files with authentication.  You can use
+ @file{/etc/grub.d/40_custom} to add simple superuser authentication, by
+ adding @kbd{set superusers=} and @kbd{password} or @kbd{password_pbkdf2}
+@@ -5887,7 +5894,17 @@ may halt or otherwise impact the boot process.
+ 
+ An initial trusted public key can be embedded within the GRUB @file{core.img}
+ using the @code{--pubkey} option to @command{grub-install}
+-(@pxref{Invoking grub-install}).
++(@pxref{Invoking grub2-install}).
++
++@comment Unfortunately --pubkey is not yet supported by grub2-install,
++@comment but we should not bring up internal detail grub2-mkimage here
++@comment in the user guide (as opposed to developer's manual).
++
++@comment An initial trusted public key can be embedded within the GRUB
++@comment @file{core.img} using the @code{--pubkey} option to
++@comment @command{grub2-mkimage} (@pxref{Invoking grub2-install}).  Presently it
++@comment is necessary to write a custom wrapper around @command{grub2-mkimage}
++@comment using the @code{--grub-mkimage} flag to @command{grub2-install}.
+ 
+ GRUB uses GPG-style detached signatures (meaning that a file
+ @file{foo.sig} will be produced when file @file{foo} is signed), and
+@@ -5907,8 +5924,8 @@ gpg --detach-sign /path/to/file
+ For successful validation of all of GRUB's subcomponents and the
+ loaded OS kernel, they must all be signed.  One way to accomplish this
+ is the following (after having already produced the desired
+-@file{grub.cfg} file, e.g., by running @command{grub-mkconfig}
+-(@pxref{Invoking grub-mkconfig}):
++@file{grub.cfg} file, e.g., by running @command{grub2-mkconfig}
++(@pxref{Invoking grub2-mkconfig}):
+ 
+ @example
+ @group
+@@ -5930,7 +5947,7 @@ See also: @ref{check_signatures}, @ref{verify_detached}, @ref{trust},
+ Note that internally signature enforcement is controlled by setting
+ the environment variable @code{check_signatures} equal to
+ @code{enforce}.  Passing one or more @code{--pubkey} options to
+-@command{grub-mkimage} implicitly defines @code{check_signatures}
++@command{grub2-mkimage} implicitly defines @code{check_signatures}
+ equal to @code{enforce} in @file{core.img} prior to processing any
+ configuration files.
+ 
+@@ -6388,10 +6405,10 @@ Required files are:
+ 
+ GRUB's normal start-up procedure involves setting the @samp{prefix}
+ environment variable to a value set in the core image by
+-@command{grub-install}, setting the @samp{root} variable to match, loading
++@command{grub2-install}, setting the @samp{root} variable to match, loading
+ the @samp{normal} module from the prefix, and running the @samp{normal}
+ command (@pxref{normal}).  This command is responsible for reading
+-@file{/boot/grub/grub.cfg}, running the menu, and doing all the useful
++@file{/boot/grub2/grub.cfg}, running the menu, and doing all the useful
+ things GRUB is supposed to do.
+ 
+ If, instead, you only get a rescue shell, this usually means that GRUB
+@@ -6417,8 +6434,8 @@ normal
+ 
+ However, any problem that leaves you in the rescue shell probably means that
+ GRUB was not correctly installed.  It may be more useful to try to reinstall
+-it properly using @kbd{grub-install @var{device}} (@pxref{Invoking
+-grub-install}).  When doing this, there are a few things to remember:
++it properly using @kbd{grub2-install @var{device}} (@pxref{Invoking
++grub2-install}).  When doing this, there are a few things to remember:
+ 
+ @itemize @bullet{}
+ @item
+@@ -6430,7 +6447,7 @@ is usually better to use UUIDs or file system labels and avoid depending on
+ drive ordering entirely.
+ 
+ @item
+-At least on BIOS systems, if you tell @command{grub-install} to install GRUB
++At least on BIOS systems, if you tell @command{grub2-install} to install GRUB
+ to a partition but GRUB has already been installed in the master boot
+ record, then the GRUB installation in the partition will be ignored.
+ 
+@@ -6461,21 +6478,21 @@ entry which claims partition start at block 0. This change will not hamper
+ bootability on other machines.
+ 
+ 
+-@node Invoking grub-install
+-@chapter Invoking grub-install
++@node Invoking grub2-install
++@chapter Invoking grub2-install
+ 
+-The program @command{grub-install} generates a GRUB core image using
+-@command{grub-mkimage} and installs it on your system.  You must specify the
++The program @command{grub2-install} generates a GRUB core image using
++@command{grub2-mkimage} and installs it on your system.  You must specify the
+ device name on which you want to install GRUB, like this:
+ 
+ @example
+-grub-install @var{install_device}
++grub2-install @var{install_device}
+ @end example
+ 
+ The device name @var{install_device} is an OS device name or a GRUB
+ device name.
+ 
+-@command{grub-install} accepts the following options:
++@command{grub2-install} accepts the following options:
+ 
+ @table @option
+ @item --help
+@@ -6491,13 +6508,13 @@ separate partition or a removable disk.
+ If this option is not specified then it defaults to @file{/boot}, so
+ 
+ @example
+-@kbd{grub-install /dev/sda}
++@kbd{grub2-install /dev/sda}
+ @end example
+ 
+ is equivalent to
+ 
+ @example
+-@kbd{grub-install --boot-directory=/boot/ /dev/sda}
++@kbd{grub2-install --boot-directory=/boot/ /dev/sda}
+ @end example
+ 
+ Here is an example in which you have a separate @dfn{boot} partition which is 
+@@ -6505,16 +6522,16 @@ mounted on
+ @file{/mnt/boot}:
+ 
+ @example
+-@kbd{grub-install --boot-directory=/mnt/boot /dev/sdb}
++@kbd{grub2-install --boot-directory=/mnt/boot /dev/sdb}
+ @end example
+ 
+ @item --recheck
+-Recheck the device map, even if @file{/boot/grub/device.map} already
++Recheck the device map, even if @file{/boot/grub2/device.map} already
+ exists. You should use this option whenever you add/remove a disk
+ into/from your computer.
+ 
+ @item --no-rs-codes
+-By default on x86 BIOS systems, @command{grub-install} will use some
++By default on x86 BIOS systems, @command{grub2-install} will use some
+ extra space in the bootloader embedding area for Reed-Solomon
+ error-correcting codes. This enables GRUB to still boot successfully
+ if some blocks are corrupted.  The exact amount of protection offered
+@@ -6527,17 +6544,17 @@ installation}) where GRUB does not reside in any unpartitioned space
+ outside of the MBR.  Disable the Reed-Solomon codes with this option.
+ @end table
+ 
+-@node Invoking grub-mkconfig
+-@chapter Invoking grub-mkconfig
++@node Invoking grub2-mkconfig
++@chapter Invoking grub2-mkconfig
+ 
+-The program @command{grub-mkconfig} generates a configuration file for GRUB
++The program @command{grub2-mkconfig} generates a configuration file for GRUB
+ (@pxref{Simple configuration}).
+ 
+ @example
+-grub-mkconfig -o /boot/grub/grub.cfg
++grub-mkconfig -o /boot/grub2/grub.cfg
+ @end example
+ 
+-@command{grub-mkconfig} accepts the following options:
++@command{grub2-mkconfig} accepts the following options:
+ 
+ @table @option
+ @item --help
+@@ -6553,17 +6570,17 @@ it to standard output.
+ @end table
+ 
+ 
+-@node Invoking grub-mkpasswd-pbkdf2
+-@chapter Invoking grub-mkpasswd-pbkdf2
++@node Invoking grub2-mkpasswd-pbkdf2
++@chapter Invoking grub2-mkpasswd-pbkdf2
+ 
+-The program @command{grub-mkpasswd-pbkdf2} generates password hashes for
++The program @command{grub2-mkpasswd-pbkdf2} generates password hashes for
+ GRUB (@pxref{Security}).
+ 
+ @example
+ grub-mkpasswd-pbkdf2
+ @end example
+ 
+-@command{grub-mkpasswd-pbkdf2} accepts the following options:
++@command{grub2-mkpasswd-pbkdf2} accepts the following options:
+ 
+ @table @option
+ @item -c @var{number}
+@@ -6581,23 +6598,23 @@ Length of the salt.  Defaults to 64.
+ @end table
+ 
+ 
+-@node Invoking grub-mkrelpath
+-@chapter Invoking grub-mkrelpath
++@node Invoking grub2-mkrelpath
++@chapter Invoking grub2-mkrelpath
+ 
+-The program @command{grub-mkrelpath} makes a file system path relative to
++The program @command{grub2-mkrelpath} makes a file system path relative to
+ the root of its containing file system.  For instance, if @file{/usr} is a
+ mount point, then:
+ 
+ @example
+-$ @kbd{grub-mkrelpath /usr/share/grub/unicode.pf2}
++$ @kbd{grub2-mkrelpath /usr/share/grub/unicode.pf2}
+ @samp{/share/grub/unicode.pf2}
+ @end example
+ 
+ This is mainly used internally by other GRUB utilities such as
+-@command{grub-mkconfig} (@pxref{Invoking grub-mkconfig}), but may
++@command{grub2-mkconfig} (@pxref{Invoking grub2-mkconfig}), but may
+ occasionally also be useful for debugging.
+ 
+-@command{grub-mkrelpath} accepts the following options:
++@command{grub2-mkrelpath} accepts the following options:
+ 
+ @table @option
+ @item --help
+@@ -6608,17 +6625,17 @@ Print the version number of GRUB and exit.
+ @end table
+ 
+ 
+-@node Invoking grub-mkrescue
+-@chapter Invoking grub-mkrescue
++@node Invoking grub2-mkrescue
++@chapter Invoking grub2-mkrescue
+ 
+-The program @command{grub-mkrescue} generates a bootable GRUB rescue image
++The program @command{grub2-mkrescue} generates a bootable GRUB rescue image
+ (@pxref{Making a GRUB bootable CD-ROM}).
+ 
+ @example
+ grub-mkrescue -o grub.iso
+ @end example
+ 
+-All arguments not explicitly listed as @command{grub-mkrescue} options are
++All arguments not explicitly listed as @command{grub2-mkrescue} options are
+ passed on directly to @command{xorriso} in @command{mkisofs} emulation mode.
+ Options passed to @command{xorriso} will normally be interpreted as
+ @command{mkisofs} options; if the option @samp{--} is used, then anything
+@@ -6633,7 +6650,7 @@ mkdir -p disk/boot/grub
+ grub-mkrescue -o grub.iso disk
+ @end example
+ 
+-@command{grub-mkrescue} accepts the following options:
++@command{grub2-mkrescue} accepts the following options:
+ 
+ @table @option
+ @item --help
+@@ -6661,15 +6678,15 @@ Use @var{file} as the @command{xorriso} program, rather than the built-in
+ default.
+ 
+ @item --grub-mkimage=@var{file}
+-Use @var{file} as the @command{grub-mkimage} program, rather than the
++Use @var{file} as the @command{grub2-mkimage} program, rather than the
+ built-in default.
+ @end table
+ 
+ 
+-@node Invoking grub-mount
+-@chapter Invoking grub-mount
++@node Invoking grub2-mount
++@chapter Invoking grub2-mount
+ 
+-The program @command{grub-mount} performs a read-only mount of any file
++The program @command{grub2-mount} performs a read-only mount of any file
+ system or file system image that GRUB understands, using GRUB's file system
+ drivers via FUSE.  (It is only available if FUSE development files were
+ present when GRUB was built.)  This has a number of uses:
+@@ -6701,13 +6718,13 @@ even if nobody has yet written a FUSE module specifically for that file
+ system type.
+ @end itemize
+ 
+-Using @command{grub-mount} is normally as simple as:
++Using @command{grub2-mount} is normally as simple as:
+ 
+ @example
+ grub-mount /dev/sda1 /mnt
+ @end example
+ 
+-@command{grub-mount} must be given one or more images and a mount point as
++@command{grub2-mount} must be given one or more images and a mount point as
+ non-option arguments (if it is given more than one image, it will treat them
+ as a RAID set), and also accepts the following options:
+ 
+@@ -6729,13 +6746,13 @@ Show debugging output for conditions matching @var{string}.
+ @item -K prompt|@var{file}
+ @itemx --zfs-key=prompt|@var{file}
+ Load a ZFS encryption key.  If you use @samp{prompt} as the argument,
+-@command{grub-mount} will read a passphrase from the terminal; otherwise, it
++@command{grub2-mount} will read a passphrase from the terminal; otherwise, it
+ will read key material from the specified file.
+ 
+ @item -r @var{device}
+ @itemx --root=@var{device}
+ Set the GRUB root device to @var{device}.  You do not normally need to set
+-this; @command{grub-mount} will automatically set the root device to the
++this; @command{grub2-mount} will automatically set the root device to the
+ root of the supplied file system.
+ 
+ If @var{device} is just a number, then it will be treated as a partition
+@@ -6753,10 +6770,10 @@ Print verbose messages.
+ @end table
+ 
+ 
+-@node Invoking grub-probe
+-@chapter Invoking grub-probe
++@node Invoking grub2-probe
++@chapter Invoking grub2-probe
+ 
+-The program @command{grub-probe} probes device information for a given path
++The program @command{grub2-probe} probes device information for a given path
+ or device.
+ 
+ @example
+@@ -6764,7 +6781,7 @@ grub-probe --target=fs /boot/grub
+ grub-probe --target=drive --device /dev/sda1
+ @end example
+ 
+-@command{grub-probe} must be given a path or device as a non-option
++@command{grub2-probe} must be given a path or device as a non-option
+ argument, and also accepts the following options:
+ 
+ @table @option
+@@ -6777,16 +6794,16 @@ Print the version number of GRUB and exit.
+ @item -d
+ @itemx --device
+ If this option is given, then the non-option argument is a system device
+-name (such as @samp{/dev/sda1}), and @command{grub-probe} will print
++name (such as @samp{/dev/sda1}), and @command{grub2-probe} will print
+ information about that device.  If it is not given, then the non-option
+ argument is a filesystem path (such as @samp{/boot/grub}), and
+-@command{grub-probe} will print information about the device containing that
++@command{grub2-probe} will print information about the device containing that
+ part of the filesystem.
+ 
+ @item -m @var{file}
+ @itemx --device-map=@var{file}
+ Use @var{file} as the device map (@pxref{Device map}) rather than the
+-default, usually @samp{/boot/grub/device.map}.
++default, usually @samp{/boot/grub2/device.map}.
+ 
+ @item -t @var{target}
+ @itemx --target=@var{target}
+@@ -6839,19 +6856,19 @@ Print verbose messages.
+ @end table
+ 
+ 
+-@node Invoking grub-script-check
+-@chapter Invoking grub-script-check
++@node Invoking grub2-script-check
++@chapter Invoking grub2-script-check
+ 
+-The program @command{grub-script-check} takes a GRUB script file
++The program @command{grub2-script-check} takes a GRUB script file
+ (@pxref{Shell-like scripting}) and checks it for syntax errors, similar to
+ commands such as @command{sh -n}.  It may take a @var{path} as a non-option
+ argument; if none is supplied, it will read from standard input.
+ 
+ @example
+-grub-script-check /boot/grub/grub.cfg
++grub-script-check /boot/grub2/grub.cfg
+ @end example
+ 
+-@command{grub-script-check} accepts the following options:
++@command{grub2-script-check} accepts the following options:
+ 
+ @table @option
+ @item --help
diff --git a/SOURCES/0056-macos-just-build-chainloader-entries-don-t-try-any-x.patch b/SOURCES/0056-macos-just-build-chainloader-entries-don-t-try-any-x.patch
new file mode 100644
index 0000000..574d117
--- /dev/null
+++ b/SOURCES/0056-macos-just-build-chainloader-entries-don-t-try-any-x.patch
@@ -0,0 +1,124 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Wed, 24 May 2017 12:42:32 -0400
+Subject: [PATCH] macos: just build chainloader entries, don't try any xnu xnu.
+
+Since our bugs tell us that the xnu boot entries really just don't work
+most of the time, and they create piles of extra boot entries, because
+they can't quite figure out 32-vs-64 and other stuff like that.
+
+It's rediculous, and we should just boot their bootloader through the
+chainloader instead.
+
+So this patch does that.
+
+Resolves: rhbz#893179
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ util/grub.d/30_os-prober.in | 78 +++++++++++----------------------------------
+ 1 file changed, 18 insertions(+), 60 deletions(-)
+
+diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in
+index 1b91c102f35..4b27bd20153 100644
+--- a/util/grub.d/30_os-prober.in
++++ b/util/grub.d/30_os-prober.in
+@@ -42,68 +42,25 @@ if [ -z "${OSPROBED}" ] ; then
+ fi
+ 
+ osx_entry() {
+-    if [ x$2 = x32 ]; then
+-        # TRANSLATORS: it refers to kernel architecture (32-bit)
+-	bitstr="$(gettext "(32-bit)")"
+-    else
+-        # TRANSLATORS: it refers to kernel architecture (64-bit)
+-	bitstr="$(gettext "(64-bit)")"
+-    fi
+     # TRANSLATORS: it refers on the OS residing on device %s
+     onstr="$(gettext_printf "(on %s)" "${DEVICE}")"
+-        cat << EOF
+-menuentry '$(echo "${LONGNAME} $bitstr $onstr" | grub_quote)' --class osx --class darwin --class os \$menuentry_id_option 'osprober-xnu-$2-$(grub_get_device_id "${DEVICE}")'  {
++    hints=""
++    for hint in `"${grub_probe}" --device ${device} --target=efi_hints 2> /dev/null` ; do
++      hints="${hints} --hint=${hint}"
++    done
++    cat << EOF
++menuentry '$(echo "${LONGNAME} $onstr" | grub_quote)' --class osx --class darwin --class os \$menuentry_id_option 'osprober-xnu-$2-$(grub_get_device_id "${DEVICE}")'  {
+ EOF
+ 	save_default_entry | grub_add_tab
+ 	prepare_grub_to_access_device ${DEVICE} | grub_add_tab
+ 	cat << EOF
++	set gfxpayload=keep
+         load_video
+-        set do_resume=0
+-        if [ /var/vm/sleepimage -nt10 / ]; then
+-           if xnu_resume /var/vm/sleepimage; then
+-             set do_resume=1
+-           fi
+-        fi
+-        if [ \$do_resume = 0 ]; then
+-           xnu_uuid ${OSXUUID} uuid
+-           if [ -f /Extra/DSDT.aml ]; then
+-              acpi -e /Extra/DSDT.aml
+-           fi
+-           if [ /kernelcache -nt /System/Library/Extensions ]; then
+-              $1 /kernelcache boot-uuid=\${uuid} rd=*uuid
+-           elif [ -f /System/Library/Kernels/kernel ]; then
+-              $1 /System/Library/Kernels/kernel boot-uuid=\${uuid} rd=*uuid
+-              xnu_kextdir /System/Library/Extensions
+-           else
+-              $1 /mach_kernel boot-uuid=\${uuid} rd=*uuid
+-              if [ /System/Library/Extensions.mkext -nt /System/Library/Extensions ]; then
+-                xnu_mkext /System/Library/Extensions.mkext
+-              else
+-                xnu_kextdir /System/Library/Extensions
+-              fi
+-           fi
+-           if [ -f /Extra/Extensions.mkext ]; then
+-              xnu_mkext /Extra/Extensions.mkext
+-           fi
+-           if [ -d /Extra/Extensions ]; then
+-              xnu_kextdir /Extra/Extensions
+-           fi
+-           if [ -f /Extra/devprop.bin ]; then
+-              xnu_devprop_load /Extra/devprop.bin
+-           fi
+-           if [ -f /Extra/splash.jpg ]; then
+-              insmod jpeg
+-              xnu_splash /Extra/splash.jpg
+-           fi
+-           if [ -f /Extra/splash.png ]; then
+-              insmod png
+-              xnu_splash /Extra/splash.png
+-           fi
+-           if [ -f /Extra/splash.tga ]; then
+-              insmod tga
+-              xnu_splash /Extra/splash.tga
+-           fi
+-        fi
++	insmod part_gpt
++	insmod hfsplus
++	search --no-floppy --fs-uuid --set=root ${hints} $(grub_get_device_id "${DEVICE}")
++	chainloader (\$root)/System/Library/CoreServices/boot.efi
++	boot
+ }
+ EOF
+ }
+@@ -292,11 +249,12 @@ EOF
+       echo "$title_correction_code"
+     ;;
+     macosx)
+-      if [ "${UUID}" ]; then
+-	OSXUUID="${UUID}"
+-	osx_entry xnu_kernel 32
+-	osx_entry xnu_kernel64 64
+-      fi
++      for subdevice in ${DEVICE%[[:digit:]]*}* ; do
++	parttype="`"${grub_probe}" --device ${device} --target=gpt_parttype "${subdevice}" 2> /dev/null`"
++	if [[ "$parttype" = "426f6f74-0000-11aa-aa11-00306543ecac" ]]; then
++	  DEVICE="${subdevice}" osx_entry
++	fi
++      done
+     ;;
+     hurd)
+       onstr="$(gettext_printf "(on %s)" "${DEVICE}")"
diff --git a/SOURCES/0057-grub2-btrfs-Add-ability-to-boot-from-subvolumes.patch b/SOURCES/0057-grub2-btrfs-Add-ability-to-boot-from-subvolumes.patch
new file mode 100644
index 0000000..68dfc1d
--- /dev/null
+++ b/SOURCES/0057-grub2-btrfs-Add-ability-to-boot-from-subvolumes.patch
@@ -0,0 +1,703 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jeff Mahoney <jeffm@suse.com>
+Date: Tue, 9 Jul 2019 13:39:45 +0200
+Subject: [PATCH] grub2/btrfs: Add ability to boot from subvolumes
+
+This patch adds the ability to specify a different root on a btrfs
+filesystem too boot from other than the default one.
+
+btrfs-list-snapshots <dev> will list the subvolumes available on the
+filesystem.
+
+set btrfs_subvol=<path> and set btrfs_subvolid=<subvolid> will specify
+which subvolume to use and any pathnames provided with either of those
+variables set will start using that root. If the subvolume or subvolume id
+doesn't exist, then an error case will result.
+
+It is possible to boot into a separate GRUB instance by exporting the
+variable and loading the config file from the subvolume.
+
+Signed-off-by: Jeff Mahoney <jeffm@suse.com>
+---
+ grub-core/fs/btrfs.c | 552 +++++++++++++++++++++++++++++++++++++++++++++++++--
+ include/grub/btrfs.h |   1 +
+ 2 files changed, 533 insertions(+), 20 deletions(-)
+
+diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
+index 63203034dfc..f1fff7385b5 100644
+--- a/grub-core/fs/btrfs.c
++++ b/grub-core/fs/btrfs.c
+@@ -38,6 +38,9 @@
+ #include <zstd.h>
+ #include <grub/i18n.h>
+ #include <grub/btrfs.h>
++#include <grub/command.h>
++#include <grub/env.h>
++#include <grub/extcmd.h>
+ #include <grub/crypto.h>
+ #include <grub/diskfilter.h>
+ #include <grub/safemath.h>
+@@ -79,9 +82,11 @@ struct grub_btrfs_superblock
+   grub_uint64_t generation;
+   grub_uint64_t root_tree;
+   grub_uint64_t chunk_tree;
+-  grub_uint8_t dummy2[0x20];
++  grub_uint8_t dummy2[0x18];
++  grub_uint64_t bytes_used;
+   grub_uint64_t root_dir_objectid;
+-  grub_uint8_t dummy3[0x41];
++  grub_uint64_t num_devices;
++  grub_uint8_t dummy3[0x39];
+   struct grub_btrfs_device this_device;
+   char label[0x100];
+   grub_uint8_t dummy4[0x100];
+@@ -121,6 +126,7 @@ struct grub_btrfs_data
+   grub_uint64_t exttree;
+   grub_size_t extsize;
+   struct grub_btrfs_extent_data *extent;
++  grub_uint64_t fs_tree;
+ };
+ 
+ struct grub_btrfs_chunk_item
+@@ -191,6 +197,14 @@ struct grub_btrfs_leaf_descriptor
+   } *data;
+ };
+ 
++struct grub_btrfs_root_ref
++{
++  grub_uint64_t dirid;
++  grub_uint64_t sequence;
++  grub_uint16_t name_len;
++  const char name[0];
++} __attribute__ ((packed));
++
+ struct grub_btrfs_time
+ {
+   grub_int64_t sec;
+@@ -236,6 +250,14 @@ struct grub_btrfs_extent_data
+ 
+ #define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100
+ 
++#define GRUB_BTRFS_ROOT_TREE_OBJECTID 1ULL
++#define GRUB_BTRFS_FS_TREE_OBJECTID 5ULL
++#define GRUB_BTRFS_ROOT_REF_KEY     156
++#define GRUB_BTRFS_ROOT_ITEM_KEY     132
++
++static grub_uint64_t btrfs_default_subvolid = 0;
++static char *btrfs_default_subvol = NULL;
++
+ static grub_disk_addr_t superblock_sectors[] = { 64 * 2, 64 * 1024 * 2,
+   256 * 1048576 * 2, 1048576ULL * 1048576ULL * 2
+ };
+@@ -1173,6 +1195,62 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
+   return GRUB_ERR_NONE;
+ }
+ 
++static grub_err_t
++get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree,
++            grub_uint64_t objectid, grub_uint64_t offset,
++            grub_uint64_t *fs_root);
++
++static grub_err_t
++lookup_root_by_id(struct grub_btrfs_data *data, grub_uint64_t id)
++{
++  grub_err_t err;
++  grub_uint64_t tree;
++
++  err = get_fs_root(data, data->sblock.root_tree, id, -1, &tree);
++  if (!err)
++    data->fs_tree = tree;
++  return err;
++}
++
++static grub_err_t
++find_path (struct grub_btrfs_data *data,
++	   const char *path, struct grub_btrfs_key *key,
++	   grub_uint64_t *tree, grub_uint8_t *type);
++
++static grub_err_t
++lookup_root_by_name(struct grub_btrfs_data *data, const char *path)
++{
++  grub_err_t err;
++  grub_uint64_t tree = 0;
++  grub_uint8_t type;
++  struct grub_btrfs_key key;
++
++  err = find_path (data, path, &key, &tree, &type);
++  if (err)
++      return grub_error(GRUB_ERR_FILE_NOT_FOUND, "couldn't locate %s\n", path);
++
++  if (key.object_id != grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0)
++    return grub_error(GRUB_ERR_BAD_FILE_TYPE, "%s: not a subvolume\n", path);
++
++  data->fs_tree = tree;
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++btrfs_handle_subvol(struct grub_btrfs_data *data __attribute__ ((unused)))
++{
++  if (btrfs_default_subvol)
++    return lookup_root_by_name(data, btrfs_default_subvol);
++
++  if (btrfs_default_subvolid)
++    return lookup_root_by_id(data, btrfs_default_subvolid);
++
++  data->fs_tree = 0;
++
++  return GRUB_ERR_NONE;
++}
++
++
+ static struct grub_btrfs_data *
+ grub_btrfs_mount (grub_device_t dev)
+ {
+@@ -1208,6 +1286,13 @@ grub_btrfs_mount (grub_device_t dev)
+   data->devices_attached[0].dev = dev;
+   data->devices_attached[0].id = data->sblock.this_device.device_id;
+ 
++  err = btrfs_handle_subvol (data);
++  if (err)
++    {
++      grub_free (data);
++      return NULL;
++    }
++
+   return data;
+ }
+ 
+@@ -1673,6 +1758,91 @@ get_root (struct grub_btrfs_data *data, struct grub_btrfs_key *key,
+   return GRUB_ERR_NONE;
+ }
+ 
++static grub_err_t
++find_pathname(struct grub_btrfs_data *data, grub_uint64_t objectid,
++              grub_uint64_t fs_root, const char *name, char **pathname)
++{
++  grub_err_t err;
++  struct grub_btrfs_key key = {
++    .object_id = objectid,
++    .type = GRUB_BTRFS_ITEM_TYPE_INODE_REF,
++    .offset = 0,
++  };
++  struct grub_btrfs_key key_out;
++  struct grub_btrfs_leaf_descriptor desc;
++  char *p = grub_strdup (name);
++  grub_disk_addr_t elemaddr;
++  grub_size_t elemsize;
++  grub_size_t alloc = grub_strlen(name) + 1;
++
++  err = lower_bound(data, &key, &key_out, fs_root,
++                    &elemaddr, &elemsize, &desc, 0);
++  if (err)
++    return grub_error(err, "lower_bound caught %d\n", err);
++
++  if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF)
++    next(data, &desc, &elemaddr, &elemsize, &key_out);
++
++  if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF)
++    {
++      return grub_error(GRUB_ERR_FILE_NOT_FOUND,
++                        "Can't find inode ref for {%"PRIuGRUB_UINT64_T
++                        ", %u, %"PRIuGRUB_UINT64_T"} %"PRIuGRUB_UINT64_T
++                        "/%"PRIuGRUB_SIZE"\n",
++                        key_out.object_id, key_out.type,
++                        key_out.offset, elemaddr, elemsize);
++    }
++
++
++  while (key_out.type == GRUB_BTRFS_ITEM_TYPE_INODE_REF &&
++         key_out.object_id != key_out.offset) {
++    struct grub_btrfs_inode_ref *inode_ref;
++    char *new;
++
++    inode_ref = grub_malloc(elemsize + 1);
++    if (!inode_ref)
++      return grub_error(GRUB_ERR_OUT_OF_MEMORY,
++                        "couldn't allocate memory for inode_ref (%"PRIuGRUB_SIZE")\n", elemsize);
++
++    err = grub_btrfs_read_logical(data, elemaddr, inode_ref, elemsize, 0);
++    if (err)
++      return grub_error(err, "read_logical caught %d\n", err);
++
++    alloc += grub_le_to_cpu16 (inode_ref->n) + 2;
++    new = grub_malloc(alloc);
++    if (!new)
++      return grub_error(GRUB_ERR_OUT_OF_MEMORY,
++                        "couldn't allocate memory for name (%"PRIuGRUB_SIZE")\n", alloc);
++
++    grub_memcpy(new, inode_ref->name, grub_le_to_cpu16 (inode_ref->n));
++    if (p)
++      {
++        new[grub_le_to_cpu16 (inode_ref->n)] = '/';
++        grub_strcpy (new + grub_le_to_cpu16 (inode_ref->n) + 1, p);
++        grub_free(p);
++      }
++    else
++      new[grub_le_to_cpu16 (inode_ref->n)] = 0;
++    grub_free(inode_ref);
++
++    p = new;
++
++    key.object_id = key_out.offset;
++
++    err = lower_bound(data, &key, &key_out, fs_root, &elemaddr,
++                      &elemsize, &desc, 0);
++    if (err)
++      return grub_error(err, "lower_bound caught %d\n", err);
++
++    if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF)
++      next(data, &desc, &elemaddr, &elemsize, &key_out);
++
++  }
++
++  *pathname = p;
++  return 0;
++}
++
+ static grub_err_t
+ find_path (struct grub_btrfs_data *data,
+ 	   const char *path, struct grub_btrfs_key *key,
+@@ -1691,14 +1861,26 @@ find_path (struct grub_btrfs_data *data,
+   char *origpath = NULL;
+   unsigned symlinks_max = 32;
+ 
+-  err = get_root (data, key, tree, type);
+-  if (err)
+-    return err;
+-
+   origpath = grub_strdup (path);
+   if (!origpath)
+     return grub_errno;
+ 
++  if (data->fs_tree)
++    {
++      *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
++      *tree = data->fs_tree;
++      /* This is a tree root, so everything starts at objectid 256 */
++      key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK);
++      key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
++      key->offset = 0;
++    }
++  else
++    {
++      err = get_root (data, key, tree, type);
++      if (err)
++	return err;
++    }
++
+   while (1)
+     {
+       while (path[0] == '/')
+@@ -1871,9 +2053,21 @@ find_path (struct grub_btrfs_data *data,
+ 	  path = path_alloc = tmp;
+ 	  if (path[0] == '/')
+ 	    {
+-	      err = get_root (data, key, tree, type);
+-	      if (err)
+-		return err;
++	      if (data->fs_tree)
++		{
++		  *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
++		  *tree = data->fs_tree;
++		  /* This is a tree root, so everything starts at objectid 256 */
++		  key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK);
++		  key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
++		  key->offset = 0;
++		}
++	      else
++		{
++		  err = get_root (data, key, tree, type);
++		  if (err)
++		    return err;
++		}
+ 	    }
+ 	  continue;
+ 	}
+@@ -2114,18 +2308,10 @@ grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len)
+ 				 data->tree, file->offset, buf, len);
+ }
+ 
+-static grub_err_t
+-grub_btrfs_uuid (grub_device_t device, char **uuid)
++static char *
++btrfs_unparse_uuid(struct grub_btrfs_data *data)
+ {
+-  struct grub_btrfs_data *data;
+-
+-  *uuid = NULL;
+-
+-  data = grub_btrfs_mount (device);
+-  if (!data)
+-    return grub_errno;
+-
+-  *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
++  return  grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
+ 			  grub_be_to_cpu16 (data->sblock.uuid[0]),
+ 			  grub_be_to_cpu16 (data->sblock.uuid[1]),
+ 			  grub_be_to_cpu16 (data->sblock.uuid[2]),
+@@ -2134,6 +2320,20 @@ grub_btrfs_uuid (grub_device_t device, char **uuid)
+ 			  grub_be_to_cpu16 (data->sblock.uuid[5]),
+ 			  grub_be_to_cpu16 (data->sblock.uuid[6]),
+ 			  grub_be_to_cpu16 (data->sblock.uuid[7]));
++}
++
++static grub_err_t
++grub_btrfs_uuid (grub_device_t device, char **uuid)
++{
++  struct grub_btrfs_data *data;
++
++  *uuid = NULL;
++
++  data = grub_btrfs_mount (device);
++  if (!data)
++    return grub_errno;
++
++  *uuid = btrfs_unparse_uuid(data);
+ 
+   grub_btrfs_unmount (data);
+ 
+@@ -2190,6 +2390,242 @@ grub_btrfs_embed (grub_device_t device __attribute__ ((unused)),
+ }
+ #endif
+ 
++static grub_err_t
++grub_cmd_btrfs_info (grub_command_t cmd __attribute__ ((unused)), int argc,
++		     char **argv)
++{
++  grub_device_t dev;
++  char *devname;
++  struct grub_btrfs_data *data;
++  char *uuid;
++
++  if (argc < 1)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
++
++  devname = grub_file_get_device_name(argv[0]);
++
++  if (!devname)
++    return grub_errno;
++
++  dev = grub_device_open (devname);
++  grub_free (devname);
++  if (!dev)
++    return grub_errno;
++
++  data = grub_btrfs_mount (dev);
++  if (!data)
++    {
++      grub_device_close(dev);
++      return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to open fs");
++    }
++
++  if (data->sblock.label)
++    grub_printf("Label: '%s' ", data->sblock.label);
++  else
++    grub_printf("Label: none ");
++
++  uuid = btrfs_unparse_uuid(data);
++
++  grub_printf(" uuid: %s\n\tTotal devices %" PRIuGRUB_UINT64_T
++              " FS bytes used %" PRIuGRUB_UINT64_T "\n",
++	      uuid, grub_cpu_to_le64(data->sblock.num_devices),
++	      grub_cpu_to_le64(data->sblock.bytes_used));
++
++  grub_btrfs_unmount (data);
++
++  return 0;
++}
++
++static grub_err_t
++get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree,
++            grub_uint64_t objectid, grub_uint64_t offset,
++            grub_uint64_t *fs_root)
++{
++  grub_err_t err;
++  struct grub_btrfs_key key_in = {
++    .object_id = objectid,
++    .type = GRUB_BTRFS_ROOT_ITEM_KEY,
++    .offset = offset,
++  }, key_out;
++  struct grub_btrfs_leaf_descriptor desc;
++  grub_disk_addr_t elemaddr;
++  grub_size_t elemsize;
++  struct grub_btrfs_root_item ri;
++
++  err = lower_bound(data, &key_in, &key_out, tree,
++                    &elemaddr, &elemsize, &desc, 0);
++
++  if (err)
++    return err;
++
++  if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM || elemaddr == 0)
++    return grub_error(GRUB_ERR_FILE_NOT_FOUND,
++                    N_("can't find fs root for subvol %"PRIuGRUB_UINT64_T"\n"),
++                    key_in.object_id);
++
++  err = grub_btrfs_read_logical (data, elemaddr, &ri, sizeof (ri), 0);
++  if (err)
++    return err;
++
++  *fs_root = ri.tree;
++
++  return GRUB_ERR_NONE;
++}
++
++static const struct grub_arg_option options[] = {
++  {"output", 'o', 0, N_("Output to a variable instead of the console."),
++   N_("VARNAME"), ARG_TYPE_STRING},
++  {"path-only", 'p', 0, N_("Show only the path of the subvolume."), 0, 0},
++  {"id-only", 'i', 0, N_("Show only the id of the subvolume."), 0, 0},
++  {0, 0, 0, 0, 0, 0}
++};
++
++static grub_err_t
++grub_cmd_btrfs_list_subvols (struct grub_extcmd_context *ctxt,
++			     int argc, char **argv)
++{
++  struct grub_btrfs_data *data;
++  grub_device_t dev;
++  char *devname;
++  grub_uint64_t tree;
++  struct grub_btrfs_key key_in = {
++    .object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_FS_TREE_OBJECTID),
++    .type = GRUB_BTRFS_ROOT_REF_KEY,
++    .offset = 0,
++  }, key_out;
++  struct grub_btrfs_leaf_descriptor desc;
++  grub_disk_addr_t elemaddr;
++  grub_uint64_t fs_root = 0;
++  grub_size_t elemsize;
++  grub_size_t allocated = 0;
++  int r = 0;
++  grub_err_t err;
++  char *buf = NULL;
++  int print = 1;
++  int path_only = ctxt->state[1].set;
++  int num_only = ctxt->state[2].set;
++  char *varname = NULL;
++  char *output = NULL;
++
++  if (ctxt->state[0].set) {
++    varname = ctxt->state[0].arg;
++    print = 0;
++  }
++
++  if (argc < 1)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
++
++  devname = grub_file_get_device_name(argv[0]);
++  if (!devname)
++    return grub_errno;
++
++  dev = grub_device_open (devname);
++  grub_free (devname);
++  if (!dev)
++    return grub_errno;
++
++  data = grub_btrfs_mount(dev);
++  if (!data)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, "could not open device");
++
++  tree = data->sblock.root_tree;
++  err = get_fs_root(data, tree, grub_cpu_to_le64_compile_time (GRUB_BTRFS_FS_TREE_OBJECTID),
++                    0, &fs_root);
++  if (err)
++    goto out;
++
++  err = lower_bound(data, &key_in, &key_out, tree,
++                    &elemaddr, &elemsize, &desc, 0);
++
++  if (err)
++    {
++      grub_btrfs_unmount(data);
++      return err;
++    }
++
++  if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF || elemaddr == 0)
++    {
++      r = next(data, &desc, &elemaddr, &elemsize, &key_out);
++    }
++
++  if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF) {
++    err = GRUB_ERR_FILE_NOT_FOUND;
++    grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("can't find root refs"));
++    goto out;
++  }
++
++  do
++    {
++      struct grub_btrfs_root_ref *ref;
++      char *p = NULL;
++
++      if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF)
++        {
++          r = 0;
++          break;
++        }
++
++      if (elemsize > allocated)
++        {
++          grub_free(buf);
++          allocated = 2 * elemsize;
++          buf = grub_malloc(allocated + 1);
++          if (!buf)
++            {
++              r = -grub_errno;
++              break;
++            }
++        }
++      ref = (struct grub_btrfs_root_ref *)buf;
++
++      err = grub_btrfs_read_logical(data, elemaddr, buf, elemsize, 0);
++      if (err)
++        {
++          r = -err;
++          break;
++        }
++        buf[elemsize] = 0;
++
++      find_pathname(data, ref->dirid, fs_root, ref->name, &p);
++
++      if (print)
++        {
++          if (num_only)
++            grub_printf("ID %"PRIuGRUB_UINT64_T"\n", key_out.offset);
++          else if (path_only)
++            grub_printf("%s\n", p);
++          else
++            grub_printf("ID %"PRIuGRUB_UINT64_T" path %s\n", key_out.offset, p);
++        } else {
++          char *old = output;
++          if (num_only)
++            output = grub_xasprintf("%s%"PRIuGRUB_UINT64_T"\n",
++                                    old ?: "", key_out.offset);
++          else if (path_only)
++            output = grub_xasprintf("%s%s\n", old ?: "", p);
++          else
++            output = grub_xasprintf("%sID %"PRIuGRUB_UINT64_T" path %s\n",
++                                    old ?: "", key_out.offset, p);
++
++          if (old)
++            grub_free(old);
++        }
++
++      r = next(data, &desc, &elemaddr, &elemsize, &key_out);
++  } while(r > 0);
++
++  if (output)
++    grub_env_set(varname, output);
++
++out:
++  free_iterator(&desc);
++  grub_btrfs_unmount(data);
++
++  grub_device_close (dev);
++
++  return 0;
++}
++
+ static struct grub_fs grub_btrfs_fs = {
+   .name = "btrfs",
+   .fs_dir = grub_btrfs_dir,
+@@ -2205,12 +2641,88 @@ static struct grub_fs grub_btrfs_fs = {
+ #endif
+ };
+ 
++static grub_command_t cmd_info;
++static grub_extcmd_t cmd_list_subvols;
++
++static char *
++subvolid_set_env (struct grub_env_var *var __attribute__ ((unused)),
++                  const char *val)
++{
++  unsigned long long result = 0;
++
++  grub_errno = GRUB_ERR_NONE;
++  if (*val)
++    {
++      result = grub_strtoull(val, NULL, 10);
++      if (grub_errno)
++        return NULL;
++    }
++
++  grub_free (btrfs_default_subvol);
++  btrfs_default_subvol = NULL;
++  btrfs_default_subvolid = result;
++  return grub_strdup(val);
++}
++
++static const char *
++subvolid_get_env (struct grub_env_var *var __attribute__ ((unused)),
++                  const char *val __attribute__ ((unused)))
++{
++  if (btrfs_default_subvol)
++    return grub_xasprintf("subvol:%s", btrfs_default_subvol);
++  else if (btrfs_default_subvolid)
++    return grub_xasprintf("%"PRIuGRUB_UINT64_T, btrfs_default_subvolid);
++  else
++    return "";
++}
++
++static char *
++subvol_set_env (struct grub_env_var *var __attribute__ ((unused)),
++                const char *val)
++{
++  grub_free (btrfs_default_subvol);
++  btrfs_default_subvol = grub_strdup (val);
++  btrfs_default_subvolid = 0;
++  return grub_strdup(val);
++}
++
++static const char *
++subvol_get_env (struct grub_env_var *var __attribute__ ((unused)),
++                const char *val __attribute__ ((unused)))
++{
++  if (btrfs_default_subvol)
++    return btrfs_default_subvol;
++  else if (btrfs_default_subvolid)
++    return grub_xasprintf("subvolid:%" PRIuGRUB_UINT64_T,
++                          btrfs_default_subvolid);
++  else
++    return "";
++}
++
+ GRUB_MOD_INIT (btrfs)
+ {
+   grub_fs_register (&grub_btrfs_fs);
++  cmd_info = grub_register_command("btrfs-info", grub_cmd_btrfs_info,
++				   "DEVICE",
++				   "Print BtrFS info about DEVICE.");
++  cmd_list_subvols = grub_register_extcmd("btrfs-list-subvols",
++					 grub_cmd_btrfs_list_subvols, 0,
++					 "[-p|-n] [-o var] DEVICE",
++					 "Print list of BtrFS subvolumes on "
++					 "DEVICE.", options);
++  grub_register_variable_hook ("btrfs_subvol", subvol_get_env,
++                               subvol_set_env);
++  grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env,
++                               subvolid_set_env);
+ }
+ 
+ GRUB_MOD_FINI (btrfs)
+ {
++  grub_register_variable_hook ("btrfs_subvol", NULL, NULL);
++  grub_register_variable_hook ("btrfs_subvolid", NULL, NULL);
++  grub_unregister_command (cmd_info);
++  grub_unregister_extcmd (cmd_list_subvols);
+   grub_fs_unregister (&grub_btrfs_fs);
+ }
++
++// vim: si et sw=2:
+diff --git a/include/grub/btrfs.h b/include/grub/btrfs.h
+index 9d93fb6c182..234ad976771 100644
+--- a/include/grub/btrfs.h
++++ b/include/grub/btrfs.h
+@@ -29,6 +29,7 @@ enum
+     GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM = 0x84,
+     GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF = 0x90,
+     GRUB_BTRFS_ITEM_TYPE_DEVICE = 0xd8,
++    GRUB_BTRFS_ITEM_TYPE_ROOT_REF = 0x9c,
+     GRUB_BTRFS_ITEM_TYPE_CHUNK = 0xe4
+   };
+ 
diff --git a/SOURCES/0058-export-btrfs_subvol-and-btrfs_subvolid.patch b/SOURCES/0058-export-btrfs_subvol-and-btrfs_subvolid.patch
new file mode 100644
index 0000000..d38a6b8
--- /dev/null
+++ b/SOURCES/0058-export-btrfs_subvol-and-btrfs_subvolid.patch
@@ -0,0 +1,26 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Chang <mchang@suse.com>
+Date: Wed, 18 Dec 2013 09:57:04 +0000
+Subject: [PATCH] export btrfs_subvol and btrfs_subvolid
+
+We should export btrfs_subvol and btrfs_subvolid to have both visible
+to subsidiary configuration files loaded using configfile.
+
+Signed-off-by: Michael Chang <mchang@suse.com>
+---
+ grub-core/fs/btrfs.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
+index f1fff7385b5..ad1b56b716d 100644
+--- a/grub-core/fs/btrfs.c
++++ b/grub-core/fs/btrfs.c
+@@ -2714,6 +2714,8 @@ GRUB_MOD_INIT (btrfs)
+                                subvol_set_env);
+   grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env,
+                                subvolid_set_env);
++  grub_env_export ("btrfs_subvol");
++  grub_env_export ("btrfs_subvolid");
+ }
+ 
+ GRUB_MOD_FINI (btrfs)
diff --git a/SOURCES/0059-grub2-btrfs-03-follow_default.patch b/SOURCES/0059-grub2-btrfs-03-follow_default.patch
new file mode 100644
index 0000000..2b9a136
--- /dev/null
+++ b/SOURCES/0059-grub2-btrfs-03-follow_default.patch
@@ -0,0 +1,196 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Chang <mchang@suse.com>
+Date: Thu, 21 Aug 2014 03:39:11 +0000
+Subject: [PATCH] grub2-btrfs-03-follow_default
+
+---
+ grub-core/fs/btrfs.c | 107 ++++++++++++++++++++++++++++++++++++---------------
+ 1 file changed, 76 insertions(+), 31 deletions(-)
+
+diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
+index ad1b56b716d..113c1f746c9 100644
+--- a/grub-core/fs/btrfs.c
++++ b/grub-core/fs/btrfs.c
+@@ -1256,6 +1256,7 @@ grub_btrfs_mount (grub_device_t dev)
+ {
+   struct grub_btrfs_data *data;
+   grub_err_t err;
++  const char *relpath = grub_env_get ("btrfs_relative_path");
+ 
+   if (!dev->disk)
+     {
+@@ -1286,11 +1287,14 @@ grub_btrfs_mount (grub_device_t dev)
+   data->devices_attached[0].dev = dev;
+   data->devices_attached[0].id = data->sblock.this_device.device_id;
+ 
+-  err = btrfs_handle_subvol (data);
+-  if (err)
++  if (relpath && (relpath[0] == '1' || relpath[0] == 'y'))
+     {
+-      grub_free (data);
+-      return NULL;
++      err = btrfs_handle_subvol (data);
++      if (err)
++      {
++        grub_free (data);
++        return NULL;
++      }
+     }
+ 
+   return data;
+@@ -1855,24 +1859,39 @@ find_path (struct grub_btrfs_data *data,
+   grub_size_t allocated = 0;
+   struct grub_btrfs_dir_item *direl = NULL;
+   struct grub_btrfs_key key_out;
++  int follow_default;
+   const char *ctoken;
+   grub_size_t ctokenlen;
+   char *path_alloc = NULL;
+   char *origpath = NULL;
+   unsigned symlinks_max = 32;
++  const char *relpath = grub_env_get ("btrfs_relative_path");
+ 
++  follow_default = 0;
+   origpath = grub_strdup (path);
+   if (!origpath)
+     return grub_errno;
+ 
+-  if (data->fs_tree)
++  if (relpath && (relpath[0] == '1' || relpath[0] == 'y'))
+     {
+-      *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
+-      *tree = data->fs_tree;
+-      /* This is a tree root, so everything starts at objectid 256 */
+-      key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK);
+-      key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
+-      key->offset = 0;
++      if (data->fs_tree)
++        {
++          *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
++          *tree = data->fs_tree;
++          /* This is a tree root, so everything starts at objectid 256 */
++          key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK);
++          key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
++          key->offset = 0;
++        }
++      else
++        {
++          *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
++          *tree = data->sblock.root_tree;
++          key->object_id = data->sblock.root_dir_objectid;
++          key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
++          key->offset = 0;
++          follow_default = 1;
++        }
+     }
+   else
+     {
+@@ -1883,15 +1902,23 @@ find_path (struct grub_btrfs_data *data,
+ 
+   while (1)
+     {
+-      while (path[0] == '/')
+-	path++;
+-      if (!path[0])
+-	break;
+-      slash = grub_strchr (path, '/');
+-      if (!slash)
+-	slash = path + grub_strlen (path);
+-      ctoken = path;
+-      ctokenlen = slash - path;
++      if (!follow_default)
++	{
++	  while (path[0] == '/')
++	    path++;
++	  if (!path[0])
++	    break;
++	  slash = grub_strchr (path, '/');
++	  if (!slash)
++	    slash = path + grub_strlen (path);
++	  ctoken = path;
++	  ctokenlen = slash - path;
++	}
++      else
++	{
++	  ctoken = "default";
++	  ctokenlen = sizeof ("default") - 1;
++	}
+ 
+       if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
+ 	{
+@@ -1902,7 +1929,9 @@ find_path (struct grub_btrfs_data *data,
+ 
+       if (ctokenlen == 1 && ctoken[0] == '.')
+ 	{
+-	  path = slash;
++	  if (!follow_default)
++	    path = slash;
++	  follow_default = 0;
+ 	  continue;
+ 	}
+       if (ctokenlen == 2 && ctoken[0] == '.' && ctoken[1] == '.')
+@@ -1933,8 +1962,9 @@ find_path (struct grub_btrfs_data *data,
+ 	  *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
+ 	  key->object_id = key_out.offset;
+ 
+-	  path = slash;
+-
++	  if (!follow_default)
++	    path = slash;
++	  follow_default = 0;
+ 	  continue;
+ 	}
+ 
+@@ -2003,7 +2033,9 @@ find_path (struct grub_btrfs_data *data,
+ 	  return err;
+ 	}
+ 
+-      path = slash;
++      if (!follow_default)
++	path = slash;
++      follow_default = 0;
+       if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK)
+ 	{
+ 	  struct grub_btrfs_inode inode;
+@@ -2053,14 +2085,26 @@ find_path (struct grub_btrfs_data *data,
+ 	  path = path_alloc = tmp;
+ 	  if (path[0] == '/')
+ 	    {
+-	      if (data->fs_tree)
++              if (relpath && (relpath[0] == '1' || relpath[0] == 'y'))
+ 		{
+-		  *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
+-		  *tree = data->fs_tree;
+-		  /* This is a tree root, so everything starts at objectid 256 */
+-		  key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK);
+-		  key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
+-		  key->offset = 0;
++	          if (data->fs_tree)
++		    {
++		      *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
++		      *tree = data->fs_tree;
++		      /* This is a tree root, so everything starts at objectid 256 */
++		      key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK);
++		      key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
++		      key->offset = 0;
++		    }
++		  else
++		    {
++	              *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
++	              *tree = data->sblock.root_tree;
++	              key->object_id = data->sblock.root_dir_objectid;
++	              key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
++	              key->offset = 0;
++	              follow_default = 1;
++		    }
+ 		}
+ 	      else
+ 		{
+@@ -2716,6 +2760,7 @@ GRUB_MOD_INIT (btrfs)
+                                subvolid_set_env);
+   grub_env_export ("btrfs_subvol");
+   grub_env_export ("btrfs_subvolid");
++  grub_env_export ("btrfs_relative_path");
+ }
+ 
+ GRUB_MOD_FINI (btrfs)
diff --git a/SOURCES/0060-grub2-btrfs-04-grub2-install.patch b/SOURCES/0060-grub2-btrfs-04-grub2-install.patch
new file mode 100644
index 0000000..f91c31a
--- /dev/null
+++ b/SOURCES/0060-grub2-btrfs-04-grub2-install.patch
@@ -0,0 +1,174 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Chang <mchang@suse.com>
+Date: Thu, 21 Aug 2014 03:39:11 +0000
+Subject: [PATCH] grub2-btrfs-04-grub2-install
+
+---
+ grub-core/osdep/linux/getroot.c |  7 +++++++
+ grub-core/osdep/unix/config.c   | 17 +++++++++++++++--
+ util/config.c                   | 10 ++++++++++
+ util/grub-install.c             | 15 +++++++++++++++
+ util/grub-mkrelpath.c           |  6 ++++++
+ include/grub/emu/config.h       |  1 +
+ 6 files changed, 54 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c
+index 001b818fe58..caf9b1ccd3f 100644
+--- a/grub-core/osdep/linux/getroot.c
++++ b/grub-core/osdep/linux/getroot.c
+@@ -376,6 +376,7 @@ get_btrfs_fs_prefix (const char *mount_path)
+   return NULL;
+ }
+ 
++int use_relative_path_on_btrfs = 0;
+ 
+ char **
+ grub_find_root_devices_from_mountinfo (const char *dir, char **relroot)
+@@ -519,6 +520,12 @@ again:
+ 	{
+ 	  ret = grub_find_root_devices_from_btrfs (dir);
+ 	  fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path);
++	  if (use_relative_path_on_btrfs)
++	    {
++	      if (fs_prefix)
++	        free (fs_prefix);
++	      fs_prefix = xstrdup ("/");
++	    }
+ 	}
+       else if (!retry && grub_strcmp (entries[i].fstype, "autofs") == 0)
+ 	{
+diff --git a/grub-core/osdep/unix/config.c b/grub-core/osdep/unix/config.c
+index 7d6325138ce..46a881530c0 100644
+--- a/grub-core/osdep/unix/config.c
++++ b/grub-core/osdep/unix/config.c
+@@ -82,6 +82,19 @@ grub_util_load_config (struct grub_util_config *cfg)
+   if (v)
+     cfg->grub_distributor = xstrdup (v);
+ 
++  v = getenv ("SUSE_BTRFS_SNAPSHOT_BOOTING");
++  if (v)
++    {
++      if (grub_strncmp(v, "true", sizeof ("true") - 1) == 0)
++        {
++          cfg->is_suse_btrfs_snapshot_enabled = 1;
++        }
++      else
++        {
++          cfg->is_suse_btrfs_snapshot_enabled = 0;
++        }
++    }
++
+   cfgfile = grub_util_get_config_filename ();
+   if (!grub_util_is_regular (cfgfile))
+     return;
+@@ -105,8 +118,8 @@ grub_util_load_config (struct grub_util_config *cfg)
+       *ptr++ = *iptr;
+     }
+ 
+-  strcpy (ptr, "'; printf \"GRUB_ENABLE_CRYPTODISK=%s\\nGRUB_DISTRIBUTOR=%s\\n\" "
+-	  "\"$GRUB_ENABLE_CRYPTODISK\" \"$GRUB_DISTRIBUTOR\"");
++  strcpy (ptr, "'; printf \"GRUB_ENABLE_CRYPTODISK=%s\\nGRUB_DISTRIBUTOR=%s\\nSUSE_BTRFS_SNAPSHOT_BOOTING=%s\\n\" "
++	  "\"$GRUB_ENABLE_CRYPTODISK\" \"$GRUB_DISTRIBUTOR\" \"$SUSE_BTRFS_SNAPSHOT_BOOTING\"");
+ 
+   argv[2] = script;
+   argv[3] = '\0';
+diff --git a/util/config.c b/util/config.c
+index ebcdd8f5e22..f044a880a76 100644
+--- a/util/config.c
++++ b/util/config.c
+@@ -42,6 +42,16 @@ grub_util_parse_config (FILE *f, struct grub_util_config *cfg, int simple)
+ 	    cfg->is_cryptodisk_enabled = 1;
+ 	  continue;
+ 	}
++      if (grub_strncmp (ptr, "SUSE_BTRFS_SNAPSHOT_BOOTING=",
++			sizeof ("SUSE_BTRFS_SNAPSHOT_BOOTING=") - 1) == 0)
++	{
++	  ptr += sizeof ("SUSE_BTRFS_SNAPSHOT_BOOTING=") - 1;
++	  if (*ptr == '"' || *ptr == '\'')
++	    ptr++;
++	  if (grub_strncmp(ptr, "true", sizeof ("true") - 1) == 0)
++	    cfg->is_suse_btrfs_snapshot_enabled = 1;
++	  continue;
++	}
+       if (grub_strncmp (ptr, "GRUB_DISTRIBUTOR=",
+ 			sizeof ("GRUB_DISTRIBUTOR=") - 1) == 0)
+ 	{
+diff --git a/util/grub-install.c b/util/grub-install.c
+index 0fbe7f78c6d..0f66f36d23a 100644
+--- a/util/grub-install.c
++++ b/util/grub-install.c
+@@ -827,6 +827,8 @@ fill_core_services (const char *core_services)
+   free (sysv_plist);
+ }
+ 
++extern int use_relative_path_on_btrfs;
++
+ int
+ main (int argc, char *argv[])
+ {
+@@ -860,6 +862,9 @@ main (int argc, char *argv[])
+ 
+   grub_util_load_config (&config);
+ 
++  if (config.is_suse_btrfs_snapshot_enabled)
++    use_relative_path_on_btrfs = 1;
++
+   if (!bootloader_id && config.grub_distributor)
+     {
+       char *ptr;
+@@ -1352,6 +1357,16 @@ main (int argc, char *argv[])
+       fprintf (load_cfg_f, "set debug='%s'\n",
+ 	      debug_image);
+     }
++
++  if (config.is_suse_btrfs_snapshot_enabled
++      && grub_strncmp(grub_fs->name, "btrfs", sizeof ("btrfs") - 1) == 0)
++    {
++      if (!load_cfg_f)
++        load_cfg_f = grub_util_fopen (load_cfg, "wb");
++      have_load_cfg = 1;
++      fprintf (load_cfg_f, "set btrfs_relative_path='y'\n");
++    }
++
+   char *prefix_drive = NULL;
+   char *install_drive = NULL;
+ 
+diff --git a/util/grub-mkrelpath.c b/util/grub-mkrelpath.c
+index 47a241a391b..5db7a9a7d97 100644
+--- a/util/grub-mkrelpath.c
++++ b/util/grub-mkrelpath.c
+@@ -40,9 +40,12 @@ struct arguments
+ };
+ 
+ static struct argp_option options[] = {
++  {"relative",  'r', 0, 0, "use relative path on btrfs", 0},
+   { 0, 0, 0, 0, 0, 0 }
+ };
+ 
++extern int use_relative_path_on_btrfs;
++
+ static error_t
+ argp_parser (int key, char *arg, struct argp_state *state)
+ {
+@@ -52,6 +55,9 @@ argp_parser (int key, char *arg, struct argp_state *state)
+ 
+   switch (key)
+     {
++    case 'r':
++      use_relative_path_on_btrfs = 1;
++      break;
+     case ARGP_KEY_ARG:
+       if (state->arg_num == 0)
+ 	arguments->pathname = xstrdup (arg);
+diff --git a/include/grub/emu/config.h b/include/grub/emu/config.h
+index 875d5896ce1..c9a7e5f4ade 100644
+--- a/include/grub/emu/config.h
++++ b/include/grub/emu/config.h
+@@ -37,6 +37,7 @@ struct grub_util_config
+ {
+   int is_cryptodisk_enabled;
+   char *grub_distributor;
++  int is_suse_btrfs_snapshot_enabled;
+ };
+ 
+ void
diff --git a/SOURCES/0061-grub2-btrfs-05-grub2-mkconfig.patch b/SOURCES/0061-grub2-btrfs-05-grub2-mkconfig.patch
new file mode 100644
index 0000000..8270505
--- /dev/null
+++ b/SOURCES/0061-grub2-btrfs-05-grub2-mkconfig.patch
@@ -0,0 +1,129 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Chang <mchang@suse.com>
+Date: Thu, 21 Aug 2014 03:39:11 +0000
+Subject: [PATCH] grub2-btrfs-05-grub2-mkconfig
+
+Signed-off-by: Michael Chang <mchang@suse.com>
+---
+ util/grub-mkconfig.in       |  3 ++-
+ util/grub-mkconfig_lib.in   |  4 ++++
+ util/grub.d/00_header.in    | 25 ++++++++++++++++++++++++-
+ util/grub.d/10_linux.in     |  4 ++++
+ util/grub.d/20_linux_xen.in |  4 ++++
+ 5 files changed, 38 insertions(+), 2 deletions(-)
+
+diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in
+index 005f093809b..535c0f02499 100644
+--- a/util/grub-mkconfig.in
++++ b/util/grub-mkconfig.in
+@@ -252,7 +252,8 @@ export GRUB_DEFAULT \
+   GRUB_BADRAM \
+   GRUB_OS_PROBER_SKIP_LIST \
+   GRUB_DISABLE_SUBMENU \
+-  GRUB_DEFAULT_DTB
++  GRUB_DEFAULT_DTB \
++  SUSE_BTRFS_SNAPSHOT_BOOTING
+ 
+ if test "x${grub_cfg}" != "x"; then
+   rm -f "${grub_cfg}.new"
+diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in
+index 42c2ea9ba50..fafeac95061 100644
+--- a/util/grub-mkconfig_lib.in
++++ b/util/grub-mkconfig_lib.in
+@@ -52,7 +52,11 @@ grub_warn ()
+ 
+ make_system_path_relative_to_its_root ()
+ {
++  if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ] ; then
++  "${grub_mkrelpath}" -r "$1"
++  else
+   "${grub_mkrelpath}" "$1"
++  fi
+ }
+ 
+ is_path_readable_by_grub ()
+diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in
+index 858b526c925..de727e6ee6b 100644
+--- a/util/grub.d/00_header.in
++++ b/util/grub.d/00_header.in
+@@ -27,6 +27,14 @@ export TEXTDOMAINDIR="@localedir@"
+ 
+ . "$pkgdatadir/grub-mkconfig_lib"
+ 
++if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ] &&
++   [ "x${GRUB_FS}" = "xbtrfs" ] ; then
++    cat <<EOF
++set btrfs_relative_path="y"
++export btrfs_relative_path
++EOF
++fi
++
+ # Do this as early as possible, since other commands might depend on it.
+ # (e.g. the `loadfont' command might need lvm or raid modules)
+ for i in ${GRUB_PRELOAD_MODULES} ; do
+@@ -45,7 +53,9 @@ if [ "x${GRUB_TIMEOUT_BUTTON}" = "x" ] ; then GRUB_TIMEOUT_BUTTON="$GRUB_TIMEOUT
+ cat << EOF
+ set pager=1
+ 
+-if [ -s \$prefix/grubenv ]; then
++if [ -f \${config_directory}/grubenv ]; then
++  load_env -f \${config_directory}/grubenv
++elif [ -s \$prefix/grubenv ]; then
+   load_env
+ fi
+ EOF
+@@ -356,3 +366,16 @@ fi
+ if [ "x${GRUB_BADRAM}" != "x" ] ; then
+   echo "badram ${GRUB_BADRAM}"
+ fi
++
++if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ] &&
++   [ "x${GRUB_ENABLE_BLSCFG}" = "xtrue" ] &&
++   [ "x${GRUB_FS}" = "xbtrfs" ] ; then
++    # Note: No $snapshot_num on *read-only* rollback!  (bsc#901487)
++    cat <<EOF
++if [ -n "\$extra_cmdline" ]; then
++  submenu "Bootable snapshot #\$snapshot_num" {
++    menuentry "If OK, run 'snapper rollback' and reboot." { true; }
++  }
++fi
++EOF
++fi
+diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
+index 0f3c19e30cc..cbfaca34cc7 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -66,10 +66,14 @@ fi
+ 
+ case x"$GRUB_FS" in
+     xbtrfs)
++	if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ]; then
++	GRUB_CMDLINE_LINUX="${GRUB_CMDLINE_LINUX} \${extra_cmdline}"
++	else
+ 	rootsubvol="`make_system_path_relative_to_its_root /`"
+ 	rootsubvol="${rootsubvol#/}"
+ 	if [ "x${rootsubvol}" != x ]; then
+ 	    GRUB_CMDLINE_LINUX="rootflags=subvol=${rootsubvol} ${GRUB_CMDLINE_LINUX}"
++	fi
+ 	fi;;
+     xzfs)
+ 	rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || true`
+diff --git a/util/grub.d/20_linux_xen.in b/util/grub.d/20_linux_xen.in
+index ada20775a14..e9e73b815fb 100644
+--- a/util/grub.d/20_linux_xen.in
++++ b/util/grub.d/20_linux_xen.in
+@@ -73,10 +73,14 @@ fi
+ 
+ case x"$GRUB_FS" in
+     xbtrfs)
++	if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ]; then
++	GRUB_CMDLINE_LINUX="${GRUB_CMDLINE_LINUX} \${extra_cmdline}"
++	else
+ 	rootsubvol="`make_system_path_relative_to_its_root /`"
+ 	rootsubvol="${rootsubvol#/}"
+ 	if [ "x${rootsubvol}" != x ]; then
+ 	    GRUB_CMDLINE_LINUX="rootflags=subvol=${rootsubvol} ${GRUB_CMDLINE_LINUX}"
++	fi
+ 	fi;;
+     xzfs)
+ 	rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || true`
diff --git a/SOURCES/0062-grub2-btrfs-06-subvol-mount.patch b/SOURCES/0062-grub2-btrfs-06-subvol-mount.patch
new file mode 100644
index 0000000..9a51175
--- /dev/null
+++ b/SOURCES/0062-grub2-btrfs-06-subvol-mount.patch
@@ -0,0 +1,537 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Chang <mchang@suse.com>
+Date: Tue, 9 Jul 2019 13:56:16 +0200
+Subject: [PATCH] grub2-btrfs-06-subvol-mount
+
+---
+ grub-core/fs/btrfs.c            | 195 +++++++++++++++++++++++++++++++++++++++-
+ grub-core/osdep/linux/getroot.c | 148 +++++++++++++++++++++++++++++-
+ util/grub-install.c             |  49 ++++++++++
+ include/grub/emu/getroot.h      |   5 ++
+ 4 files changed, 392 insertions(+), 5 deletions(-)
+
+diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
+index 113c1f746c9..d323746ecfa 100644
+--- a/grub-core/fs/btrfs.c
++++ b/grub-core/fs/btrfs.c
+@@ -41,6 +41,7 @@
+ #include <grub/command.h>
+ #include <grub/env.h>
+ #include <grub/extcmd.h>
++#include <grub/list.h>
+ #include <grub/crypto.h>
+ #include <grub/diskfilter.h>
+ #include <grub/safemath.h>
+@@ -266,6 +267,12 @@ static grub_err_t
+ grub_btrfs_read_logical (struct grub_btrfs_data *data,
+ 			 grub_disk_addr_t addr, void *buf, grub_size_t size,
+ 			 int recursion_depth);
++static grub_err_t
++get_root (struct grub_btrfs_data *data, struct grub_btrfs_key *key,
++	  grub_uint64_t *tree, grub_uint8_t *type);
++
++grub_uint64_t
++find_mtab_subvol_tree (const char *path, char **path_in_subvol);
+ 
+ static grub_err_t
+ read_sblock (grub_disk_t disk, struct grub_btrfs_superblock *sb)
+@@ -1223,9 +1230,26 @@ lookup_root_by_name(struct grub_btrfs_data *data, const char *path)
+   grub_err_t err;
+   grub_uint64_t tree = 0;
+   grub_uint8_t type;
++  grub_uint64_t saved_tree;
+   struct grub_btrfs_key key;
+ 
++  if (path[0] == '\0')
++    {
++      data->fs_tree = 0;
++      return GRUB_ERR_NONE;
++    }
++
++  err = get_root (data, &key, &tree, &type);
++  if (err)
++    return err;
++
++  saved_tree = data->fs_tree;
++  data->fs_tree = tree;
++
+   err = find_path (data, path, &key, &tree, &type);
++
++  data->fs_tree = saved_tree;
++
+   if (err)
+       return grub_error(GRUB_ERR_FILE_NOT_FOUND, "couldn't locate %s\n", path);
+ 
+@@ -2199,11 +2223,20 @@ grub_btrfs_dir (grub_device_t device, const char *path,
+   int r = 0;
+   grub_uint64_t tree;
+   grub_uint8_t type;
++  char *new_path = NULL;
+ 
+   if (!data)
+     return grub_errno;
+ 
+-  err = find_path (data, path, &key_in, &tree, &type);
++  tree = find_mtab_subvol_tree (path, &new_path);
++
++  if (tree)
++    data->fs_tree = tree;
++
++  err = find_path (data, new_path ? new_path : path, &key_in, &tree, &type);
++  if (new_path)
++    grub_free (new_path);
++
+   if (err)
+     {
+       grub_btrfs_unmount (data);
+@@ -2305,11 +2338,21 @@ grub_btrfs_open (struct grub_file *file, const char *name)
+   struct grub_btrfs_inode inode;
+   grub_uint8_t type;
+   struct grub_btrfs_key key_in;
++  grub_uint64_t tree;
++  char *new_path = NULL;
+ 
+   if (!data)
+     return grub_errno;
+ 
+-  err = find_path (data, name, &key_in, &data->tree, &type);
++  tree = find_mtab_subvol_tree (name, &new_path);
++
++  if (tree)
++    data->fs_tree = tree;
++
++  err = find_path (data, new_path ? new_path : name, &key_in, &data->tree, &type);
++  if (new_path)
++    grub_free (new_path);
++
+   if (err)
+     {
+       grub_btrfs_unmount (data);
+@@ -2480,6 +2523,150 @@ grub_cmd_btrfs_info (grub_command_t cmd __attribute__ ((unused)), int argc,
+   return 0;
+ }
+ 
++struct grub_btrfs_mtab
++{
++  struct grub_btrfs_mtab *next;
++  struct grub_btrfs_mtab **prev;
++  char *path;
++  char *subvol;
++  grub_uint64_t tree;
++};
++
++typedef struct grub_btrfs_mtab* grub_btrfs_mtab_t;
++
++static struct grub_btrfs_mtab *btrfs_mtab;
++
++#define FOR_GRUB_MTAB(var) FOR_LIST_ELEMENTS (var, btrfs_mtab)
++#define FOR_GRUB_MTAB_SAFE(var, next) FOR_LIST_ELEMENTS_SAFE((var), (next), btrfs_mtab)
++
++static void
++add_mountpoint (const char *path, const char *subvol, grub_uint64_t tree)
++{
++  grub_btrfs_mtab_t m = grub_malloc (sizeof (*m));
++
++  m->path = grub_strdup (path);
++  m->subvol = grub_strdup (subvol);
++  m->tree = tree;
++  grub_list_push (GRUB_AS_LIST_P (&btrfs_mtab), GRUB_AS_LIST (m));
++}
++
++static grub_err_t
++grub_cmd_btrfs_mount_subvol (grub_command_t cmd __attribute__ ((unused)), int argc,
++		     char **argv)
++{
++  char *devname, *dirname, *subvol;
++  struct grub_btrfs_key key_in;
++  grub_uint8_t type;
++  grub_uint64_t tree;
++  grub_uint64_t saved_tree;
++  grub_err_t err;
++  struct grub_btrfs_data *data = NULL;
++  grub_device_t dev = NULL;
++
++  if (argc < 3)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, "required <dev> <dir> and <subvol>");
++
++  devname = grub_file_get_device_name(argv[0]);
++  dev = grub_device_open (devname);
++  grub_free (devname);
++
++  if (!dev)
++    {
++      err = grub_errno;
++      goto err_out;
++    }
++
++  dirname = argv[1];
++  subvol = argv[2];
++
++  data = grub_btrfs_mount (dev);
++  if (!data)
++    {
++      err = grub_errno;
++      goto err_out;
++    }
++
++  err = find_path (data, dirname, &key_in, &tree, &type);
++  if (err)
++    goto err_out;
++
++  if (type !=  GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
++    {
++      err = grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
++      goto err_out;
++    }
++
++  err = get_root (data, &key_in, &tree, &type);
++
++  if (err)
++    goto err_out;
++
++  saved_tree = data->fs_tree;
++  data->fs_tree = tree;
++  err = find_path (data, subvol, &key_in, &tree, &type);
++  data->fs_tree = saved_tree;
++
++  if (err)
++    goto err_out;
++
++  if (key_in.object_id != grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0)
++    {
++      err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "%s: not a subvolume\n", subvol);
++      goto err_out;
++    }
++
++  grub_btrfs_unmount (data);
++  grub_device_close (dev);
++  add_mountpoint (dirname, subvol, tree);
++
++  return GRUB_ERR_NONE;
++
++err_out:
++
++  if (data)
++    grub_btrfs_unmount (data);
++
++  if (dev)
++    grub_device_close (dev);
++
++  return err;
++}
++
++grub_uint64_t
++find_mtab_subvol_tree (const char *path, char **path_in_subvol)
++{
++  grub_btrfs_mtab_t m, cm;
++  grub_uint64_t tree;
++
++  if (!path || !path_in_subvol)
++    return 0;
++
++  *path_in_subvol = NULL;
++  tree = 0;
++  cm = NULL;
++
++  FOR_GRUB_MTAB (m)
++    {
++      if (grub_strncmp (path, m->path, grub_strlen (m->path)) == 0)
++	{
++	  if (!cm)
++	    cm = m;
++	  else
++	    if (grub_strcmp (m->path, cm->path) > 0)
++	      cm = m;
++	}
++    }
++
++  if (cm)
++    {
++      const char *s = path + grub_strlen (cm->path);
++      *path_in_subvol = (s[0] == '\0') ? grub_strdup ("/") : grub_strdup (s);
++      tree = cm->tree;
++    }
++
++  return tree;
++}
++
+ static grub_err_t
+ get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree,
+             grub_uint64_t objectid, grub_uint64_t offset,
+@@ -2686,6 +2873,7 @@ static struct grub_fs grub_btrfs_fs = {
+ };
+ 
+ static grub_command_t cmd_info;
++static grub_command_t cmd_mount_subvol;
+ static grub_extcmd_t cmd_list_subvols;
+ 
+ static char *
+@@ -2749,6 +2937,9 @@ GRUB_MOD_INIT (btrfs)
+   cmd_info = grub_register_command("btrfs-info", grub_cmd_btrfs_info,
+ 				   "DEVICE",
+ 				   "Print BtrFS info about DEVICE.");
++  cmd_mount_subvol = grub_register_command("btrfs-mount-subvol", grub_cmd_btrfs_mount_subvol,
++				   "DEVICE DIRECTORY SUBVOL",
++				   "Set btrfs DEVICE the DIRECTORY a mountpoint of SUBVOL.");
+   cmd_list_subvols = grub_register_extcmd("btrfs-list-subvols",
+ 					 grub_cmd_btrfs_list_subvols, 0,
+ 					 "[-p|-n] [-o var] DEVICE",
+diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c
+index caf9b1ccd3f..28790307e00 100644
+--- a/grub-core/osdep/linux/getroot.c
++++ b/grub-core/osdep/linux/getroot.c
+@@ -107,6 +107,14 @@ struct btrfs_ioctl_search_key
+   grub_uint32_t unused[9];
+ };
+ 
++struct btrfs_ioctl_search_header {
++  grub_uint64_t transid;
++  grub_uint64_t objectid;
++  grub_uint64_t offset;
++  grub_uint32_t type;
++  grub_uint32_t len;
++};
++
+ struct btrfs_ioctl_search_args {
+   struct btrfs_ioctl_search_key key;
+   grub_uint64_t buf[(4096 - sizeof(struct btrfs_ioctl_search_key))
+@@ -378,6 +386,109 @@ get_btrfs_fs_prefix (const char *mount_path)
+ 
+ int use_relative_path_on_btrfs = 0;
+ 
++static char *
++get_btrfs_subvol (const char *path)
++{
++  struct btrfs_ioctl_ino_lookup_args args;
++  grub_uint64_t tree_id;
++  int fd = -1;
++  char *ret = NULL;
++
++  fd = open (path, O_RDONLY);
++
++  if (fd < 0)
++    return NULL;
++
++  memset (&args, 0, sizeof(args));
++  args.objectid = GRUB_BTRFS_TREE_ROOT_OBJECTID;
++
++  if (ioctl (fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
++    goto error;
++
++  tree_id = args.treeid;
++
++  while (tree_id != GRUB_BTRFS_ROOT_VOL_OBJECTID)
++    {
++      struct btrfs_ioctl_search_args sargs;
++      struct grub_btrfs_root_backref *br;
++      struct btrfs_ioctl_search_header *search_header;
++      char *old;
++      grub_uint16_t len;
++      grub_uint64_t inode_id;
++
++      memset (&sargs, 0, sizeof(sargs));
++
++      sargs.key.tree_id = 1;
++      sargs.key.min_objectid = tree_id;
++      sargs.key.max_objectid = tree_id;
++
++      sargs.key.min_offset = 0;
++      sargs.key.max_offset = ~0ULL;
++      sargs.key.min_transid = 0;
++      sargs.key.max_transid = ~0ULL;
++      sargs.key.min_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF;
++      sargs.key.max_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF;
++
++      sargs.key.nr_items = 1;
++
++      if (ioctl (fd, BTRFS_IOC_TREE_SEARCH, &sargs) < 0)
++	goto error;
++
++      if (sargs.key.nr_items == 0)
++	goto error;
++
++      search_header = (struct btrfs_ioctl_search_header *)sargs.buf;
++      br = (struct grub_btrfs_root_backref *) (search_header + 1);
++
++      len = grub_le_to_cpu16 (br->n);
++      inode_id = grub_le_to_cpu64 (br->inode_id);
++      tree_id = search_header->offset;
++
++      old = ret;
++      ret = malloc (len + 1);
++      memcpy (ret, br->name, len);
++      ret[len] = '\0';
++
++      if (inode_id != GRUB_BTRFS_TREE_ROOT_OBJECTID)
++	{
++	  char *s;
++
++	  memset(&args, 0, sizeof(args));
++	  args.treeid = search_header->offset;
++	  args.objectid = inode_id;
++
++	  if (ioctl (fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
++	    goto error;
++
++	  s = xasprintf ("%s%s", args.name, ret);
++	  free (ret);
++	  ret = s;
++	}
++
++      if (old)
++	{
++	  char *s = xasprintf ("%s/%s", ret, old);
++	  free (ret);
++	  free (old);
++	  ret = s;
++	}
++    }
++
++  close (fd);
++  return ret;
++
++error:
++
++  if (fd >= 0)
++    close (fd);
++  if (ret)
++    free (ret);
++
++  return NULL;
++}
++
++void (*grub_find_root_btrfs_mount_path_hook)(const char *mount_path);
++
+ char **
+ grub_find_root_devices_from_mountinfo (const char *dir, char **relroot)
+ {
+@@ -519,12 +630,15 @@ again:
+       else if (grub_strcmp (entries[i].fstype, "btrfs") == 0)
+ 	{
+ 	  ret = grub_find_root_devices_from_btrfs (dir);
+-	  fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path);
+ 	  if (use_relative_path_on_btrfs)
+ 	    {
+-	      if (fs_prefix)
+-	        free (fs_prefix);
+ 	      fs_prefix = xstrdup ("/");
++	      if (grub_find_root_btrfs_mount_path_hook)
++		grub_find_root_btrfs_mount_path_hook (entries[i].enc_path);
++	    }
++	  else
++	    {
++	      fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path);
+ 	    }
+ 	}
+       else if (!retry && grub_strcmp (entries[i].fstype, "autofs") == 0)
+@@ -1150,6 +1264,34 @@ grub_util_get_grub_dev_os (const char *os_dev)
+   return grub_dev;
+ }
+ 
++
++char *
++grub_util_get_btrfs_subvol (const char *path, char **mount_path)
++{
++  char *mp = NULL;
++
++  if (mount_path)
++    *mount_path = NULL;
++
++  auto void
++  mount_path_hook (const char *m)
++  {
++    mp = strdup (m);
++  }
++
++  grub_find_root_btrfs_mount_path_hook = mount_path_hook;
++  grub_free (grub_find_root_devices_from_mountinfo (path, NULL));
++  grub_find_root_btrfs_mount_path_hook = NULL;
++
++  if (!mp)
++    return NULL;
++
++  if (mount_path)
++    *mount_path = mp;
++
++  return get_btrfs_subvol (mp);
++}
++
+ char *
+ grub_make_system_path_relative_to_its_root_os (const char *path)
+ {
+diff --git a/util/grub-install.c b/util/grub-install.c
+index 0f66f36d23a..84ed6e88ecb 100644
+--- a/util/grub-install.c
++++ b/util/grub-install.c
+@@ -1569,6 +1569,55 @@ main (int argc, char *argv[])
+       prefix_drive = xasprintf ("(%s)", grub_drives[0]);
+     }
+ 
++#ifdef __linux__
++
++  if (config.is_suse_btrfs_snapshot_enabled
++      && grub_strncmp(grub_fs->name, "btrfs", sizeof ("btrfs") - 1) == 0)
++    {
++      char *subvol = NULL;
++      char *mount_path = NULL;
++      char **rootdir_devices = NULL;
++      char *rootdir_path = grub_util_path_concat (2, "/", rootdir);
++
++      if (grub_util_is_directory (rootdir_path))
++	rootdir_devices = grub_guess_root_devices (rootdir_path);
++
++      free (rootdir_path);
++
++      if (rootdir_devices && rootdir_devices[0])
++	if (grub_strcmp (rootdir_devices[0], grub_devices[0]) == 0)
++	  subvol = grub_util_get_btrfs_subvol (platdir, &mount_path);
++
++      if (subvol && mount_path)
++	{
++	  char *def_subvol;
++
++	  def_subvol = grub_util_get_btrfs_subvol ("/", NULL);
++
++	  if (def_subvol)
++	    {
++	      if (!load_cfg_f)
++		load_cfg_f = grub_util_fopen (load_cfg, "wb");
++	      have_load_cfg = 1;
++
++	      if (grub_strcmp (subvol, def_subvol) != 0)
++		fprintf (load_cfg_f, "btrfs-mount-subvol ($root) %s %s\n", mount_path, subvol);
++	      free (def_subvol);
++	    }
++	}
++
++      for (curdev = rootdir_devices; *curdev; curdev++)
++	free (*curdev);
++      if (rootdir_devices)
++	free (rootdir_devices);
++      if (subvol)
++	free (subvol);
++      if (mount_path)
++	free (mount_path);
++    }
++
++#endif
++
+   char mkimage_target[200];
+   const char *core_name = NULL;
+ 
+diff --git a/include/grub/emu/getroot.h b/include/grub/emu/getroot.h
+index 73fa2d34abb..9c642ae3fe3 100644
+--- a/include/grub/emu/getroot.h
++++ b/include/grub/emu/getroot.h
+@@ -53,6 +53,11 @@ char **
+ grub_find_root_devices_from_mountinfo (const char *dir, char **relroot);
+ #endif
+ 
++#ifdef __linux__
++char *
++grub_util_get_btrfs_subvol (const char *path, char **mount_path);
++#endif
++
+ /* Devmapper functions provided by getroot_devmapper.c.  */
+ void
+ grub_util_pull_devmapper (const char *os_dev);
diff --git a/SOURCES/0063-Fallback-to-old-subvol-name-scheme-to-support-old-sn.patch b/SOURCES/0063-Fallback-to-old-subvol-name-scheme-to-support-old-sn.patch
new file mode 100644
index 0000000..b02ab53
--- /dev/null
+++ b/SOURCES/0063-Fallback-to-old-subvol-name-scheme-to-support-old-sn.patch
@@ -0,0 +1,58 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Andrei Borzenkov <arvidjaar@gmail.com>
+Date: Tue, 21 Jun 2016 16:44:17 +0000
+Subject: [PATCH] Fallback to old subvol name scheme to support old snapshot
+ config
+
+Ref: bsc#953538
+---
+ grub-core/fs/btrfs.c | 32 +++++++++++++++++++++++++++++++-
+ 1 file changed, 31 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
+index d323746ecfa..673ded03522 100644
+--- a/grub-core/fs/btrfs.c
++++ b/grub-core/fs/btrfs.c
+@@ -1260,11 +1260,41 @@ lookup_root_by_name(struct grub_btrfs_data *data, const char *path)
+   return GRUB_ERR_NONE;
+ }
+ 
++static grub_err_t
++lookup_root_by_name_fallback(struct grub_btrfs_data *data, const char *path)
++{
++  grub_err_t err;
++  grub_uint64_t tree = 0;
++  grub_uint8_t type;
++  struct grub_btrfs_key key;
++
++  err = find_path (data, path, &key, &tree, &type);
++  if (err)
++      return grub_error(GRUB_ERR_FILE_NOT_FOUND, "couldn't locate %s\n", path);
++
++  if (key.object_id != grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0)
++    return grub_error(GRUB_ERR_BAD_FILE_TYPE, "%s: not a subvolume\n", path);
++
++  data->fs_tree = tree;
++  return GRUB_ERR_NONE;
++}
++
+ static grub_err_t
+ btrfs_handle_subvol(struct grub_btrfs_data *data __attribute__ ((unused)))
+ {
+   if (btrfs_default_subvol)
+-    return lookup_root_by_name(data, btrfs_default_subvol);
++    {
++      grub_err_t err;
++      err = lookup_root_by_name(data, btrfs_default_subvol);
++
++      /* Fallback to old schemes */
++      if (err == GRUB_ERR_FILE_NOT_FOUND)
++	{
++	  err = GRUB_ERR_NONE;
++	  return lookup_root_by_name_fallback(data, btrfs_default_subvol);
++	}
++      return err;
++    }
+ 
+   if (btrfs_default_subvolid)
+     return lookup_root_by_id(data, btrfs_default_subvolid);
diff --git a/SOURCES/0064-Grub-not-working-correctly-with-btrfs-snapshots-bsc-.patch b/SOURCES/0064-Grub-not-working-correctly-with-btrfs-snapshots-bsc-.patch
new file mode 100644
index 0000000..d4e20cd
--- /dev/null
+++ b/SOURCES/0064-Grub-not-working-correctly-with-btrfs-snapshots-bsc-.patch
@@ -0,0 +1,272 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Chang <mchang@suse.com>
+Date: Thu, 11 May 2017 08:56:57 +0000
+Subject: [PATCH] Grub not working correctly with btrfs snapshots (bsc#1026511)
+
+---
+ grub-core/fs/btrfs.c | 238 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 238 insertions(+)
+
+diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
+index 673ded03522..2b21cbaa67e 100644
+--- a/grub-core/fs/btrfs.c
++++ b/grub-core/fs/btrfs.c
+@@ -2887,6 +2887,238 @@ out:
+   return 0;
+ }
+ 
++static grub_err_t
++grub_btrfs_get_parent_subvol_path (struct grub_btrfs_data *data,
++		grub_uint64_t child_id,
++		const char *child_path,
++		grub_uint64_t *parent_id,
++		char **path_out)
++{
++  grub_uint64_t fs_root = 0;
++  struct grub_btrfs_key key_in = {
++    .object_id = child_id,
++    .type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF,
++    .offset = 0,
++  }, key_out;
++  struct grub_btrfs_root_ref *ref;
++  char *buf;
++  struct grub_btrfs_leaf_descriptor desc;
++  grub_size_t elemsize;
++  grub_disk_addr_t elemaddr;
++  grub_err_t err;
++  char *parent_path;
++
++  *parent_id = 0;
++  *path_out = 0;
++
++  err = lower_bound(data, &key_in, &key_out, data->sblock.root_tree,
++                    &elemaddr, &elemsize, &desc, 0);
++  if (err)
++    return err;
++
++  if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF || elemaddr == 0)
++    next(data, &desc, &elemaddr, &elemsize, &key_out);
++
++  if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF)
++    {
++      free_iterator(&desc);
++      return grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("can't find root backrefs"));
++    }
++
++  buf = grub_malloc(elemsize + 1);
++  if (!buf)
++    {
++      free_iterator(&desc);
++      return grub_errno;
++    }
++
++  err = grub_btrfs_read_logical(data, elemaddr, buf, elemsize, 0);
++  if (err)
++    {
++      grub_free(buf);
++      free_iterator(&desc);
++      return err;
++    }
++
++  buf[elemsize] = 0;
++  ref = (struct grub_btrfs_root_ref *)buf;
++
++  err = get_fs_root(data, data->sblock.root_tree, grub_le_to_cpu64 (key_out.offset),
++                    0, &fs_root);
++  if (err)
++    {
++      grub_free(buf);
++      free_iterator(&desc);
++      return err;
++    }
++
++  find_pathname(data, grub_le_to_cpu64 (ref->dirid), fs_root, ref->name, &parent_path);
++
++  if (child_path)
++    {
++      *path_out = grub_xasprintf ("%s/%s", parent_path, child_path);
++      grub_free (parent_path);
++    }
++  else
++    *path_out = parent_path;
++
++  *parent_id = grub_le_to_cpu64 (key_out.offset);
++
++  grub_free(buf);
++  free_iterator(&desc);
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_btrfs_get_default_subvolume_id (struct grub_btrfs_data *data, grub_uint64_t *id)
++{
++  grub_err_t err;
++  grub_disk_addr_t elemaddr;
++  grub_size_t elemsize;
++  struct grub_btrfs_key key, key_out;
++  struct grub_btrfs_dir_item *direl = NULL;
++  const char *ctoken = "default";
++  grub_size_t ctokenlen = sizeof ("default") - 1;
++
++  *id = 0;
++  key.object_id = data->sblock.root_dir_objectid;
++  key.type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
++  key.offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen));
++  err = lower_bound (data, &key, &key_out, data->sblock.root_tree, &elemaddr, &elemsize,
++			 NULL, 0);
++  if (err)
++    return err;
++
++  if (key_cmp (&key, &key_out) != 0)
++    return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found"));
++
++  struct grub_btrfs_dir_item *cdirel;
++  direl = grub_malloc (elemsize + 1);
++  err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize, 0);
++  if (err)
++    {
++      grub_free (direl);
++      return err;
++    }
++  for (cdirel = direl;
++       (grub_uint8_t *) cdirel - (grub_uint8_t *) direl
++       < (grub_ssize_t) elemsize;
++       cdirel = (void *) ((grub_uint8_t *) (direl + 1)
++       + grub_le_to_cpu16 (cdirel->n)
++       + grub_le_to_cpu16 (cdirel->m)))
++    {
++      if (ctokenlen == grub_le_to_cpu16 (cdirel->n)
++        && grub_memcmp (cdirel->name, ctoken, ctokenlen) == 0)
++      break;
++    }
++  if ((grub_uint8_t *) cdirel - (grub_uint8_t *) direl
++      >= (grub_ssize_t) elemsize)
++    {
++      grub_free (direl);
++      err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found"));
++      return err;
++    }
++
++  if (cdirel->key.type != GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM)
++    {
++      grub_free (direl);
++      err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found"));
++      return err;
++    }
++
++  *id = grub_le_to_cpu64 (cdirel->key.object_id);
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_cmd_btrfs_get_default_subvol (struct grub_extcmd_context *ctxt,
++			     int argc, char **argv)
++{
++  char *devname;
++  grub_device_t dev;
++  struct grub_btrfs_data *data;
++  grub_err_t err;
++  grub_uint64_t id;
++  char *subvol = NULL;
++  grub_uint64_t subvolid = 0;
++  char *varname = NULL;
++  char *output = NULL;
++  int path_only = ctxt->state[1].set;
++  int num_only = ctxt->state[2].set;
++
++  if (ctxt->state[0].set)
++    varname = ctxt->state[0].arg;
++
++  if (argc < 1)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
++
++  devname = grub_file_get_device_name(argv[0]);
++  if (!devname)
++    return grub_errno;
++
++  dev = grub_device_open (devname);
++  grub_free (devname);
++  if (!dev)
++    return grub_errno;
++
++  data = grub_btrfs_mount(dev);
++  if (!data)
++    {
++      grub_device_close (dev);
++      grub_dprintf ("btrfs", "failed to open fs\n");
++      grub_errno = GRUB_ERR_NONE;
++      return 0;
++    }
++
++  err = grub_btrfs_get_default_subvolume_id (data, &subvolid);
++  if (err)
++    {
++      grub_btrfs_unmount (data);
++      grub_device_close (dev);
++      return err;
++    }
++
++  id = subvolid;
++  while (id != GRUB_BTRFS_ROOT_VOL_OBJECTID)
++    {
++      grub_uint64_t parent_id;
++      char *path_out;
++
++      err = grub_btrfs_get_parent_subvol_path (data, grub_cpu_to_le64 (id), subvol, &parent_id, &path_out);
++      if (err)
++	{
++	  grub_btrfs_unmount (data);
++	  grub_device_close (dev);
++	  return err;
++	}
++
++      if (subvol)
++        grub_free (subvol);
++      subvol = path_out;
++      id = parent_id;
++    }
++
++  if (num_only && path_only)
++      output = grub_xasprintf ("%"PRIuGRUB_UINT64_T" /%s", subvolid, subvol);
++  else if (num_only)
++      output = grub_xasprintf ("%"PRIuGRUB_UINT64_T, subvolid);
++  else
++      output = grub_xasprintf ("/%s", subvol);
++
++  if (varname)
++    grub_env_set(varname, output);
++  else
++    grub_printf ("%s\n", output);
++
++  grub_free (output);
++  grub_free (subvol);
++
++  grub_btrfs_unmount (data);
++  grub_device_close (dev);
++
++  return GRUB_ERR_NONE;
++}
++
+ static struct grub_fs grub_btrfs_fs = {
+   .name = "btrfs",
+   .fs_dir = grub_btrfs_dir,
+@@ -2905,6 +3137,7 @@ static struct grub_fs grub_btrfs_fs = {
+ static grub_command_t cmd_info;
+ static grub_command_t cmd_mount_subvol;
+ static grub_extcmd_t cmd_list_subvols;
++static grub_extcmd_t cmd_get_default_subvol;
+ 
+ static char *
+ subvolid_set_env (struct grub_env_var *var __attribute__ ((unused)),
+@@ -2975,6 +3208,11 @@ GRUB_MOD_INIT (btrfs)
+ 					 "[-p|-n] [-o var] DEVICE",
+ 					 "Print list of BtrFS subvolumes on "
+ 					 "DEVICE.", options);
++  cmd_get_default_subvol = grub_register_extcmd("btrfs-get-default-subvol",
++					 grub_cmd_btrfs_get_default_subvol, 0,
++					 "[-p|-n] [-o var] DEVICE",
++					 "Print default BtrFS subvolume on "
++					 "DEVICE.", options);
+   grub_register_variable_hook ("btrfs_subvol", subvol_get_env,
+                                subvol_set_env);
+   grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env,
diff --git a/SOURCES/0065-Add-grub_efi_allocate_pool-and-grub_efi_free_pool-wr.patch b/SOURCES/0065-Add-grub_efi_allocate_pool-and-grub_efi_free_pool-wr.patch
new file mode 100644
index 0000000..bde7096
--- /dev/null
+++ b/SOURCES/0065-Add-grub_efi_allocate_pool-and-grub_efi_free_pool-wr.patch
@@ -0,0 +1,72 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 1 Jun 2017 09:59:56 -0400
+Subject: [PATCH] Add grub_efi_allocate_pool() and grub_efi_free_pool()
+ wrappers.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ include/grub/efi/efi.h | 36 ++++++++++++++++++++++++++++++++----
+ 1 file changed, 32 insertions(+), 4 deletions(-)
+
+diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
+index 585fa6662b6..03f9a9d0118 100644
+--- a/include/grub/efi/efi.h
++++ b/include/grub/efi/efi.h
+@@ -24,6 +24,10 @@
+ #include <grub/dl.h>
+ #include <grub/efi/api.h>
+ 
++/* Variables.  */
++extern grub_efi_system_table_t *EXPORT_VAR(grub_efi_system_table);
++extern grub_efi_handle_t EXPORT_VAR(grub_efi_image_handle);
++
+ /* Functions.  */
+ void *EXPORT_FUNC(grub_efi_locate_protocol) (grub_efi_guid_t *protocol,
+ 					     void *registration);
+@@ -60,6 +64,33 @@ EXPORT_FUNC(grub_efi_get_memory_map) (grub_efi_uintn_t *memory_map_size,
+ 				      grub_efi_uintn_t *descriptor_size,
+ 				      grub_efi_uint32_t *descriptor_version);
+ void grub_efi_memory_fini (void);
++
++static inline grub_efi_status_t
++__attribute__((__unused__))
++grub_efi_allocate_pool (grub_efi_memory_type_t pool_type,
++			grub_efi_uintn_t buffer_size,
++			void **buffer)
++{
++  grub_efi_boot_services_t *b;
++  grub_efi_status_t status;
++
++  b = grub_efi_system_table->boot_services;
++  status = efi_call_3 (b->allocate_pool, pool_type, buffer_size, buffer);
++  return status;
++}
++
++static inline grub_efi_status_t
++__attribute__((__unused__))
++grub_efi_free_pool (void *buffer)
++{
++  grub_efi_boot_services_t *b;
++  grub_efi_status_t status;
++
++  b = grub_efi_system_table->boot_services;
++  status = efi_call_1 (b->free_pool, buffer);
++  return status;
++}
++
+ grub_efi_loaded_image_t *EXPORT_FUNC(grub_efi_get_loaded_image) (grub_efi_handle_t image_handle);
+ void EXPORT_FUNC(grub_efi_print_device_path) (grub_efi_device_path_t *dp);
+ char *EXPORT_FUNC(grub_efi_get_filename) (grub_efi_device_path_t *dp);
+@@ -115,10 +146,7 @@ void grub_efi_init (void);
+ void grub_efi_fini (void);
+ void grub_efi_set_prefix (void);
+ 
+-/* Variables.  */
+-extern grub_efi_system_table_t *EXPORT_VAR(grub_efi_system_table);
+-extern grub_efi_handle_t EXPORT_VAR(grub_efi_image_handle);
+-
++/* More variables.  */
+ extern int EXPORT_VAR(grub_efi_is_finished);
+ 
+ struct grub_net_card;
diff --git a/SOURCES/0066-Use-grub_efi_.-memory-helpers-where-reasonable.patch b/SOURCES/0066-Use-grub_efi_.-memory-helpers-where-reasonable.patch
new file mode 100644
index 0000000..4fa92c0
--- /dev/null
+++ b/SOURCES/0066-Use-grub_efi_.-memory-helpers-where-reasonable.patch
@@ -0,0 +1,106 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 1 Jun 2017 10:06:38 -0400
+Subject: [PATCH] Use grub_efi_...() memory helpers where reasonable.
+
+This uses grub_efi_allocate_pool(), grub_efi_free_pool(), and
+grub_efi_free_pages() instead of open-coded efi_call_N() calls, so we
+get more reasonable type checking.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/loader/efi/chainloader.c | 24 +++++++++---------------
+ 1 file changed, 9 insertions(+), 15 deletions(-)
+
+diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
+index 3ff305b1d32..ba3d2930197 100644
+--- a/grub-core/loader/efi/chainloader.c
++++ b/grub-core/loader/efi/chainloader.c
+@@ -65,7 +65,7 @@ grub_chainloader_unload (void)
+ 
+   b = grub_efi_system_table->boot_services;
+   efi_call_1 (b->unload_image, image_handle);
+-  efi_call_2 (b->free_pages, address, pages);
++  grub_efi_free_pages (address, pages);
+ 
+   grub_free (file_path);
+   grub_free (cmdline);
+@@ -108,7 +108,7 @@ grub_chainloader_boot (void)
+     }
+ 
+   if (exit_data)
+-    efi_call_1 (b->free_pool, exit_data);
++    grub_efi_free_pool (exit_data);
+ 
+   grub_loader_unset ();
+ 
+@@ -523,10 +523,9 @@ grub_efi_get_media_file_path (grub_efi_device_path_t *dp)
+ static grub_efi_boolean_t
+ handle_image (void *data, grub_efi_uint32_t datasize)
+ {
+-  grub_efi_boot_services_t *b;
+   grub_efi_loaded_image_t *li, li_bak;
+   grub_efi_status_t efi_status;
+-  char *buffer = NULL;
++  void *buffer = NULL;
+   char *buffer_aligned = NULL;
+   grub_efi_uint32_t i;
+   struct grub_pe32_section_table *section;
+@@ -537,8 +536,6 @@ handle_image (void *data, grub_efi_uint32_t datasize)
+   int found_entry_point = 0;
+   int rc;
+ 
+-  b = grub_efi_system_table->boot_services;
+-
+   rc = read_header (data, datasize, &context);
+   if (rc < 0)
+     {
+@@ -578,8 +575,8 @@ handle_image (void *data, grub_efi_uint32_t datasize)
+   grub_dprintf ("chain", "image size is %08"PRIxGRUB_UINT64_T", datasize is %08x\n",
+ 	       context.image_size, datasize);
+ 
+-  efi_status = efi_call_3 (b->allocate_pool, GRUB_EFI_LOADER_DATA,
+-			   buffer_size, &buffer);
++  efi_status = grub_efi_allocate_pool (GRUB_EFI_LOADER_DATA, buffer_size,
++				       &buffer);
+ 
+   if (efi_status != GRUB_EFI_SUCCESS)
+     {
+@@ -811,14 +808,14 @@ handle_image (void *data, grub_efi_uint32_t datasize)
+ 
+   grub_dprintf ("chain", "entry_point returned %ld\n", efi_status);
+   grub_memcpy (li, &li_bak, sizeof (grub_efi_loaded_image_t));
+-  efi_status = efi_call_1 (b->free_pool, buffer);
++  efi_status = grub_efi_free_pool (buffer);
+ 
+   return 1;
+ 
+ error_exit:
+   grub_dprintf ("chain", "error_exit: grub_errno: %d\n", grub_errno);
+   if (buffer)
+-      efi_call_1 (b->free_pool, buffer);
++    grub_efi_free_pool (buffer);
+ 
+   return 0;
+ }
+@@ -826,10 +823,7 @@ error_exit:
+ static grub_err_t
+ grub_secureboot_chainloader_unload (void)
+ {
+-  grub_efi_boot_services_t *b;
+-
+-  b = grub_efi_system_table->boot_services;
+-  efi_call_2 (b->free_pages, address, pages);
++  grub_efi_free_pages (address, pages);
+   grub_free (file_path);
+   grub_free (cmdline);
+   cmdline = 0;
+@@ -1096,7 +1090,7 @@ fail:
+   grub_free (file_path);
+ 
+   if (address)
+-    efi_call_2 (b->free_pages, address, pages);
++    grub_efi_free_pages (address, pages);
+ 
+   if (cmdline)
+     grub_free (cmdline);
diff --git a/SOURCES/0067-Add-PRIxGRUB_EFI_STATUS-and-use-it.patch b/SOURCES/0067-Add-PRIxGRUB_EFI_STATUS-and-use-it.patch
new file mode 100644
index 0000000..1a96b43
--- /dev/null
+++ b/SOURCES/0067-Add-PRIxGRUB_EFI_STATUS-and-use-it.patch
@@ -0,0 +1,48 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 1 Jun 2017 10:07:50 -0400
+Subject: [PATCH] Add PRIxGRUB_EFI_STATUS and use it.
+
+This avoids syntax checkers getting confused about if it's llx or lx.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/loader/efi/chainloader.c | 3 ++-
+ include/grub/efi/api.h             | 9 +++++++++
+ 2 files changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
+index ba3d2930197..47f5aa14817 100644
+--- a/grub-core/loader/efi/chainloader.c
++++ b/grub-core/loader/efi/chainloader.c
+@@ -806,7 +806,8 @@ handle_image (void *data, grub_efi_uint32_t datasize)
+   efi_status = efi_call_2 (entry_point, grub_efi_image_handle,
+ 			   grub_efi_system_table);
+ 
+-  grub_dprintf ("chain", "entry_point returned %ld\n", efi_status);
++  grub_dprintf ("chain", "entry_point returned 0x%"PRIxGRUB_EFI_STATUS"\n",
++		efi_status);
+   grub_memcpy (li, &li_bak, sizeof (grub_efi_loaded_image_t));
+   efi_status = grub_efi_free_pool (buffer);
+ 
+diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
+index 117469450d3..99628801478 100644
+--- a/include/grub/efi/api.h
++++ b/include/grub/efi/api.h
+@@ -546,7 +546,16 @@ typedef grub_uint64_t grub_efi_uint64_t;
+ typedef grub_uint8_t grub_efi_char8_t;
+ typedef grub_uint16_t grub_efi_char16_t;
+ 
++
+ typedef grub_efi_uintn_t grub_efi_status_t;
++/* Make grub_efi_status_t reasonably printable. */
++#if GRUB_CPU_SIZEOF_VOID_P == 8
++#define PRIxGRUB_EFI_STATUS "lx"
++#define PRIdGRUB_EFI_STATUS "ld"
++#else
++#define PRIxGRUB_EFI_STATUS "llx"
++#define PRIdGRUB_EFI_STATUS "lld"
++#endif
+ 
+ #define GRUB_EFI_ERROR_CODE(value)	\
+   ((((grub_efi_status_t) 1) << (sizeof (grub_efi_status_t) * 8 - 1)) | (value))
diff --git a/SOURCES/0068-don-t-use-int-for-efi-status.patch b/SOURCES/0068-don-t-use-int-for-efi-status.patch
new file mode 100644
index 0000000..44d3555
--- /dev/null
+++ b/SOURCES/0068-don-t-use-int-for-efi-status.patch
@@ -0,0 +1,22 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 26 Jun 2017 12:44:59 -0400
+Subject: [PATCH] don't use int for efi status
+
+---
+ grub-core/kern/efi/efi.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
+index 05d8237a9b2..ae9885edb84 100644
+--- a/grub-core/kern/efi/efi.c
++++ b/grub-core/kern/efi/efi.c
+@@ -167,7 +167,7 @@ grub_reboot (void)
+ void
+ grub_exit (int retval)
+ {
+-  int rc = GRUB_EFI_LOAD_ERROR;
++  grub_efi_status_t rc = GRUB_EFI_LOAD_ERROR;
+ 
+   if (retval == 0)
+     rc = GRUB_EFI_SUCCESS;
diff --git a/SOURCES/0069-make-GRUB_MOD_INIT-declare-its-function-prototypes.patch b/SOURCES/0069-make-GRUB_MOD_INIT-declare-its-function-prototypes.patch
new file mode 100644
index 0000000..1014900
--- /dev/null
+++ b/SOURCES/0069-make-GRUB_MOD_INIT-declare-its-function-prototypes.patch
@@ -0,0 +1,29 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 26 Jun 2017 12:46:23 -0400
+Subject: [PATCH] make GRUB_MOD_INIT() declare its function prototypes.
+
+---
+ include/grub/dl.h | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/include/grub/dl.h b/include/grub/dl.h
+index b3753c9ca26..91933b85f2c 100644
+--- a/include/grub/dl.h
++++ b/include/grub/dl.h
+@@ -54,6 +54,7 @@ grub_mod_fini (void)
+ 
+ #define GRUB_MOD_INIT(name)	\
+ static void grub_mod_init (grub_dl_t mod __attribute__ ((unused))) __attribute__ ((used)); \
++extern void grub_##name##_init (void); \
+ void \
+ grub_##name##_init (void) { grub_mod_init (0); } \
+ static void \
+@@ -61,6 +62,7 @@ grub_mod_init (grub_dl_t mod __attribute__ ((unused)))
+ 
+ #define GRUB_MOD_FINI(name)	\
+ static void grub_mod_fini (void) __attribute__ ((used)); \
++extern void grub_##name##_fini (void); \
+ void \
+ grub_##name##_fini (void) { grub_mod_fini (); } \
+ static void \
diff --git a/SOURCES/0070-Don-t-guess-boot-efi-as-HFS-on-ppc-machines-in-grub-.patch b/SOURCES/0070-Don-t-guess-boot-efi-as-HFS-on-ppc-machines-in-grub-.patch
new file mode 100644
index 0000000..890aa34
--- /dev/null
+++ b/SOURCES/0070-Don-t-guess-boot-efi-as-HFS-on-ppc-machines-in-grub-.patch
@@ -0,0 +1,41 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 20 Apr 2017 13:29:06 -0400
+Subject: [PATCH] Don't guess /boot/efi/ as HFS+ on ppc machines in
+ grub-install
+
+This should never be trying this, and since we've consolidated the
+grubenv to always be on /boot/efi/EFI/fedora/, this code causes it to
+always make the wrong decision.
+
+Resolves: rhbz#1484474
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ util/grub-install.c | 12 +-----------
+ 1 file changed, 1 insertion(+), 11 deletions(-)
+
+diff --git a/util/grub-install.c b/util/grub-install.c
+index 84ed6e88ecb..a2bec7446cb 100644
+--- a/util/grub-install.c
++++ b/util/grub-install.c
+@@ -1190,18 +1190,8 @@ main (int argc, char *argv[])
+ 	  char *d;
+ 
+ 	  is_guess = 1;
+-	  d = grub_util_path_concat (2, bootdir, "macppc");
+-	  if (!grub_util_is_directory (d))
+-	    {
+-	      free (d);
+-	      d = grub_util_path_concat (2, bootdir, "efi");
+-	    }
+ 	  /* Find the Mac HFS(+) System Partition.  */
+-	  if (!grub_util_is_directory (d))
+-	    {
+-	      free (d);
+-	      d = grub_util_path_concat (2, bootdir, "EFI");
+-	    }
++	  d = grub_util_path_concat (2, bootdir, "macppc");
+ 	  if (!grub_util_is_directory (d))
+ 	    {
+ 	      free (d);
diff --git a/SOURCES/0071-20_linux_xen-load-xen-or-multiboot-2-modules-as-need.patch b/SOURCES/0071-20_linux_xen-load-xen-or-multiboot-2-modules-as-need.patch
new file mode 100644
index 0000000..3fd779f
--- /dev/null
+++ b/SOURCES/0071-20_linux_xen-load-xen-or-multiboot-2-modules-as-need.patch
@@ -0,0 +1,47 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 9 Jul 2019 14:31:19 +0200
+Subject: [PATCH] 20_linux_xen: load xen or multiboot{,2} modules as needed.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ util/grub.d/20_linux_xen.in | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/util/grub.d/20_linux_xen.in b/util/grub.d/20_linux_xen.in
+index e9e73b815fb..c23b064be6c 100644
+--- a/util/grub.d/20_linux_xen.in
++++ b/util/grub.d/20_linux_xen.in
+@@ -153,6 +153,7 @@ linux_entry_xsm ()
+         else
+             xen_rm_opts="no-real-mode edd=off"
+         fi
++	insmod ${xen_module}
+ 	${xen_loader}	${rel_xen_dirname}/${xen_basename} placeholder ${xen_args} \${xen_rm_opts}
+ 	echo	'$(echo "$lmessage" | grub_quote)'
+ 	${module_loader}	${rel_dirname}/${basename} placeholder root=${linux_root_device_thisversion} ro ${args}
+@@ -166,6 +167,7 @@ EOF
+     done
+     sed "s/^/$submenu_indentation/" << EOF
+ 	echo	'$(echo "$message" | grub_quote)'
++	insmod ${xen_module}
+ 	${module_loader}	--nounzip   $(echo $initrd_path)
+ EOF
+   fi
+@@ -253,13 +255,16 @@ while [ "x${xen_list}" != "x" ] ; do
+ 	echo "	submenu '$(gettext_printf "Xen hypervisor, version %s" "${xen_version}" | grub_quote)' \$menuentry_id_option 'xen-hypervisor-$xen_version-$boot_device_id' {"
+     fi
+     if ($grub_file --is-arm64-efi $current_xen); then
++	xen_module="xen_boot"
+ 	xen_loader="xen_hypervisor"
+ 	module_loader="xen_module"
+     else
+ 	if ($grub_file --is-x86-multiboot2 $current_xen); then
++	    xen_module="multiboot2"
+ 	    xen_loader="multiboot2"
+ 	    module_loader="module2"
+ 	else
++	    xen_module="multiboot"
+ 	    xen_loader="multiboot"
+ 	    module_loader="module"
+         fi
diff --git a/SOURCES/0072-Make-pmtimer-tsc-calibration-not-take-51-seconds-to-.patch b/SOURCES/0072-Make-pmtimer-tsc-calibration-not-take-51-seconds-to-.patch
new file mode 100644
index 0000000..acfb116
--- /dev/null
+++ b/SOURCES/0072-Make-pmtimer-tsc-calibration-not-take-51-seconds-to-.patch
@@ -0,0 +1,211 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 7 Nov 2017 17:12:17 -0500
+Subject: [PATCH] Make pmtimer tsc calibration not take 51 seconds to fail.
+
+On my laptop running at 2.4GHz, if I run a VM where tsc calibration
+using pmtimer will fail presuming a broken pmtimer, it takes ~51 seconds
+to do so (as measured with the stopwatch on my phone), with a tsc delta
+of 0x1cd1c85300, or around 125 billion cycles.
+
+If instead of trying to wait for 5-200ms to show up on the pmtimer, we try
+to wait for 5-200us, it decides it's broken in ~0x2626aa0 TSCs, aka ~2.4
+million cycles, or more or less instantly.
+
+Additionally, this reading the pmtimer was returning 0xffffffff anyway,
+and that's obviously an invalid return.  I've added a check for that and
+0 so we don't bother waiting for the test if what we're seeing is dead
+pins with no response at all.
+
+If "debug" is includes "pmtimer", you will see one of the following
+three outcomes.  If pmtimer gives all 0 or all 1 bits, you will see:
+
+kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 1
+kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 2
+kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 3
+kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 4
+kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 5
+kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 6
+kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 7
+kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 8
+kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 9
+kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 10
+kern/i386/tsc_pmtimer.c:78: timer is broken; giving up.
+
+This outcome was tested using qemu+kvm with UEFI (OVMF) firmware and
+these options: -machine pc-q35-2.10 -cpu Broadwell-noTSX
+
+If pmtimer gives any other bit patterns but is not actually marching
+forward fast enough to use for clock calibration, you will see:
+
+kern/i386/tsc_pmtimer.c:121: pmtimer delta is 0x0 (1904 iterations)
+kern/i386/tsc_pmtimer.c:124: tsc delta is implausible: 0x2626aa0
+
+This outcome was tested using grub compiled with GRUB_PMTIMER_IGNORE_BAD_READS
+defined (so as not to trip the bad read test) using qemu+kvm with UEFI
+(OVMF) firmware, and these options: -machine pc-q35-2.10 -cpu Broadwell-noTSX
+
+If pmtimer actually works, you'll see something like:
+
+kern/i386/tsc_pmtimer.c:121: pmtimer delta is 0x0 (1904 iterations)
+kern/i386/tsc_pmtimer.c:124: tsc delta is implausible: 0x2626aa0
+
+This outcome was tested using qemu+kvm with UEFI (OVMF) firmware, and
+these options: -machine pc-i440fx-2.4 -cpu Broadwell-noTSX
+
+I've also tested this outcome on a real Intel Xeon E3-1275v3 on an Intel
+Server Board S1200V3RPS using the SDV.RP.B8 "Release" build here:
+https://firmware.intel.com/sites/default/files/UEFIDevKit_S1200RP_vB8.zip
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/kern/i386/tsc_pmtimer.c | 109 +++++++++++++++++++++++++++++++-------
+ 1 file changed, 89 insertions(+), 20 deletions(-)
+
+diff --git a/grub-core/kern/i386/tsc_pmtimer.c b/grub-core/kern/i386/tsc_pmtimer.c
+index c9c36169978..ca15c3aacd7 100644
+--- a/grub-core/kern/i386/tsc_pmtimer.c
++++ b/grub-core/kern/i386/tsc_pmtimer.c
+@@ -28,40 +28,101 @@
+ #include <grub/acpi.h>
+ #include <grub/cpu/io.h>
+ 
++/*
++ * Define GRUB_PMTIMER_IGNORE_BAD_READS if you're trying to test a timer that's
++ * present but doesn't keep time well.
++ */
++// #define GRUB_PMTIMER_IGNORE_BAD_READS
++
+ grub_uint64_t
+ grub_pmtimer_wait_count_tsc (grub_port_t pmtimer,
+ 			     grub_uint16_t num_pm_ticks)
+ {
+   grub_uint32_t start;
+-  grub_uint32_t last;
+-  grub_uint32_t cur, end;
++  grub_uint64_t cur, end;
+   grub_uint64_t start_tsc;
+   grub_uint64_t end_tsc;
+-  int num_iter = 0;
++  unsigned int num_iter = 0;
++#ifndef GRUB_PMTIMER_IGNORE_BAD_READS
++  int bad_reads = 0;
++#endif
+ 
+-  start = grub_inl (pmtimer) & 0xffffff;
+-  last = start;
++  /*
++   * Some timers are 24-bit and some are 32-bit, but it doesn't make much
++   * difference to us.  Caring which one we have isn't really worth it since
++   * the low-order digits will give us enough data to calibrate TSC.  So just
++   * mask the top-order byte off.
++   */
++  cur = start = grub_inl (pmtimer) & 0xffffffUL;
+   end = start + num_pm_ticks;
+   start_tsc = grub_get_tsc ();
+   while (1)
+     {
+-      cur = grub_inl (pmtimer) & 0xffffff;
+-      if (cur < last)
+-	cur |= 0x1000000;
+-      num_iter++;
++      cur &= 0xffffffffff000000ULL;
++      cur |= grub_inl (pmtimer) & 0xffffffUL;
++
++      end_tsc = grub_get_tsc();
++
++#ifndef GRUB_PMTIMER_IGNORE_BAD_READS
++      /*
++       * If we get 10 reads in a row that are obviously dead pins, there's no
++       * reason to do this thousands of times.
++       */
++      if (cur == 0xffffffUL || cur == 0)
++	{
++	  bad_reads++;
++	  grub_dprintf ("pmtimer",
++			"pmtimer: 0x%"PRIxGRUB_UINT64_T" bad_reads: %d\n",
++			cur, bad_reads);
++	  grub_dprintf ("pmtimer", "timer is broken; giving up.\n");
++
++	  if (bad_reads == 10)
++	    return 0;
++	}
++#endif
++
++      if (cur < start)
++	cur += 0x1000000;
++
+       if (cur >= end)
+ 	{
+-	  end_tsc = grub_get_tsc ();
++	  grub_dprintf ("pmtimer", "pmtimer delta is 0x%"PRIxGRUB_UINT64_T"\n",
++			cur - start);
++	  grub_dprintf ("pmtimer", "tsc delta is 0x%"PRIxGRUB_UINT64_T"\n",
++			end_tsc - start_tsc);
+ 	  return end_tsc - start_tsc;
+ 	}
+-      /* Check for broken PM timer.
+-	 50000000 TSCs is between 5 ms (10GHz) and 200 ms (250 MHz)
+-	 if after this time we still don't have 1 ms on pmtimer, then
+-	 pmtimer is broken.
++
++      /*
++       * Check for broken PM timer.  1ms at 10GHz should be 1E+7 TSCs; at
++       * 250MHz it should be 2.5E6.  So if after 4E+7 TSCs on a 10GHz machine,
++       * we should have seen pmtimer show 4ms of change (i.e. cur =~
++       * start+14320); on a 250MHz machine that should be 16ms (start+57280).
++       * If after this a time we still don't have 1ms on pmtimer, then pmtimer
++       * is broken.
++       *
++       * Likewise, if our code is perfectly efficient and introduces no delays
++       * whatsoever, on a 10GHz system we should see a TSC delta of 3580 in
++       * ~3580 iterations.  On a 250MHz machine that should be ~900 iterations.
++       *
++       * With those factors in mind, there are two limits here.  There's a hard
++       * limit here at 8x our desired pm timer delta, picked as an arbitrarily
++       * large value that's still not a lot of time to humans, because if we
++       * get that far this is either an implausibly fast machine or the pmtimer
++       * is not running.  And there's another limit on 4x our 10GHz tsc delta
++       * without seeing cur converge on our target value.
+        */
+-      if ((num_iter & 0xffffff) == 0 && grub_get_tsc () - start_tsc > 5000000) {
+-	return 0;
+-      }
++      if ((++num_iter > (grub_uint32_t)num_pm_ticks << 3UL) ||
++	  end_tsc - start_tsc > 40000000)
++	{
++	  grub_dprintf ("pmtimer",
++			"pmtimer delta is 0x%"PRIxGRUB_UINT64_T" (%u iterations)\n",
++			cur - start, num_iter);
++	  grub_dprintf ("pmtimer",
++			"tsc delta is implausible: 0x%"PRIxGRUB_UINT64_T"\n",
++			end_tsc - start_tsc);
++	  return 0;
++	}
+     }
+ }
+ 
+@@ -74,12 +135,20 @@ grub_tsc_calibrate_from_pmtimer (void)
+ 
+   fadt = grub_acpi_find_fadt ();
+   if (!fadt)
+-    return 0;
++    {
++      grub_dprintf ("pmtimer", "No FADT found; not using pmtimer.\n");
++      return 0;
++    }
+   pmtimer = fadt->pmtimer;
+   if (!pmtimer)
+-    return 0;
++    {
++      grub_dprintf ("pmtimer", "FADT does not specify pmtimer; skipping.\n");
++      return 0;
++    }
+ 
+-  /* It's 3.579545 MHz clock. Wait 1 ms.  */
++  /*
++   * It's 3.579545 MHz clock. Wait 1 ms.
++   */
+   tsc_diff = grub_pmtimer_wait_count_tsc (pmtimer, 3580);
+   if (tsc_diff == 0)
+     return 0;
diff --git a/SOURCES/0073-align-struct-efi_variable-better.patch b/SOURCES/0073-align-struct-efi_variable-better.patch
new file mode 100644
index 0000000..ec26def
--- /dev/null
+++ b/SOURCES/0073-align-struct-efi_variable-better.patch
@@ -0,0 +1,33 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 27 Feb 2018 13:55:35 -0500
+Subject: [PATCH] align struct efi_variable better...
+
+---
+ include/grub/efiemu/runtime.h | 2 +-
+ include/grub/types.h          | 1 +
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/include/grub/efiemu/runtime.h b/include/grub/efiemu/runtime.h
+index 36d2dedf47e..9d93ba88bac 100644
+--- a/include/grub/efiemu/runtime.h
++++ b/include/grub/efiemu/runtime.h
+@@ -33,5 +33,5 @@ struct efi_variable
+   grub_uint32_t namelen;
+   grub_uint32_t size;
+   grub_efi_uint32_t attributes;
+-} GRUB_PACKED;
++} GRUB_PACKED GRUB_ALIGNED(8);
+ #endif /* ! GRUB_EFI_EMU_RUNTIME_HEADER */
+diff --git a/include/grub/types.h b/include/grub/types.h
+index 0a3ff159136..ba446d99040 100644
+--- a/include/grub/types.h
++++ b/include/grub/types.h
+@@ -29,6 +29,7 @@
+ #else
+ #define GRUB_PACKED __attribute__ ((packed))
+ #endif
++#define GRUB_ALIGNED(x) __attribute__((aligned (x)))
+ 
+ #ifdef GRUB_BUILD
+ # define GRUB_CPU_SIZEOF_VOID_P	BUILD_SIZEOF_VOID_P
diff --git a/SOURCES/0074-Add-BLS-support-to-grub-mkconfig.patch b/SOURCES/0074-Add-BLS-support-to-grub-mkconfig.patch
new file mode 100644
index 0000000..6c65440
--- /dev/null
+++ b/SOURCES/0074-Add-BLS-support-to-grub-mkconfig.patch
@@ -0,0 +1,397 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Fri, 9 Dec 2016 15:40:29 -0500
+Subject: [PATCH] Add BLS support to grub-mkconfig
+
+GRUB now has BootLoaderSpec support, the user can choose to use this by
+setting GRUB_ENABLE_BLSCFG to true in /etc/default/grub. On this setup,
+the boot menu entries are not added to the grub.cfg, instead BLS config
+files are parsed by blscfg command and the entries created dynamically.
+
+A 10_linux_bls grub.d snippet to generate menu entries from BLS files
+is also added that can be used on platforms where the bootloader doesn't
+have BLS support and only can parse a normal grub configuration file.
+
+Portions of the 10_linux_bls were taken from the ostree-grub-generator
+script that's included in the OSTree project.
+
+Fixes to support multi-devices and generate a BLS section even if no
+kernels are found in the boot directory were proposed by Yclept Nemo
+and Tom Gundersen respectively.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ util/grub-mkconfig.8      |   4 +
+ util/grub-mkconfig.in     |   9 +-
+ util/grub-mkconfig_lib.in |  22 ++++-
+ util/grub.d/10_linux.in   | 223 +++++++++++++++++++++++++++++++++++++++++++++-
+ 4 files changed, 252 insertions(+), 6 deletions(-)
+
+diff --git a/util/grub-mkconfig.8 b/util/grub-mkconfig.8
+index a2d1f577b9b..434fa4deda4 100644
+--- a/util/grub-mkconfig.8
++++ b/util/grub-mkconfig.8
+@@ -13,5 +13,9 @@
+ \fB--output\fR=\fIFILE\fR
+ Write generated output to \fIFILE\fR.
+ 
++.TP
++\fB--no-grubenv-update\fR
++Do not update variables in the grubenv file.
++
+ .SH SEE ALSO
+ .BR "info grub"
+diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in
+index 535c0f02499..f55339a3f64 100644
+--- a/util/grub-mkconfig.in
++++ b/util/grub-mkconfig.in
+@@ -50,6 +50,8 @@ grub_get_kernel_settings="${sbindir}/@grub_get_kernel_settings@"
+ export TEXTDOMAIN=@PACKAGE@
+ export TEXTDOMAINDIR="@localedir@"
+ 
++export GRUB_GRUBENV_UPDATE="yes"
++
+ . "${pkgdatadir}/grub-mkconfig_lib"
+ 
+ # Usage: usage
+@@ -59,6 +61,7 @@ usage () {
+     gettext "Generate a grub config file"; echo
+     echo
+     print_option_help "-o, --output=$(gettext FILE)" "$(gettext "output generated config to FILE [default=stdout]")"
++    print_option_help "--no-grubenv-update" "$(gettext "do not update variables in the grubenv file")"
+     print_option_help "-h, --help" "$(gettext "print this message and exit")"
+     print_option_help "-V, --version" "$(gettext "print the version information and exit")"
+     echo
+@@ -94,6 +97,9 @@ do
+     --output=*)
+ 	grub_cfg=`echo "$option" | sed 's/--output=//'`
+ 	;;
++    --no-grubenv-update)
++	GRUB_GRUBENV_UPDATE="no"
++	;;
+     -*)
+ 	gettext_printf "Unrecognized option \`%s'\n" "$option" 1>&2
+ 	usage
+@@ -253,7 +259,8 @@ export GRUB_DEFAULT \
+   GRUB_OS_PROBER_SKIP_LIST \
+   GRUB_DISABLE_SUBMENU \
+   GRUB_DEFAULT_DTB \
+-  SUSE_BTRFS_SNAPSHOT_BOOTING
++  SUSE_BTRFS_SNAPSHOT_BOOTING \
++  GRUB_ENABLE_BLSCFG
+ 
+ if test "x${grub_cfg}" != "x"; then
+   rm -f "${grub_cfg}.new"
+diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in
+index fafeac95061..d8bb4069360 100644
+--- a/util/grub-mkconfig_lib.in
++++ b/util/grub-mkconfig_lib.in
+@@ -30,6 +30,9 @@ fi
+ if test "x$grub_file" = x; then
+   grub_file="${bindir}/@grub_file@"
+ fi
++if test "x$grub_editenv" = x; then
++  grub_editenv="${bindir}/@grub_editenv@"
++fi
+ if test "x$grub_mkrelpath" = x; then
+   grub_mkrelpath="${bindir}/@grub_mkrelpath@"
+ fi
+@@ -125,8 +128,19 @@ EOF
+   fi
+ }
+ 
++prepare_grub_to_access_device_with_variable ()
++{
++  device_variable="$1"
++  shift
++  prepare_grub_to_access_device "$@"
++  unset "device_variable"
++}
++
+ prepare_grub_to_access_device ()
+ {
++  if [ -z "$device_variable" ]; then
++    device_variable="root"
++  fi
+   old_ifs="$IFS"
+   IFS='
+ '
+@@ -161,18 +175,18 @@ prepare_grub_to_access_device ()
+   # otherwise set root as per value in device.map.
+   fs_hint="`"${grub_probe}" --device $@ --target=compatibility_hint`"
+   if [ "x$fs_hint" != x ]; then
+-    echo "set root='$fs_hint'"
++    echo "set ${device_variable}='$fs_hint'"
+   fi
+   if [ "x${GRUB_DISABLE_UUID}" != "xtrue" ] && fs_uuid="`"${grub_probe}" --device $@ --target=fs_uuid 2> /dev/null`" ; then
+     hints="`"${grub_probe}" --device $@ --target=hints_string 2> /dev/null`" || hints=
+     if [ "x$hints" != x ]; then
+       echo "if [ x\$feature_platform_search_hint = xy ]; then"
+-      echo "  search --no-floppy --fs-uuid --set=root ${hints} ${fs_uuid}"
++      echo "  search --no-floppy --fs-uuid --set=${device_variable} ${hints} ${fs_uuid}"
+       echo "else"
+-      echo "  search --no-floppy --fs-uuid --set=root ${fs_uuid}"
++      echo "  search --no-floppy --fs-uuid --set=${device_variable} ${fs_uuid}"
+       echo "fi"
+     else
+-      echo "search --no-floppy --fs-uuid --set=root ${fs_uuid}"
++      echo "search --no-floppy --fs-uuid --set=${device_variable} ${fs_uuid}"
+     fi
+   fi
+   IFS="$old_ifs"
+diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
+index cbfaca34cc7..68adb55d893 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -82,6 +82,223 @@ case x"$GRUB_FS" in
+ 	;;
+ esac
+ 
++populate_header_warn()
++{
++if [ "x${BLS_POPULATE_MENU}" = "xtrue" ]; then
++  bls_parser="10_linux script"
++else
++  bls_parser="blscfg command"
++fi
++cat <<EOF
++
++# This section was generated by a script. Do not modify the generated file - all changes
++# will be lost the next time file is regenerated. Instead edit the BootLoaderSpec files.
++#
++# The $bls_parser parses the BootLoaderSpec files stored in /boot/loader/entries and
++# populates the boot menu. Please refer to the Boot Loader Specification documentation
++# for the files format: https://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/.
++
++EOF
++}
++
++read_config()
++{
++    config_file=${1}
++    title=""
++    initrd=""
++    options=""
++    linux=""
++    grub_arg=""
++
++    while read -r line
++    do
++        record=$(echo ${line} | cut -f 1 -d ' ')
++        value=$(echo ${line} | cut -s -f2- -d ' ')
++        case "${record}" in
++            "title")
++                title=${value}
++                ;;
++            "initrd")
++                initrd=${value}
++                ;;
++            "linux")
++                linux=${value}
++                ;;
++            "options")
++                options=${value}
++                ;;
++            "grub_arg")
++                grub_arg=${value}
++                ;;
++        esac
++    done < ${config_file}
++}
++
++blsdir="/boot/loader/entries"
++
++get_sorted_bls()
++{
++    if ! [ -d "${blsdir}" ] || ! [ -e /etc/machine-id ]; then
++        return
++    fi
++
++    read machine_id < /etc/machine-id
++    if [ -z "${machine_id}" ]; then
++        return
++    fi
++
++    local IFS=$'\n'
++
++    files=($(for bls in ${blsdir}/${machine_id}-*.conf; do
++        if ! [[ -e "${bls}" ]] ; then
++            continue
++        fi
++        bls="${bls%.conf}"
++        bls="${bls##*/}"
++        echo "${bls}"
++    done | ${kernel_sort} 2>/dev/null | tac)) || :
++
++    echo "${files[@]}"
++}
++
++update_bls_cmdline()
++{
++    local cmdline="root=${LINUX_ROOT_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}"
++    local -a files=($(get_sorted_bls))
++
++    for bls in "${files[@]}"; do
++        local options="${cmdline}"
++        if [ -z "${bls##*debug*}" ]; then
++            options="${options} ${GRUB_CMDLINE_LINUX_DEBUG}"
++        fi
++        options="$(echo "${options}" | sed -e 's/\//\\\//g')"
++        sed -i -e "s/^options.*/options ${options}/" "${blsdir}/${bls}.conf"
++    done
++}
++
++populate_menu()
++{
++    local -a files=($(get_sorted_bls))
++
++    gettext_printf "Generating boot entries from BLS files...\n" >&2
++
++    for bls in "${files[@]}"; do
++        read_config "${blsdir}/${bls}.conf"
++
++        menu="${menu}menuentry '${title}' ${grub_arg} --id=${bls} {\n"
++        menu="${menu}\t linux ${linux} ${options}\n"
++        if [ -n "${initrd}" ] ; then
++            menu="${menu}\t initrd ${boot_prefix}${initrd}\n"
++        fi
++        menu="${menu}}\n\n"
++    done
++    # The printf command seems to be more reliable across shells for special character (\n, \t) evaluation
++    printf "$menu"
++}
++
++# Make BLS the default if GRUB_ENABLE_BLSCFG was not set and grubby is not installed.
++if [ -z "${GRUB_ENABLE_BLSCFG}" ] && [ -z "$(which new-kernel-pkg 2> /dev/null)" ]; then
++	  GRUB_ENABLE_BLSCFG="true"
++fi
++
++if [ "x${GRUB_ENABLE_BLSCFG}" = "xtrue" ]; then
++  if [ x$dirname = x/ ]; then
++    if [ -z "${prepare_root_cache}" ]; then
++      prepare_grub_to_access_device ${GRUB_DEVICE}
++    fi
++  else
++    if [ -z "${prepare_boot_cache}" ]; then
++      prepare_grub_to_access_device ${GRUB_DEVICE_BOOT}
++    fi
++  fi
++
++  if [ -d /sys/firmware/efi ]; then
++      bootefi_device="`${grub_probe} --target=device /boot/efi/`"
++      prepare_grub_to_access_device_with_variable boot ${bootefi_device}
++  else
++      boot_device="`${grub_probe} --target=device /boot/`"
++      prepare_grub_to_access_device_with_variable boot ${boot_device}
++  fi
++
++  arch="$(uname -m)"
++  if [ "x${arch}" = "xppc64le" ] && [ -d /sys/firmware/opal ]; then
++
++      BLS_POPULATE_MENU="true"
++      petitboot_path="/sys/firmware/devicetree/base/ibm,firmware-versions/petitboot"
++
++      if test -e ${petitboot_path}; then
++          read -r -d '' petitboot_version < ${petitboot_path}
++          petitboot_version="$(echo ${petitboot_version//v})"
++
++	  if test -n ${petitboot_version}; then
++              major_version="$(echo ${petitboot_version} | cut -d . -f1)"
++              minor_version="$(echo ${petitboot_version} | cut -d . -f2)"
++
++              re='^[0-9]+$'
++              if [[ $major_version =~ $re ]] && [[ $minor_version =~ $re ]] &&
++                 ([[ ${major_version} -gt 1 ]] ||
++                  [[ ${major_version} -eq 1 &&
++                     ${minor_version} -ge 8  ]]); then
++                  BLS_POPULATE_MENU="false"
++              fi
++          fi
++      fi
++  fi
++
++  populate_header_warn
++
++  cat << EOF
++# The kernelopts variable should be defined in the grubenv file. But to ensure that menu
++# entries populated from BootLoaderSpec files that use this variable work correctly even
++# without a grubenv file, define a fallback kernelopts variable if this has not been set.
++#
++# The kernelopts variable in the grubenv file can be modified using the grubby tool or by
++# executing the grub2-mkconfig tool. For the latter, the values of the GRUB_CMDLINE_LINUX
++# and GRUB_CMDLINE_LINUX_DEFAULT options from /etc/default/grub file are used to set both
++# the kernelopts variable in the grubenv file and the fallback kernelopts variable.
++if [ -z "\${kernelopts}" ]; then
++  set kernelopts="root=${LINUX_ROOT_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}"
++fi
++EOF
++
++  update_bls_cmdline
++
++  if [ "x${BLS_POPULATE_MENU}" = "xtrue" ]; then
++      populate_menu
++  else
++      cat << EOF
++
++insmod blscfg
++blscfg
++EOF
++  fi
++
++  if [ "x${GRUB_GRUBENV_UPDATE}" = "xyes" ]; then
++      blsdir="/boot/loader/entries"
++      [ -d "${blsdir}" ] && GRUB_BLS_FS="$(${grub_probe} --target=fs ${blsdir})"
++      if [ "x${GRUB_BLS_FS}" = "xbtrfs" ] || [ "x${GRUB_BLS_FS}" = "xzfs" ]; then
++          blsdir=$(make_system_path_relative_to_its_root "${blsdir}")
++          if [ "x${blsdir}" != "x/loader/entries" ] && [ "x${blsdir}" != "x/boot/loader/entries" ]; then
++              ${grub_editenv} - set blsdir="${blsdir}"
++          fi
++      fi
++
++      if [ -n "${GRUB_EARLY_INITRD_LINUX_CUSTOM}" ]; then
++          ${grub_editenv} - set early_initrd="${GRUB_EARLY_INITRD_LINUX_CUSTOM}"
++      fi
++
++      if [ -n "${GRUB_DEFAULT_DTB}" ]; then
++          ${grub_editenv} - set devicetree="${GRUB_DEFAULT_DTB}"
++      fi
++
++      if [ -n "${GRUB_SAVEDEFAULT}" ]; then
++           ${grub_editenv} - set save_default="${GRUB_SAVEDEFAULT}"
++      fi
++  fi
++
++  exit 0
++fi
++
+ mktitle ()
+ {
+   local title_type
+@@ -121,6 +338,7 @@ linux_entry ()
+   if [ -z "$boot_device_id" ]; then
+       boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")"
+   fi
++
+   if [ x$type != xsimple ] ; then
+       title=$(mktitle "$type" "$version")
+       if [ x"$title" = x"$GRUB_ACTUAL_DEFAULT" ] || [ x"Previous Linux versions>$title" = x"$GRUB_ACTUAL_DEFAULT" ]; then
+@@ -224,6 +442,7 @@ is_top_level=true
+ while [ "x$list" != "x" ] ; do
+   linux=`version_find_latest $list`
+   gettext_printf "Found linux image: %s\n" "$linux" >&2
++
+   basename=`basename $linux`
+   dirname=`dirname $linux`
+   rel_dirname=`make_system_path_relative_to_its_root $dirname`
+@@ -262,7 +481,9 @@ while [ "x$list" != "x" ] ; do
+     for i in ${initrd}; do
+       initrd_display="${initrd_display} ${dirname}/${i}"
+     done
+-    gettext_printf "Found initrd image: %s\n" "$(echo $initrd_display)" >&2
++    if [ "x${GRUB_ENABLE_BLSCFG}" != "xtrue" ]; then
++      gettext_printf "Found initrd image: %s\n" "$(echo $initrd_display)" >&2
++    fi
+   fi
+ 
+   fdt=
diff --git a/SOURCES/0075-Don-t-attempt-to-backtrace-on-grub_abort-for-grub-em.patch b/SOURCES/0075-Don-t-attempt-to-backtrace-on-grub_abort-for-grub-em.patch
new file mode 100644
index 0000000..9ff4c8b
--- /dev/null
+++ b/SOURCES/0075-Don-t-attempt-to-backtrace-on-grub_abort-for-grub-em.patch
@@ -0,0 +1,26 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Tue, 6 Feb 2018 11:16:28 +0100
+Subject: [PATCH] Don't attempt to backtrace on grub_abort() for grub-emu
+
+The emu platform doesn't have a grub_backtrace() implementation, so this
+causes a build error. Don't attempt to call this when building grub-emu.
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/kern/misc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
+index a3e215155bd..c60601b699d 100644
+--- a/grub-core/kern/misc.c
++++ b/grub-core/kern/misc.c
+@@ -1201,7 +1201,7 @@ static void __attribute__ ((noreturn))
+ grub_abort (void)
+ {
+ #ifndef GRUB_UTIL
+-#if defined(__i386__) || defined(__x86_64__)
++#if (defined(__i386__) || defined(__x86_64__)) && !defined(GRUB_MACHINE_EMU)
+   grub_backtrace();
+ #endif
+ #endif
diff --git a/SOURCES/0076-Add-linux-and-initrd-commands-for-grub-emu.patch b/SOURCES/0076-Add-linux-and-initrd-commands-for-grub-emu.patch
new file mode 100644
index 0000000..dd69b13
--- /dev/null
+++ b/SOURCES/0076-Add-linux-and-initrd-commands-for-grub-emu.patch
@@ -0,0 +1,347 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Chang <mchang@suse.com>
+Date: Tue, 6 Feb 2018 09:09:00 +0100
+Subject: [PATCH] Add linux and initrd commands for grub-emu
+
+When using grub-emu, the linux and initrd commands are used as arguments
+to the kexec command line tool, to allow booting the selected menu entry.
+---
+ grub-core/Makefile.core.def  |   1 -
+ grub-core/kern/emu/main.c    |   4 +
+ grub-core/kern/emu/misc.c    |  18 ++++-
+ grub-core/loader/emu/linux.c | 172 +++++++++++++++++++++++++++++++++++++++++++
+ include/grub/emu/exec.h      |   4 +-
+ include/grub/emu/hostfile.h  |   3 +-
+ include/grub/emu/misc.h      |   3 +
+ grub-core/Makefile.am        |   1 +
+ 8 files changed, 202 insertions(+), 4 deletions(-)
+ create mode 100644 grub-core/loader/emu/linux.c
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 058c88ac3af..5354f9613d3 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -1826,7 +1826,6 @@ module = {
+ 
+   common = loader/linux.c;
+   common = lib/cmdline.c;
+-  enable = noemu;
+ 
+   efi = loader/efi/linux.c;
+ };
+diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c
+index 55ea5a11ccd..846fe9715ec 100644
+--- a/grub-core/kern/emu/main.c
++++ b/grub-core/kern/emu/main.c
+@@ -107,6 +107,7 @@ static struct argp_option options[] = {
+    N_("use GRUB files in the directory DIR [default=%s]"), 0},
+   {"verbose",     'v', 0,      0, N_("print verbose messages."), 0},
+   {"hold",     'H', N_("SECS"),      OPTION_ARG_OPTIONAL, N_("wait until a debugger will attach"), 0},
++  {"kexec",       'X', 0,      0, N_("try the untryable."), 0},
+   { 0, 0, 0, 0, 0, 0 }
+ };
+ 
+@@ -164,6 +165,9 @@ argp_parser (int key, char *arg, struct argp_state *state)
+     case 'v':
+       verbosity++;
+       break;
++    case 'X':
++      grub_util_set_kexecute();
++      break;
+ 
+     case ARGP_KEY_ARG:
+       {
+diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c
+index 0ff13bcaf8c..eeea092752d 100644
+--- a/grub-core/kern/emu/misc.c
++++ b/grub-core/kern/emu/misc.c
+@@ -39,6 +39,7 @@
+ #include <grub/emu/misc.h>
+ 
+ int verbosity;
++int kexecute;
+ 
+ void
+ grub_util_warn (const char *fmt, ...)
+@@ -82,7 +83,7 @@ grub_util_error (const char *fmt, ...)
+   vfprintf (stderr, fmt, ap);
+   va_end (ap);
+   fprintf (stderr, ".\n");
+-  exit (1);
++  grub_exit (1);
+ }
+ 
+ void *
+@@ -154,6 +155,9 @@ void
+ __attribute__ ((noreturn))
+ grub_exit (int rc)
+ {
++#if defined (GRUB_KERNEL)
++  grub_reboot();
++#endif
+   exit (rc < 0 ? 1 : rc);
+ }
+ #endif
+@@ -215,3 +219,15 @@ grub_util_load_image (const char *path, char *buf)
+ 
+   fclose (fp);
+ }
++
++void
++grub_util_set_kexecute(void)
++{
++  kexecute++;
++}
++
++int
++grub_util_get_kexecute(void)
++{
++  return kexecute;
++}
+diff --git a/grub-core/loader/emu/linux.c b/grub-core/loader/emu/linux.c
+new file mode 100644
+index 00000000000..fda9e00d24c
+--- /dev/null
++++ b/grub-core/loader/emu/linux.c
+@@ -0,0 +1,172 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2006,2007,2008,2009,2010  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/loader.h>
++#include <grub/dl.h>
++#include <grub/command.h>
++#include <grub/time.h>
++
++#include <grub/emu/exec.h>
++#include <grub/emu/hostfile.h>
++#include <grub/emu/misc.h>
++
++GRUB_MOD_LICENSE ("GPLv3+");
++
++static grub_dl_t my_mod;
++
++static char *kernel_path;
++static char *initrd_path;
++static char *boot_cmdline;
++
++static grub_err_t
++grub_linux_boot (void)
++{
++  grub_err_t rc = GRUB_ERR_NONE;
++  char *initrd_param;
++  const char *kexec[] = { "kexec", "-l", kernel_path, boot_cmdline, NULL, NULL };
++  const char *systemctl[] = { "systemctl", "kexec", NULL };
++  int kexecute = grub_util_get_kexecute();
++
++  if (initrd_path) {
++    initrd_param = grub_xasprintf("--initrd=%s", initrd_path);
++    kexec[3] = initrd_param;
++    kexec[4] = boot_cmdline;
++  } else {
++    initrd_param = grub_xasprintf("%s", "");
++  }
++
++  grub_printf("%serforming 'kexec -l %s %s %s'\n",
++	(kexecute) ? "P" : "Not p",
++	kernel_path, initrd_param, boot_cmdline);
++
++  if (kexecute)
++    rc = grub_util_exec(kexec);
++
++  grub_free(initrd_param);
++
++  if (rc != GRUB_ERR_NONE) {
++    grub_error (rc, N_("Error trying to perform kexec load operation."));
++    grub_sleep (3);
++    return rc;
++  }
++  if (kexecute < 1)
++    grub_fatal (N_("Use '"PACKAGE"-emu --kexec' to force a system restart."));
++
++  grub_printf("Performing 'systemctl kexec' (%s) ",
++		(kexecute==1) ? "do-or-die" : "just-in-case");
++  rc = grub_util_exec (systemctl);
++
++  if (kexecute == 1)
++    grub_fatal (N_("Error trying to perform 'systemctl kexec'"));
++
++  /* need to check read-only root before resetting hard!? */
++  grub_printf("Performing 'kexec -e'");
++  kexec[1] = "-e";
++  kexec[2] = NULL;
++  rc = grub_util_exec(kexec);
++  if ( rc != GRUB_ERR_NONE )
++    grub_fatal (N_("Error trying to directly perform 'kexec -e'."));
++
++  return rc;
++}
++
++static grub_err_t
++grub_linux_unload (void)
++{
++  grub_dl_unref (my_mod);
++  if ( boot_cmdline != NULL )
++    grub_free (boot_cmdline);
++  boot_cmdline = NULL;
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[])
++{
++  int i;
++  char *tempstr;
++
++  grub_dl_ref (my_mod);
++
++  if (argc == 0)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
++
++  if ( !grub_util_is_regular(argv[0]) )
++    return grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("Cannot find kernel file %s"), argv[0]);
++
++  if ( kernel_path != NULL )
++    grub_free(kernel_path);
++
++  kernel_path = grub_xasprintf("%s", argv[0]);
++
++  if ( boot_cmdline != NULL ) {
++    grub_free(boot_cmdline);
++    boot_cmdline = NULL;
++  }
++
++  if ( argc > 1 )
++  {
++    boot_cmdline = grub_xasprintf("--command-line=%s", argv[1]);
++    for ( i = 2; i < argc; i++ ) {
++      tempstr = grub_xasprintf("%s %s", boot_cmdline, argv[i]);
++      grub_free(boot_cmdline);
++      boot_cmdline = tempstr;
++    }
++  }
++
++  grub_loader_set (grub_linux_boot, grub_linux_unload, 0);
++
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[])
++{
++  if (argc == 0)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
++
++  if ( !grub_util_is_regular(argv[0]) )
++    return grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("Cannot find initrd file %s"), argv[0]);
++
++  if ( initrd_path != NULL )
++    grub_free(initrd_path);
++
++  initrd_path = grub_xasprintf("%s", argv[0]);
++
++  grub_dl_unref (my_mod);
++
++  return GRUB_ERR_NONE;
++}
++
++static grub_command_t cmd_linux, cmd_initrd;
++
++GRUB_MOD_INIT(linux)
++{
++  cmd_linux = grub_register_command ("linux", grub_cmd_linux, 0, N_("Load Linux."));
++  cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, 0, N_("Load initrd."));
++  my_mod = mod;
++  kernel_path = NULL;
++  initrd_path = NULL;
++  boot_cmdline = NULL;
++}
++
++GRUB_MOD_FINI(linux)
++{
++  grub_unregister_command (cmd_linux);
++  grub_unregister_command (cmd_initrd);
++}
+diff --git a/include/grub/emu/exec.h b/include/grub/emu/exec.h
+index d1073ef86af..1b61b4a2e5d 100644
+--- a/include/grub/emu/exec.h
++++ b/include/grub/emu/exec.h
+@@ -23,6 +23,8 @@
+ #include <stdarg.h>
+ 
+ #include <sys/types.h>
++#include <grub/symbol.h>
++
+ pid_t
+ grub_util_exec_pipe (const char *const *argv, int *fd);
+ pid_t
+@@ -32,7 +34,7 @@ int
+ grub_util_exec_redirect_all (const char *const *argv, const char *stdin_file,
+ 			     const char *stdout_file, const char *stderr_file);
+ int
+-grub_util_exec (const char *const *argv);
++EXPORT_FUNC(grub_util_exec) (const char *const *argv);
+ int
+ grub_util_exec_redirect (const char *const *argv, const char *stdin_file,
+ 			 const char *stdout_file);
+diff --git a/include/grub/emu/hostfile.h b/include/grub/emu/hostfile.h
+index cfb1e2b5661..a61568e36e9 100644
+--- a/include/grub/emu/hostfile.h
++++ b/include/grub/emu/hostfile.h
+@@ -22,6 +22,7 @@
+ #include <grub/disk.h>
+ #include <grub/partition.h>
+ #include <sys/types.h>
++#include <grub/symbol.h>
+ #include <grub/osdep/hostfile.h>
+ 
+ int
+@@ -29,7 +30,7 @@ grub_util_is_directory (const char *path);
+ int
+ grub_util_is_special_file (const char *path);
+ int
+-grub_util_is_regular (const char *path);
++EXPORT_FUNC(grub_util_is_regular) (const char *path);
+ 
+ char *
+ grub_util_path_concat (size_t n, ...);
+diff --git a/include/grub/emu/misc.h b/include/grub/emu/misc.h
+index ff9c48a6490..01056954b96 100644
+--- a/include/grub/emu/misc.h
++++ b/include/grub/emu/misc.h
+@@ -57,6 +57,9 @@ void EXPORT_FUNC(grub_util_warn) (const char *fmt, ...) __attribute__ ((format (
+ void EXPORT_FUNC(grub_util_info) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2)));
+ void EXPORT_FUNC(grub_util_error) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2), noreturn));
+ 
++void EXPORT_FUNC(grub_util_set_kexecute) (void);
++int EXPORT_FUNC(grub_util_get_kexecute) (void) WARN_UNUSED_RESULT;
++
+ grub_uint64_t EXPORT_FUNC (grub_util_get_cpu_time_ms) (void);
+ 
+ #ifdef HAVE_DEVICE_MAPPER
+diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
+index ee88e44e97a..80e7a83edf9 100644
+--- a/grub-core/Makefile.am
++++ b/grub-core/Makefile.am
+@@ -307,6 +307,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/net.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/hostdisk.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/hostfile.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h
++KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/exec.h
+ if COND_GRUB_EMU_SDL
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/sdl.h
+ endif
diff --git a/SOURCES/0077-Add-grub2-switch-to-blscfg.patch b/SOURCES/0077-Add-grub2-switch-to-blscfg.patch
new file mode 100644
index 0000000..ce40e90
--- /dev/null
+++ b/SOURCES/0077-Add-grub2-switch-to-blscfg.patch
@@ -0,0 +1,410 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 15 Mar 2018 14:12:40 -0400
+Subject: [PATCH] Add grub2-switch-to-blscfg
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+[jhlavac: Use ${etcdefaultgrub} instead of /etc/default/grub]
+Signed-off-by: Jan Hlavac <jhlavac@redhat.com>
+---
+ Makefile.util.def             |   7 +
+ util/grub-set-password.in     |   2 +-
+ util/grub-switch-to-blscfg.8  |  33 +++++
+ util/grub-switch-to-blscfg.in | 317 ++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 358 insertions(+), 1 deletion(-)
+ create mode 100644 util/grub-switch-to-blscfg.8
+ create mode 100644 util/grub-switch-to-blscfg.in
+
+diff --git a/Makefile.util.def b/Makefile.util.def
+index cdd2f51fe4b..afc4d7b0c3e 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -1364,6 +1364,13 @@ program = {
+   ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
+ };
+ 
++script = {
++  name = grub-switch-to-blscfg;
++  common = util/grub-switch-to-blscfg.in;
++  mansection = 8;
++  installdir = sbin;
++};
++
+ program = {
+   name = grub-glue-efi;
+   mansection = 1;
+diff --git a/util/grub-set-password.in b/util/grub-set-password.in
+index 5ebf50576d6..c0b5ebbfdc5 100644
+--- a/util/grub-set-password.in
++++ b/util/grub-set-password.in
+@@ -1,6 +1,6 @@
+ #!/bin/sh -e
+ 
+-EFIDIR=$(grep ^ID= /etc/os-release | sed -e 's/^ID=//' -e 's/rhel/redhat/')
++EFIDIR=$(grep ^ID= /etc/os-release | sed -e 's/^ID=//' -e 's/rhel/redhat/' -e 's/\"//g')
+ if [ -d /sys/firmware/efi/efivars/ ]; then
+     grubdir=`echo "/@bootdirname@/efi/EFI/${EFIDIR}/" | sed 's,//*,/,g'`
+ else
+diff --git a/util/grub-switch-to-blscfg.8 b/util/grub-switch-to-blscfg.8
+new file mode 100644
+index 00000000000..9a886282976
+--- /dev/null
++++ b/util/grub-switch-to-blscfg.8
+@@ -0,0 +1,33 @@
++.TH GRUB-SWITCH-TO-BLSCFG 1 "Wed Feb 26 2014"
++.SH NAME
++\fBgrub-switch-to-blscfg\fR \(em Switch to using BLS config files.
++
++.SH SYNOPSIS
++\fBgrub-switch-to-blscfg\fR [--grub-directory=\fIDIR\fR] [--config-file=\fIFILE\fR] [--grub-defaults=\fIFILE\fR]
++
++.SH DESCRIPTION
++\fBgrub-switch-to-blscfg\fR reconfigures grub-mkconfig to use BLS-style config files, and then regenerates the GRUB configuration.
++
++.SH OPTIONS
++.TP
++--grub-directory=\fIDIR\fR
++Search for grub.cfg under \fIDIR\fR.  The default value is \fI/boot/efi/EFI/\fBVENDOR\fR on UEFI machines and \fI/boot/grub2\fR elsewhere.
++
++.TP
++--config-file=\fIFILE\fR
++The grub config file to use.  The default value is \fI/etc/grub2-efi.cfg\fR on UEFI machines and \fI/etc/grub2.cfg\fR elsewhere.  Symbolic links will be followed.
++
++.TP
++--grub-defaults=\fIFILE\fR
++The defaults file for grub-mkconfig.  The default value is \fI/etc/default/grub\fR.
++
++.TP
++--bls-directory=\fIDIR\fR
++Create BootLoaderSpec fragments in \fIDIR\fR.  The default value is \fI/boot/loader/entries\fR.
++
++.TP
++--backup-suffix=\fSUFFIX\fR
++The suffix to use for saved backup files.  The default value is \fI.bak\fR.
++
++.SH SEE ALSO
++.BR "info grub"
+diff --git a/util/grub-switch-to-blscfg.in b/util/grub-switch-to-blscfg.in
+new file mode 100644
+index 00000000000..a851424beb2
+--- /dev/null
++++ b/util/grub-switch-to-blscfg.in
+@@ -0,0 +1,317 @@
++#! /bin/sh
++#
++# Set a default boot entry for GRUB.
++# Copyright (C) 2004,2009  Free Software Foundation, Inc.
++#
++# GRUB is free software: you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation, either version 3 of the License, or
++# (at your option) any later version.
++#
++# GRUB is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++
++#set -eu
++
++# Initialize some variables.
++prefix=@prefix@
++exec_prefix=@exec_prefix@
++sbindir=@sbindir@
++bindir=@bindir@
++sysconfdir="@sysconfdir@"
++PACKAGE_NAME=@PACKAGE_NAME@
++PACKAGE_VERSION=@PACKAGE_VERSION@
++datarootdir="@datarootdir@"
++datadir="@datadir@"
++if [ ! -v pkgdatadir ]; then
++    pkgdatadir="${datadir}/@PACKAGE@"
++fi
++
++self=`basename $0`
++
++grub_get_kernel_settings="${sbindir}/@grub_get_kernel_settings@"
++grub_editenv=${bindir}/@grub_editenv@
++etcdefaultgrub=/etc/default/grub
++
++eval "$("${grub_get_kernel_settings}")" || true
++
++EFIDIR=$(grep ^ID= /etc/os-release | sed -e 's/^ID=//' -e 's/rhel/redhat/' -e 's/\"//g')
++if [ -d /sys/firmware/efi/efivars/ ]; then
++    startlink=/etc/grub2-efi.cfg
++    grubdir=`echo "/@bootdirname@/efi/EFI/${EFIDIR}/" | sed 's,//*,/,g'`
++else
++    startlink=/etc/grub2.cfg
++    grubdir=`echo "/@bootdirname@/@grubdirname@" | sed 's,//*,/,g'`
++fi
++
++blsdir=`echo "/@bootdirname@/loader/entries" | sed 's,//*,/,g'`
++
++backupsuffix=.bak
++
++arch="$(uname -m)"
++
++export TEXTDOMAIN=@PACKAGE@
++export TEXTDOMAINDIR="@localedir@"
++
++. "${pkgdatadir}/grub-mkconfig_lib"
++
++# Usage: usage
++# Print the usage.
++usage () {
++    gettext_printf "Usage: %s\n" "$self"
++    gettext "Switch to BLS config files.\n"; echo
++    echo
++    print_option_help "-h, --help" "$(gettext "print this message and exit")"
++    print_option_help "-V, --version" "$(gettext "print the version information and exit")"
++    echo
++    print_option_help "--backup-suffix=$(gettext "SUFFIX")" "$backupsuffix"
++    print_option_help "--bls-directory=$(gettext "DIR")" "$blsdir"
++    print_option_help "--config-file=$(gettext "FILE")" "$startlink"
++    print_option_help "--grub-defaults=$(gettext "FILE")" "$etcdefaultgrub"
++    print_option_help "--grub-directory=$(gettext "DIR")" "$grubdir"
++    # echo
++    # gettext "Report bugs to <bug-grub@gnu.org>."; echo
++}
++
++argument () {
++    opt=$1
++    shift
++
++    if test $# -eq 0; then
++        gettext_printf "%s: option requires an argument -- \`%s'\n" "$self" "$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 ;;
++    -V | --version)
++        echo "$self (${PACKAGE_NAME}) ${PACKAGE_VERSION}"
++        exit 0 ;;
++
++    --backup-suffix)
++        backupsuffix=`argument $option "$@"`
++        shift
++        ;;
++    --backup-suffix=*)
++        backupsuffix=`echo "$option" | sed 's/--backup-suffix=//'`
++        ;;
++
++    --bls-directory)
++        blsdir=`argument $option "$@"`
++        shift
++        ;;
++    --bls-directory=*)
++        blsdir=`echo "$option" | sed 's/--bls-directory=//'`
++        ;;
++
++    --config-file)
++        startlink=`argument $option "$@"`
++        shift
++        ;;
++    --config-file=*)
++        startlink=`echo "$option" | sed 's/--config-file=//'`
++        ;;
++
++    --grub-defaults)
++        etcdefaultgrub=`argument $option "$@"`
++        shift
++        ;;
++    --grub-defaults=*)
++        etcdefaultgrub=`echo "$option" | sed 's/--grub-defaults=//'`
++        ;;
++
++    --grub-directory)
++        grubdir=`argument $option "$@"`
++        shift
++        ;;
++    --grub-directory=*)
++        grubdir=`echo "$option" | sed 's/--grub-directory=//'`
++        ;;
++
++    *)
++        gettext_printf "Unrecognized option \`%s'\n" "$option" 1>&2
++        usage
++        exit 1
++        ;;
++    esac
++done
++
++find_grub_cfg() {
++    local candidate=""
++    while [ -e "${candidate}" -o $# -gt 0 ]
++    do
++        if [ ! -e "${candidate}" ] ; then
++            candidate="$1"
++            shift
++        fi
++
++        if [ -L "${candidate}" ]; then
++            candidate="$(realpath "${candidate}")"
++        fi
++
++        if [ -f "${candidate}" ]; then
++            export GRUB_CONFIG_FILE="${candidate}"
++            return 0
++        fi
++    done
++    return 1
++}
++
++if ! find_grub_cfg ${startlink} ${grubdir}/grub.cfg ; then
++  gettext_printf "Couldn't find config file\n" 1>&2
++  exit 1
++fi
++
++if [ ! -d "${blsdir}" ]; then
++    install -m 700 -d "${blsdir}"
++fi
++
++if [ -f /etc/machine-id ]; then
++    MACHINE_ID=$(cat /etc/machine-id)
++else
++    MACHINE_ID=$(dmesg | sha256sum)
++fi
++
++mkbls() {
++    local kernelver=$1 && shift
++    local datetime=$1 && shift
++    local kernelopts=$1 && shift
++
++    local debugname=""
++    local debugid=""
++    local flavor=""
++
++    if [ "$kernelver" == *\+* ] ; then
++        local flavor=-"${kernelver##*+}"
++        if [ "${flavor}" == "-debug" ]; then
++            local debugname=" with debugging"
++            local debugid="-debug"
++        fi
++    fi
++    (
++        source /etc/os-release
++
++        cat <<EOF
++title ${NAME} (${kernelver}) ${VERSION}${debugname}
++version ${kernelver}${debugid}
++linux /vmlinuz-${kernelver}
++initrd /initramfs-${kernelver}.img
++options ${kernelopts}
++grub_users \$grub_users
++grub_arg --unrestricted
++grub_class kernel${flavor}
++EOF
++    ) | cat
++}
++
++copy_bls() {
++    for kernelver in $(cd /lib/modules/ ; ls -1) "" ; do
++	bls_target="${blsdir}/${MACHINE_ID}-${kernelver}.conf"
++	linux="/vmlinuz-${kernelver}"
++	linux_path="/boot${linux}"
++	kernel_dir="/lib/modules/${kernelver}"
++
++	if [ ! -d "${kernel_dir}" ] ; then
++            continue
++	fi
++	if [ ! -f "${linux_path}" ]; then
++            continue
++	fi
++
++	linux_relpath="$("${grub_mkrelpath}" "${linux_path}")"
++	bootprefix="${linux_relpath%%"${linux}"}"
++	cmdline="root=${LINUX_ROOT_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}"
++
++	mkbls "${kernelver}" \
++	      "$(date -u +%Y%m%d%H%M%S -d "$(stat -c '%y' "${kernel_dir}")")" \
++	      "${bootprefix}" "${cmdline}" >"${bls_target}"
++
++	if [ "x$GRUB_LINUX_MAKE_DEBUG" = "xtrue" ]; then
++            bls_debug="$(echo ${bls_target} | sed -e "s/${kernelver}/${kernelver}~debug/")"
++            cp -aT  "${bls_target}" "${bls_debug}"
++            title="$(grep '^title[ \t]' "${bls_debug}" | sed -e 's/^title[ \t]*//')"
++            options="$(echo "${cmdline} ${GRUB_CMDLINE_LINUX_DEBUG}" | sed -e 's/\//\\\//g')"
++            sed -i -e "s/^title.*/title ${title}${GRUB_LINUX_DEBUG_TITLE_POSTFIX}/" "${bls_debug}"
++            sed -i -e "s/^options.*/options ${options}/" "${bls_debug}"
++	fi
++    done
++
++    if [ -f "/boot/vmlinuz-0-rescue-${MACHINE_ID}" ]; then
++	mkbls "0-rescue-${MACHINE_ID}" "0" "${bootprefix}" >"${blsdir}/${MACHINE_ID}-0-rescue.conf"
++    fi
++}
++
++# The grub2 EFI binary is not copied to the ESP as a part of an ostree
++# transaction. Make sure a grub2 version with BLS support is installed
++# but only do this if the blsdir is not set, to make sure that the BLS
++# parsing module will search for the BLS snippets in the default path.
++if test -f /run/ostree-booted && test -d /sys/firmware/efi/efivars && \
++   ! ${grub_editenv} - list | grep -q blsdir && \
++   mountpoint -q /boot; then
++    grub_binary="$(find /usr/lib/ostree-boot/efi/EFI/${EFIDIR}/ -name grub*.efi)"
++    install -m 700 ${grub_binary} ${grubdir} || exit 1
++    # Create a hidden file to indicate that grub2 now has BLS support.
++    touch /boot/grub2/.grub2-blscfg-supported
++fi
++
++GENERATE=0
++if grep '^GRUB_ENABLE_BLSCFG=.*' "${etcdefaultgrub}" \
++        | grep -vq '^GRUB_ENABLE_BLSCFG="*true"*\s*$' ; then
++    if ! sed -i"${backupsuffix}" \
++            -e 's,^GRUB_ENABLE_BLSCFG=.*,GRUB_ENABLE_BLSCFG=true,' \
++            "${etcdefaultgrub}" ; then
++        gettext_printf "Updating %s failed\n" "${etcdefaultgrub}"
++        exit 1
++    fi
++    GENERATE=1
++elif ! grep -q '^GRUB_ENABLE_BLSCFG=.*' "${etcdefaultgrub}" ; then
++    if ! echo 'GRUB_ENABLE_BLSCFG=true' >> "${etcdefaultgrub}" ; then
++        gettext_printf "Updating %s failed\n" "${etcdefaultgrub}"
++        exit 1
++    fi
++    GENERATE=1
++fi
++
++if [ "${GENERATE}" -eq 1 ] ; then
++    copy_bls
++
++    if [ $arch = "x86_64" ] && [ ! -d /sys/firmware/efi ]; then
++	mod_dir="i386-pc"
++    elif [ $arch = "ppc64" -o $arch = "ppc64le" ] && [ ! -d /sys/firmware/opal ]; then
++	mod_dir="powerpc-ieee1275"
++    fi
++
++    if [ -n "${mod_dir}" ]; then
++	for mod in blscfg increment; do
++	    install -m 700 ${prefix}/lib/grub/${mod_dir}/${mod}.mod ${grubdir}/$mod_dir/ || exit 1
++	done
++    fi
++
++    cp -af "${GRUB_CONFIG_FILE}" "${GRUB_CONFIG_FILE}${backupsuffix}"
++    if ! grub2-mkconfig -o "${GRUB_CONFIG_FILE}" ; then
++        install -m 700 "${GRUB_CONFIG_FILE}${backupsuffix}" "${GRUB_CONFIG_FILE}"
++        sed -i"${backupsuffix}" \
++            -e 's,^GRUB_ENABLE_BLSCFG=.*,GRUB_ENABLE_BLSCFG=false,' \
++            "${etcdefaultgrub}"
++        gettext_printf "Updating %s failed\n" "${GRUB_CONFIG_FILE}"
++        exit 1
++    fi
++fi
++
++# Bye.
++exit 0
diff --git a/SOURCES/0078-make-better-backtraces.patch b/SOURCES/0078-make-better-backtraces.patch
new file mode 100644
index 0000000..a65af3d
--- /dev/null
+++ b/SOURCES/0078-make-better-backtraces.patch
@@ -0,0 +1,910 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 9 Jul 2019 17:05:03 +0200
+Subject: [PATCH] make better backtraces
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ Makefile.util.def                       |   6 ++
+ grub-core/Makefile.core.def             |  16 ++--
+ grub-core/{lib => commands}/backtrace.c |   2 +-
+ grub-core/gdb/cstub.c                   |   1 -
+ grub-core/kern/arm64/backtrace.c        |  94 ++++++++++++++++++++++++
+ grub-core/kern/backtrace.c              |  97 +++++++++++++++++++++++++
+ grub-core/kern/dl.c                     |  45 ++++++++++++
+ grub-core/kern/i386/backtrace.c         | 125 ++++++++++++++++++++++++++++++++
+ grub-core/kern/i386/pc/init.c           |   4 +-
+ grub-core/kern/ieee1275/init.c          |   1 -
+ grub-core/kern/misc.c                   |  13 ++--
+ grub-core/kern/mm.c                     |   6 +-
+ grub-core/lib/arm64/backtrace.c         |  62 ----------------
+ grub-core/lib/i386/backtrace.c          |  78 --------------------
+ include/grub/backtrace.h                |  10 ++-
+ include/grub/dl.h                       |   2 +
+ include/grub/kernel.h                   |   3 +
+ grub-core/kern/arm/efi/startup.S        |   2 +
+ grub-core/kern/arm/startup.S            |   2 +
+ grub-core/kern/arm64/efi/startup.S      |   2 +
+ grub-core/kern/i386/qemu/startup.S      |   3 +-
+ grub-core/kern/ia64/efi/startup.S       |   3 +-
+ grub-core/kern/sparc64/ieee1275/crt0.S  |   3 +-
+ grub-core/Makefile.am                   |   1 +
+ 24 files changed, 414 insertions(+), 167 deletions(-)
+ rename grub-core/{lib => commands}/backtrace.c (98%)
+ create mode 100644 grub-core/kern/arm64/backtrace.c
+ create mode 100644 grub-core/kern/backtrace.c
+ create mode 100644 grub-core/kern/i386/backtrace.c
+ delete mode 100644 grub-core/lib/arm64/backtrace.c
+ delete mode 100644 grub-core/lib/i386/backtrace.c
+
+diff --git a/Makefile.util.def b/Makefile.util.def
+index afc4d7b0c3e..41906486a71 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -51,6 +51,12 @@ library = {
+   common = grub-core/partmap/msdos.c;
+   common = grub-core/fs/proc.c;
+   common = grub-core/fs/archelp.c;
++  common = grub-core/kern/backtrace.c;
++
++  x86 = grub-core/kern/i386/backtrace.c;
++  i386_xen = grub-core/kern/i386/backtrace.c;
++  x86_64_xen = grub-core/kern/i386/backtrace.c;
++  arm64 = grub-core/kern/arm64/backtrace.c;
+ };
+ 
+ library = {
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 5354f9613d3..4b7c45a7b06 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -142,6 +142,12 @@ kernel = {
+   common = kern/rescue_reader.c;
+   common = kern/term.c;
+   common = kern/verifiers.c;
++  common = kern/backtrace.c;
++
++  x86 = kern/i386/backtrace.c;
++  i386_xen = kern/i386/backtrace.c;
++  x86_64_xen = kern/i386/backtrace.c;
++  arm64 = kern/arm64/backtrace.c;
+ 
+   noemu = kern/compiler-rt.c;
+   noemu = kern/mm.c;
+@@ -188,9 +194,6 @@ kernel = {
+ 
+   softdiv = lib/division.c;
+ 
+-  x86 = lib/i386/backtrace.c;
+-  x86 = lib/backtrace.c;
+-
+   i386 = kern/i386/dl.c;
+   i386_xen = kern/i386/dl.c;
+   i386_xen_pvh = kern/i386/dl.c;
+@@ -2398,15 +2401,12 @@ module = {
+ 
+ module = {
+   name = backtrace;
+-  x86 = lib/i386/backtrace.c;
+-  i386_xen_pvh = lib/i386/backtrace.c;
+-  i386_xen = lib/i386/backtrace.c;
+-  x86_64_xen = lib/i386/backtrace.c;
+-  common = lib/backtrace.c;
++  common = commands/backtrace.c;
+   enable = x86;
+   enable = i386_xen_pvh;
+   enable = i386_xen;
+   enable = x86_64_xen;
++  enable = arm64;
+ };
+ 
+ module = {
+diff --git a/grub-core/lib/backtrace.c b/grub-core/commands/backtrace.c
+similarity index 98%
+rename from grub-core/lib/backtrace.c
+rename to grub-core/commands/backtrace.c
+index c0ad6ab8be1..8b5ec3913b5 100644
+--- a/grub-core/lib/backtrace.c
++++ b/grub-core/commands/backtrace.c
+@@ -54,7 +54,7 @@ grub_cmd_backtrace (grub_command_t cmd __attribute__ ((unused)),
+ 		    int argc __attribute__ ((unused)),
+ 		    char **args __attribute__ ((unused)))
+ {
+-  grub_backtrace ();
++  grub_backtrace (1);
+   return 0;
+ }
+ 
+diff --git a/grub-core/gdb/cstub.c b/grub-core/gdb/cstub.c
+index b64acd70fee..99281472d36 100644
+--- a/grub-core/gdb/cstub.c
++++ b/grub-core/gdb/cstub.c
+@@ -215,7 +215,6 @@ grub_gdb_trap (int trap_no)
+       grub_printf ("Unhandled exception 0x%x at ", trap_no);
+       grub_backtrace_print_address ((void *) grub_gdb_regs[PC]);
+       grub_printf ("\n");
+-      grub_backtrace_pointer ((void *) grub_gdb_regs[EBP]);
+       grub_fatal ("Unhandled exception");
+     }
+ 
+diff --git a/grub-core/kern/arm64/backtrace.c b/grub-core/kern/arm64/backtrace.c
+new file mode 100644
+index 00000000000..019c6fdfef2
+--- /dev/null
++++ b/grub-core/kern/arm64/backtrace.c
+@@ -0,0 +1,94 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2009  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/misc.h>
++#include <grub/command.h>
++#include <grub/err.h>
++#include <grub/dl.h>
++#include <grub/mm.h>
++#include <grub/term.h>
++#include <grub/backtrace.h>
++
++#define MAX_STACK_FRAME 102400
++
++struct fplr
++{
++  void *lr;
++  struct fplr *fp;
++};
++
++void
++grub_backtrace_pointer (void *frame, unsigned int skip)
++{
++  unsigned int x = 0;
++  struct fplr *fplr = (struct fplr *)frame;
++
++  while (fplr)
++    {
++      const char *name = NULL;
++      char *addr = NULL;
++
++      grub_dprintf("backtrace", "fp is %p next_fp is %p\n",
++		   fplr, fplr->fp);
++
++      if (x >= skip)
++	{
++	  name = grub_get_symbol_by_addr (fplr->lr, 1);
++	  if (name)
++	    addr = grub_resolve_symbol (name);
++	  grub_backtrace_print_address (fplr->lr);
++
++	  if (addr && addr != fplr->lr)
++	    grub_printf (" %s() %p+%p \n", name ? name : "unknown", addr,
++			 (void *)((grub_uint64_t)fplr->lr - (grub_uint64_t)addr));
++	  else
++	    grub_printf(" %s() %p \n", name ? name : "unknown", addr);
++
++	}
++
++      x += 1;
++
++      if (fplr->fp < fplr ||
++	  (grub_uint64_t)fplr->fp - (grub_uint64_t)fplr > MAX_STACK_FRAME ||
++	  fplr->fp == fplr)
++	{
++	  break;
++	}
++      fplr = fplr->fp;
++    }
++}
++
++asm ("\t.global \"_text\"\n"
++     "_text:\n"
++     "\t.quad .text\n"
++     "\t.global \"_data\"\n"
++     "_data:\n"
++     "\t.quad .data\n"
++     );
++
++extern grub_uint64_t _text;
++extern grub_uint64_t _data;
++
++void
++grub_backtrace_arch (unsigned int skip)
++{
++  grub_printf ("Backtrace (.text %p .data %p):\n",
++	       (void *)_text, (void *)_data);
++  skip += 1;
++  grub_backtrace_pointer(__builtin_frame_address(0), skip);
++}
+diff --git a/grub-core/kern/backtrace.c b/grub-core/kern/backtrace.c
+new file mode 100644
+index 00000000000..4a82e865cc6
+--- /dev/null
++++ b/grub-core/kern/backtrace.c
+@@ -0,0 +1,97 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2009  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/misc.h>
++#include <grub/command.h>
++#include <grub/err.h>
++#include <grub/dl.h>
++#include <grub/mm.h>
++#include <grub/term.h>
++#include <grub/backtrace.h>
++
++GRUB_MOD_LICENSE ("GPLv3+");
++
++static void
++grub_backtrace_print_address_default (void *addr)
++{
++#ifndef GRUB_UTIL
++  grub_dl_t mod;
++  void *start_addr;
++
++  FOR_DL_MODULES (mod)
++  {
++    grub_dl_segment_t segment;
++    for (segment = mod->segment; segment; segment = segment->next)
++      if (segment->addr <= addr && (grub_uint8_t *) segment->addr
++	  + segment->size > (grub_uint8_t *) addr)
++	{
++	  grub_printf ("%s.%x+%" PRIxGRUB_SIZE, mod->name,
++		       segment->section,
++		       (grub_size_t)
++		       ((grub_uint8_t *)addr - (grub_uint8_t *)segment->addr));
++	  return;
++	}
++  }
++
++  start_addr = grub_resolve_symbol ("_start");
++  if (start_addr && start_addr < addr)
++    grub_printf ("kernel+%" PRIxGRUB_SIZE,
++		 (grub_size_t)
++		  ((grub_uint8_t *)addr - (grub_uint8_t *)start_addr));
++  else
++#endif
++    grub_printf ("%p", addr);
++}
++
++static void
++grub_backtrace_pointer_default (void *frame __attribute__((__unused__)),
++				unsigned int skip __attribute__((__unused__)))
++{
++  return;
++}
++
++void
++grub_backtrace_pointer (void *frame, unsigned int skip)
++     __attribute__((__weak__,
++		    __alias__(("grub_backtrace_pointer_default"))));
++
++void
++grub_backtrace_print_address (void *addr)
++     __attribute__((__weak__,
++		    __alias__(("grub_backtrace_print_address_default"))));
++
++static void
++grub_backtrace_arch_default(unsigned int skip)
++{
++  grub_backtrace_pointer(__builtin_frame_address(0), skip + 1);
++}
++
++void grub_backtrace_arch (unsigned int skip)
++     __attribute__((__weak__, __alias__(("grub_backtrace_arch_default"))));
++
++void grub_backtrace (unsigned int skip)
++{
++  grub_backtrace_arch(skip + 1);
++}
++
++void grub_debug_backtrace (const char * const debug,
++			   unsigned int skip)
++{
++  if (grub_debug_enabled (debug))
++    grub_backtrace (skip + 1);
++}
+diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
+index 7afb9e6f724..88d2077709e 100644
+--- a/grub-core/kern/dl.c
++++ b/grub-core/kern/dl.c
+@@ -124,6 +124,50 @@ grub_dl_resolve_symbol (const char *name)
+   return 0;
+ }
+ 
++void *
++grub_resolve_symbol (const char *name)
++{
++	grub_symbol_t sym;
++
++	sym = grub_dl_resolve_symbol (name);
++	if (sym)
++		return sym->addr;
++	return NULL;
++}
++
++const char *
++grub_get_symbol_by_addr(const void *addr, int isfunc)
++{
++  unsigned int i;
++  grub_symbol_t before = NULL, after = NULL;
++  for (i = 0; i < GRUB_SYMTAB_SIZE; i++)
++    {
++      grub_symbol_t sym;
++      for (sym = grub_symtab[i]; sym; sym = sym->next)
++	{
++	  //grub_printf ("addr 0x%08llx symbol %s\n", (unsigned long long)sym->addr, sym->name);
++	  if (sym->addr > addr)
++	    {
++	      if (!after || sym->addr > after->addr)
++		after = sym;
++	    }
++
++	  if (isfunc != sym->isfunc)
++	    continue;
++	  if (sym->addr > addr)
++	    continue;
++
++	  if ((!before && sym->addr <= addr) || (before && before->addr <= sym->addr))
++	    before = sym;
++	}
++    }
++
++  if (before && addr < after->addr)
++    return before->name;
++
++  return NULL;
++}
++
+ /* Register a symbol with the name NAME and the address ADDR.  */
+ grub_err_t
+ grub_dl_register_symbol (const char *name, void *addr, int isfunc,
+@@ -336,6 +380,7 @@ grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e)
+   const char *str;
+   Elf_Word size, entsize;
+ 
++  grub_dprintf ("modules", "Resolving symbols for \"%s\"\n", mod->name);
+   for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
+        i < e->e_shnum;
+        i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
+diff --git a/grub-core/kern/i386/backtrace.c b/grub-core/kern/i386/backtrace.c
+new file mode 100644
+index 00000000000..2413f9a57db
+--- /dev/null
++++ b/grub-core/kern/i386/backtrace.c
+@@ -0,0 +1,125 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2009  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/misc.h>
++#include <grub/command.h>
++#include <grub/err.h>
++#include <grub/dl.h>
++#include <grub/mm.h>
++#include <grub/term.h>
++#include <grub/backtrace.h>
++
++#define MAX_STACK_FRAME 102400
++
++void
++grub_backtrace_pointer (void *frame, unsigned int skip)
++{
++  void **ebp = (void **)frame;
++  unsigned long x = 0;
++
++  while (ebp)
++    {
++      void **next_ebp = (void **)ebp[0];
++      const char *name = NULL;
++      char *addr = NULL;
++
++      grub_dprintf("backtrace", "ebp is %p next_ebp is %p\n", ebp, next_ebp);
++
++      if (x >= skip)
++	{
++	  name = grub_get_symbol_by_addr (ebp[1], 1);
++	  if (name)
++	    addr = grub_resolve_symbol (name);
++	  grub_backtrace_print_address (ebp[1]);
++
++	  if (addr && addr != ebp[1])
++	    grub_printf (" %s() %p+%p \n", name ? name : "unknown", addr,
++			 (char *)((char *)ebp[1] - addr));
++	  else
++	    grub_printf(" %s() %p \n", name ? name : "unknown", addr);
++
++#if 0
++	  grub_printf ("(");
++	  for (i = 0, arg = ebp[2]; arg != next_ebp && i < 12; arg++, i++)
++	    grub_printf ("%p,", arg);
++	  grub_printf (")\n");
++#endif
++	}
++
++      x += 1;
++
++      if (next_ebp < ebp || next_ebp - ebp > MAX_STACK_FRAME || next_ebp == ebp)
++	{
++	  //grub_printf ("Invalid stack frame at %p (%p)\n", ebp, next_ebp);
++	  break;
++	}
++      ebp = next_ebp;
++    }
++}
++
++#if defined (__x86_64__)
++asm ("\t.global \"_text\"\n"
++     "_text:\n"
++     "\t.quad .text\n"
++     "\t.global \"_data\"\n"
++     "_data:\n"
++     "\t.quad .data\n"
++     );
++#elif defined(__i386__)
++asm ("\t.global \"_text\"\n"
++     "_text:\n"
++     "\t.long .text\n"
++     "\t.global \"_data\"\n"
++     "_data:\n"
++     "\t.long .data\n"
++     );
++#else
++#warning I dunno...
++#endif
++
++extern unsigned long _text;
++extern unsigned long _data;
++
++#ifdef GRUB_UTIL
++#define EXT_C(x) x
++#endif
++
++void
++grub_backtrace_arch (unsigned int skip)
++{
++  grub_printf ("Backtrace (.text %p .data %p):\n",
++	       (void *)_text, (void *)_data);
++  skip += 1;
++#if defined (__x86_64__)
++  asm volatile ("movq %%rbp, %%rdi\n"
++		"movq 0, %%rsi\n"
++		"movl %0, %%esi\n"
++		"call " EXT_C("grub_backtrace_pointer")
++		:
++		: "r" (skip));
++#elif defined(__i386__)
++  asm volatile ("addl $8, %%esp\n"
++		"pushl %0\n"
++		"pushl %%ebp\n"
++		"call " EXT_C("grub_backtrace_pointer")
++		:
++		: "r" (skip));
++#else
++  grub_backtrace_pointer(__builtin_frame_address(0), skip);
++#endif
++}
+diff --git a/grub-core/kern/i386/pc/init.c b/grub-core/kern/i386/pc/init.c
+index 27bc68b8a53..b51d0abfa6e 100644
+--- a/grub-core/kern/i386/pc/init.c
++++ b/grub-core/kern/i386/pc/init.c
+@@ -153,7 +153,7 @@ compact_mem_regions (void)
+ }
+ 
+ grub_addr_t grub_modbase;
+-extern grub_uint8_t _start[], _edata[];
++extern grub_uint8_t _edata[];
+ 
+ /* Helper for grub_machine_init.  */
+ static int
+@@ -217,7 +217,7 @@ grub_machine_init (void)
+   /* This has to happen before any BIOS calls. */
+   grub_via_workaround_init ();
+ 
+-  grub_modbase = GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR + (_edata - _start);
++  grub_modbase = GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR + (_edata - (grub_uint8_t *)_start);
+ 
+   /* Initialize the console as early as possible.  */
+   grub_console_init ();
+diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
+index 0cd2a627231..937c1bc44cb 100644
+--- a/grub-core/kern/ieee1275/init.c
++++ b/grub-core/kern/ieee1275/init.c
+@@ -63,7 +63,6 @@
+ #define HEAP_MAX_ADDR		(unsigned long) (32 * 1024 * 1024)
+ #endif
+ 
+-extern char _start[];
+ extern char _end[];
+ 
+ #ifdef __sparc__
+diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
+index c60601b699d..a432a6be54a 100644
+--- a/grub-core/kern/misc.c
++++ b/grub-core/kern/misc.c
+@@ -1197,15 +1197,15 @@ grub_printf_fmt_check (const char *fmt, const char *fmt_expected)
+ 
+ 
+ /* Abort GRUB. This function does not return.  */
+-static void __attribute__ ((noreturn))
++static inline void __attribute__ ((noreturn))
+ grub_abort (void)
+ {
+-#ifndef GRUB_UTIL
+-#if (defined(__i386__) || defined(__x86_64__)) && !defined(GRUB_MACHINE_EMU)
+-  grub_backtrace();
++#if !defined(GRUB_MACHINE_EMU) && !defined(GRUB_UTIL)
++  grub_backtrace (1);
++#else
++  grub_printf ("\n");
+ #endif
+-#endif
+-  grub_printf ("\nAborted.");
++  grub_printf ("Aborted.");
+ 
+ #ifndef GRUB_UTIL
+   if (grub_term_inputs)
+@@ -1232,6 +1232,7 @@ grub_fatal (const char *fmt, ...)
+ {
+   va_list ap;
+ 
++  grub_printf ("\n");
+   va_start (ap, fmt);
+   grub_vprintf (_(fmt), ap);
+   va_end (ap);
+diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
+index c070afc621f..d8c8377578b 100644
+--- a/grub-core/kern/mm.c
++++ b/grub-core/kern/mm.c
+@@ -97,13 +97,13 @@ get_header_from_pointer (void *ptr, grub_mm_header_t *p, grub_mm_region_t *r)
+       break;
+ 
+   if (! *r)
+-    grub_fatal ("out of range pointer %p", ptr);
++    grub_fatal ("out of range pointer %p\n", ptr);
+ 
+   *p = (grub_mm_header_t) ptr - 1;
+   if ((*p)->magic == GRUB_MM_FREE_MAGIC)
+-    grub_fatal ("double free at %p", *p);
++    grub_fatal ("double free at %p\n", *p);
+   if ((*p)->magic != GRUB_MM_ALLOC_MAGIC)
+-    grub_fatal ("alloc magic is broken at %p: %lx", *p,
++    grub_fatal ("alloc magic is broken at %p: %lx\n", *p,
+ 		(unsigned long) (*p)->magic);
+ }
+ 
+diff --git a/grub-core/lib/arm64/backtrace.c b/grub-core/lib/arm64/backtrace.c
+deleted file mode 100644
+index 1079b5380e1..00000000000
+--- a/grub-core/lib/arm64/backtrace.c
++++ /dev/null
+@@ -1,62 +0,0 @@
+-/*
+- *  GRUB  --  GRand Unified Bootloader
+- *  Copyright (C) 2009  Free Software Foundation, Inc.
+- *
+- *  GRUB is free software: you can redistribute it and/or modify
+- *  it under the terms of the GNU General Public License as published by
+- *  the Free Software Foundation, either version 3 of the License, or
+- *  (at your option) any later version.
+- *
+- *  GRUB is distributed in the hope that it will be useful,
+- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+- *  GNU General Public License for more details.
+- *
+- *  You should have received a copy of the GNU General Public License
+- *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+- */
+-
+-#include <grub/misc.h>
+-#include <grub/command.h>
+-#include <grub/err.h>
+-#include <grub/dl.h>
+-#include <grub/mm.h>
+-#include <grub/term.h>
+-#include <grub/backtrace.h>
+-
+-#define MAX_STACK_FRAME 102400
+-
+-void
+-grub_backtrace_pointer (int frame)
+-{
+-  while (1)
+-    {
+-      void *lp = __builtin_return_address (frame);
+-      if (!lp)
+-	break;
+-
+-      lp = __builtin_extract_return_addr (lp);
+-
+-      grub_printf ("%p: ", lp);
+-      grub_backtrace_print_address (lp);
+-      grub_printf (" (");
+-      for (i = 0; i < 2; i++)
+-	grub_printf ("%p,", ((void **)ptr) [i + 2]);
+-      grub_printf ("%p)\n", ((void **)ptr) [i + 2]);
+-      nptr = *(void **)ptr;
+-      if (nptr < ptr || (void **) nptr - (void **) ptr > MAX_STACK_FRAME
+-	  || nptr == ptr)
+-	{
+-	  grub_printf ("Invalid stack frame at %p (%p)\n", ptr, nptr);
+-	  break;
+-	}
+-      ptr = nptr;
+-    }
+-}
+-
+-void
+-grub_backtrace (void)
+-{
+-  grub_backtrace_pointer (1);
+-}
+-
+diff --git a/grub-core/lib/i386/backtrace.c b/grub-core/lib/i386/backtrace.c
+deleted file mode 100644
+index c67273db3ae..00000000000
+--- a/grub-core/lib/i386/backtrace.c
++++ /dev/null
+@@ -1,78 +0,0 @@
+-/*
+- *  GRUB  --  GRand Unified Bootloader
+- *  Copyright (C) 2009  Free Software Foundation, Inc.
+- *
+- *  GRUB is free software: you can redistribute it and/or modify
+- *  it under the terms of the GNU General Public License as published by
+- *  the Free Software Foundation, either version 3 of the License, or
+- *  (at your option) any later version.
+- *
+- *  GRUB is distributed in the hope that it will be useful,
+- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+- *  GNU General Public License for more details.
+- *
+- *  You should have received a copy of the GNU General Public License
+- *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+- */
+-#include <config.h>
+-#ifdef GRUB_UTIL
+-#define REALLY_GRUB_UTIL GRUB_UTIL
+-#undef GRUB_UTIL
+-#endif
+-
+-#include <grub/symbol.h>
+-#include <grub/dl.h>
+-
+-#ifdef REALLY_GRUB_UTIL
+-#define GRUB_UTIL REALLY_GRUB_UTIL
+-#undef REALLY_GRUB_UTIL
+-#endif
+-
+-#include <grub/misc.h>
+-#include <grub/command.h>
+-#include <grub/err.h>
+-#include <grub/mm.h>
+-#include <grub/term.h>
+-#include <grub/backtrace.h>
+-
+-#define MAX_STACK_FRAME 102400
+-
+-void
+-grub_backtrace_pointer (void *ebp)
+-{
+-  void *ptr, *nptr;
+-  unsigned i;
+-
+-  ptr = ebp;
+-  while (1)
+-    {
+-      grub_printf ("%p: ", ptr);
+-      grub_backtrace_print_address (((void **) ptr)[1]);
+-      grub_printf (" (");
+-      for (i = 0; i < 2; i++)
+-	grub_printf ("%p,", ((void **)ptr) [i + 2]);
+-      grub_printf ("%p)\n", ((void **)ptr) [i + 2]);
+-      nptr = *(void **)ptr;
+-      if (nptr < ptr || (void **) nptr - (void **) ptr > MAX_STACK_FRAME
+-	  || nptr == ptr)
+-	{
+-	  grub_printf ("Invalid stack frame at %p (%p)\n", ptr, nptr);
+-	  break;
+-	}
+-      ptr = nptr;
+-    }
+-}
+-
+-void
+-grub_backtrace (void)
+-{
+-#ifdef __x86_64__
+-  asm volatile ("movq %%rbp, %%rdi\n"
+-		"callq *%%rax": :"a"(grub_backtrace_pointer));
+-#else
+-  asm volatile ("movl %%ebp, %%eax\n"
+-		"calll *%%ecx": :"c"(grub_backtrace_pointer));
+-#endif
+-}
+-
+diff --git a/include/grub/backtrace.h b/include/grub/backtrace.h
+index 395519762f0..275cf85e2d3 100644
+--- a/include/grub/backtrace.h
++++ b/include/grub/backtrace.h
+@@ -19,8 +19,14 @@
+ #ifndef GRUB_BACKTRACE_HEADER
+ #define GRUB_BACKTRACE_HEADER	1
+ 
+-void grub_backtrace (void);
+-void grub_backtrace_pointer (void *ptr);
++#include <grub/symbol.h>
++#include <grub/types.h>
++
++void EXPORT_FUNC(grub_debug_backtrace) (const char * const debug,
++					unsigned int skip);
++void EXPORT_FUNC(grub_backtrace) (unsigned int skip);
++void grub_backtrace_arch (unsigned int skip);
++void grub_backtrace_pointer (void *ptr, unsigned int skip);
+ void grub_backtrace_print_address (void *addr);
+ 
+ #endif
+diff --git a/include/grub/dl.h b/include/grub/dl.h
+index 91933b85f2c..2f76e6b0437 100644
+--- a/include/grub/dl.h
++++ b/include/grub/dl.h
+@@ -259,6 +259,8 @@ grub_dl_is_persistent (grub_dl_t mod)
+ 
+ #endif
+ 
++void * EXPORT_FUNC(grub_resolve_symbol) (const char *name);
++const char * EXPORT_FUNC(grub_get_symbol_by_addr) (const void *addr, int isfunc);
+ grub_err_t grub_dl_register_symbol (const char *name, void *addr,
+ 				    int isfunc, grub_dl_t mod);
+ 
+diff --git a/include/grub/kernel.h b/include/grub/kernel.h
+index abbca5ea335..300a9766cda 100644
+--- a/include/grub/kernel.h
++++ b/include/grub/kernel.h
+@@ -111,6 +111,9 @@ grub_addr_t grub_modules_get_end (void);
+ 
+ #endif
+ 
++void EXPORT_FUNC(start) (void);
++void EXPORT_FUNC(_start) (void);
++
+ /* The start point of the C code.  */
+ void grub_main (void) __attribute__ ((noreturn));
+ 
+diff --git a/grub-core/kern/arm/efi/startup.S b/grub-core/kern/arm/efi/startup.S
+index 9f8265315a9..f3bc41f9d0f 100644
+--- a/grub-core/kern/arm/efi/startup.S
++++ b/grub-core/kern/arm/efi/startup.S
+@@ -23,6 +23,8 @@
+ 	.file 	"startup.S"
+ 	.text
+ 	.arm
++	.globl	start, _start
++FUNCTION(start)
+ FUNCTION(_start)
+ 	/*
+ 	 *  EFI_SYSTEM_TABLE and EFI_HANDLE are passed in r1/r0.
+diff --git a/grub-core/kern/arm/startup.S b/grub-core/kern/arm/startup.S
+index 3946fe8e183..5679a1d00ad 100644
+--- a/grub-core/kern/arm/startup.S
++++ b/grub-core/kern/arm/startup.S
+@@ -48,6 +48,8 @@
+ 	
+ 	.text
+ 	.arm
++	.globl	start, _start
++FUNCTION(start)
+ FUNCTION(_start)
+ 	b	codestart
+ 	
+diff --git a/grub-core/kern/arm64/efi/startup.S b/grub-core/kern/arm64/efi/startup.S
+index 666a7ee3c92..41676bdb2b8 100644
+--- a/grub-core/kern/arm64/efi/startup.S
++++ b/grub-core/kern/arm64/efi/startup.S
+@@ -19,7 +19,9 @@
+ #include <grub/symbol.h>
+ 
+ 	.file 	"startup.S"
++	.globl start, _start
+ 	.text
++FUNCTION(start)
+ FUNCTION(_start)
+ 	/*
+ 	 *  EFI_SYSTEM_TABLE and EFI_HANDLE are passed in x1/x0.
+diff --git a/grub-core/kern/i386/qemu/startup.S b/grub-core/kern/i386/qemu/startup.S
+index 0d89858d9b3..939f182fc74 100644
+--- a/grub-core/kern/i386/qemu/startup.S
++++ b/grub-core/kern/i386/qemu/startup.S
+@@ -24,7 +24,8 @@
+ 
+ 	.text
+ 	.code32
+-	.globl _start
++	.globl start, _start
++start:
+ _start:
+ 	jmp	codestart
+ 
+diff --git a/grub-core/kern/ia64/efi/startup.S b/grub-core/kern/ia64/efi/startup.S
+index d75c6d7cc74..8f2a593e529 100644
+--- a/grub-core/kern/ia64/efi/startup.S
++++ b/grub-core/kern/ia64/efi/startup.S
+@@ -24,8 +24,9 @@
+ 	.psr lsb
+ 	.lsb
+ 
+-	.global _start
++	.global start, _start
+ 	.proc _start
++start:
+ _start:
+ 	alloc loc0=ar.pfs,2,4,0,0
+ 	mov loc1=rp
+diff --git a/grub-core/kern/sparc64/ieee1275/crt0.S b/grub-core/kern/sparc64/ieee1275/crt0.S
+index 03b916f0534..701bf63abcf 100644
+--- a/grub-core/kern/sparc64/ieee1275/crt0.S
++++ b/grub-core/kern/sparc64/ieee1275/crt0.S
+@@ -22,7 +22,8 @@
+ 
+ 	.text
+ 	.align	4
+-	.globl	_start
++	.globl	start, _start
++start:
+ _start:
+ 	ba	codestart
+ 	 mov  %o4, %o0
+diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
+index 80e7a83edf9..f512573c0da 100644
+--- a/grub-core/Makefile.am
++++ b/grub-core/Makefile.am
+@@ -66,6 +66,7 @@ CLEANFILES += grub_script.yy.c grub_script.yy.h
+ 
+ include $(srcdir)/Makefile.core.am
+ 
++KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/backtrace.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/cache.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/command.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/device.h
diff --git a/SOURCES/0079-normal-don-t-draw-our-startup-message-if-debug-is-se.patch b/SOURCES/0079-normal-don-t-draw-our-startup-message-if-debug-is-se.patch
new file mode 100644
index 0000000..9922f2b
--- /dev/null
+++ b/SOURCES/0079-normal-don-t-draw-our-startup-message-if-debug-is-se.patch
@@ -0,0 +1,23 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 9 Nov 2017 15:58:52 -0500
+Subject: [PATCH] normal: don't draw our startup message if debug is set
+
+---
+ grub-core/normal/main.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
+index d5968797f4f..e349303c29b 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -432,6 +432,9 @@ grub_normal_reader_init (int nested)
+   const char *msg_esc = _("ESC at any time exits.");
+   char *msg_formatted;
+ 
++  if (grub_env_get ("debug") != NULL)
++    return 0;
++
+   msg_formatted = grub_xasprintf (_("Minimal BASH-like line editing is supported. For "
+ 				    "the first word, TAB lists possible command completions. Anywhere "
+ 				    "else TAB lists possible device or file completions. %s"),
diff --git a/SOURCES/0080-Work-around-some-minor-include-path-weirdnesses.patch b/SOURCES/0080-Work-around-some-minor-include-path-weirdnesses.patch
new file mode 100644
index 0000000..460d792
--- /dev/null
+++ b/SOURCES/0080-Work-around-some-minor-include-path-weirdnesses.patch
@@ -0,0 +1,137 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Fri, 16 Mar 2018 13:28:57 -0400
+Subject: [PATCH] Work around some minor include path weirdnesses
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ include/grub/arm/efi/console.h    | 24 ++++++++++++++++++++++++
+ include/grub/arm64/efi/console.h  | 24 ++++++++++++++++++++++++
+ include/grub/i386/efi/console.h   | 24 ++++++++++++++++++++++++
+ include/grub/x86_64/efi/console.h | 24 ++++++++++++++++++++++++
+ 4 files changed, 96 insertions(+)
+ create mode 100644 include/grub/arm/efi/console.h
+ create mode 100644 include/grub/arm64/efi/console.h
+ create mode 100644 include/grub/i386/efi/console.h
+ create mode 100644 include/grub/x86_64/efi/console.h
+
+diff --git a/include/grub/arm/efi/console.h b/include/grub/arm/efi/console.h
+new file mode 100644
+index 00000000000..1592f6f76b5
+--- /dev/null
++++ b/include/grub/arm/efi/console.h
+@@ -0,0 +1,24 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2002,2005,2006,2007  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef GRUB_ARM_EFI_CONSOLE_H
++#define GRUB_ARM_EFI_CONSOLE_H
++
++#include <efi/console.h>
++
++#endif /* ! GRUB_ARM_EFI_CONSOLE_H */
+diff --git a/include/grub/arm64/efi/console.h b/include/grub/arm64/efi/console.h
+new file mode 100644
+index 00000000000..95689339384
+--- /dev/null
++++ b/include/grub/arm64/efi/console.h
+@@ -0,0 +1,24 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2002,2005,2006,2007  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef GRUB_ARM64_EFI_CONSOLE_H
++#define GRUB_ARM64_EFI_CONSOLE_H
++
++#include <efi/console.h>
++
++#endif /* ! GRUB_ARM64_EFI_CONSOLE_H */
+diff --git a/include/grub/i386/efi/console.h b/include/grub/i386/efi/console.h
+new file mode 100644
+index 00000000000..9231375cb07
+--- /dev/null
++++ b/include/grub/i386/efi/console.h
+@@ -0,0 +1,24 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2002,2005,2006,2007  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef GRUB_I386_EFI_CONSOLE_H
++#define GRUB_I386_EFI_CONSOLE_H
++
++#include <efi/console.h>
++
++#endif /* ! GRUB_I386_EFI_CONSOLE_H */
+diff --git a/include/grub/x86_64/efi/console.h b/include/grub/x86_64/efi/console.h
+new file mode 100644
+index 00000000000..dba9d8678d0
+--- /dev/null
++++ b/include/grub/x86_64/efi/console.h
+@@ -0,0 +1,24 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2002,2005,2006,2007  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef GRUB_X86_64_EFI_CONSOLE_H
++#define GRUB_X86_64_EFI_CONSOLE_H
++
++#include <efi/console.h>
++
++#endif /* ! GRUB_X86_64_EFI_CONSOLE_H */
diff --git a/SOURCES/0081-Make-it-possible-to-enabled-build-id-sha1.patch b/SOURCES/0081-Make-it-possible-to-enabled-build-id-sha1.patch
new file mode 100644
index 0000000..356a4d8
--- /dev/null
+++ b/SOURCES/0081-Make-it-possible-to-enabled-build-id-sha1.patch
@@ -0,0 +1,61 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 25 Jun 2015 15:41:06 -0400
+Subject: [PATCH] Make it possible to enabled --build-id=sha1
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ configure.ac |  8 ++++++++
+ acinclude.m4 | 19 +++++++++++++++++++
+ 2 files changed, 27 insertions(+)
+
+diff --git a/configure.ac b/configure.ac
+index 537ed411469..b4455e4732d 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1470,7 +1470,15 @@ grub_PROG_TARGET_CC
+ if test "x$TARGET_APPLE_LINKER" != x1 ; then
+ grub_PROG_OBJCOPY_ABSOLUTE
+ fi
++
++AC_ARG_ENABLE([build-id],
++	      [AS_HELP_STRING([--enable-build-id],
++                             [ask the linker to supply build-id notes (default=no)])])
++if test x$enable_build_id = xyes; then
++grub_PROG_LD_BUILD_ID_SHA1
++else
+ grub_PROG_LD_BUILD_ID_NONE
++fi
+ if test "x$target_cpu" = xi386; then
+   if test "$platform" != emu && test "x$TARGET_APPLE_LINKER" != x1 ; then
+     if test ! -z "$TARGET_IMG_LDSCRIPT"; then
+diff --git a/acinclude.m4 b/acinclude.m4
+index 6e14bb553c6..21238fcfd03 100644
+--- a/acinclude.m4
++++ b/acinclude.m4
+@@ -136,6 +136,25 @@ if test "x$grub_cv_prog_ld_build_id_none" = xyes; then
+ fi
+ ])
+ 
++dnl Supply --build-id=sha1 to ld if building modules.
++dnl This suppresses warnings from ld on some systems
++AC_DEFUN([grub_PROG_LD_BUILD_ID_SHA1],
++[AC_MSG_CHECKING([whether linker accepts --build-id=sha1])
++AC_CACHE_VAL(grub_cv_prog_ld_build_id_sha1,
++[save_LDFLAGS="$LDFLAGS"
++LDFLAGS="$LDFLAGS -Wl,--build-id=sha1"
++AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
++	       [grub_cv_prog_ld_build_id_sha1=yes],
++	       [grub_cv_prog_ld_build_id_sha1=no])
++LDFLAGS="$save_LDFLAGS"
++])
++AC_MSG_RESULT([$grub_cv_prog_ld_build_id_sha1])
++
++if test "x$grub_cv_prog_ld_build_id_sha1" = xyes; then
++  TARGET_LDFLAGS="$TARGET_LDFLAGS -Wl,--build-id=sha1"
++fi
++])
++
+ dnl Check nm
+ AC_DEFUN([grub_PROG_NM_WORKS],
+ [AC_MSG_CHECKING([whether nm works])
diff --git a/SOURCES/0082-Add-grub_qdprintf-grub_dprintf-without-the-file-line.patch b/SOURCES/0082-Add-grub_qdprintf-grub_dprintf-without-the-file-line.patch
new file mode 100644
index 0000000..a8a757e
--- /dev/null
+++ b/SOURCES/0082-Add-grub_qdprintf-grub_dprintf-without-the-file-line.patch
@@ -0,0 +1,56 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Sun, 28 Jun 2015 13:09:58 -0400
+Subject: [PATCH] Add grub_qdprintf() - grub_dprintf() without the file+line
+ number.
+
+This just makes copy+paste of our debug loading info easier.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/kern/misc.c | 18 ++++++++++++++++++
+ include/grub/misc.h   |  2 ++
+ 2 files changed, 20 insertions(+)
+
+diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
+index a432a6be54a..9a2fae6398e 100644
+--- a/grub-core/kern/misc.c
++++ b/grub-core/kern/misc.c
+@@ -191,6 +191,24 @@ grub_real_dprintf (const char *file, const int line, const char *condition,
+     }
+ }
+ 
++void
++grub_qdprintf (const char *condition, const char *fmt, ...)
++{
++  va_list args;
++  const char *debug = grub_env_get ("debug");
++
++  if (! debug)
++    return;
++
++  if (grub_strword (debug, "all") || grub_strword (debug, condition))
++    {
++      va_start (args, fmt);
++      grub_vprintf (fmt, args);
++      va_end (args);
++      grub_refresh ();
++    }
++}
++
+ #define PREALLOC_SIZE 255
+ 
+ int
+diff --git a/include/grub/misc.h b/include/grub/misc.h
+index fd18e6320b8..3adc4036e3b 100644
+--- a/include/grub/misc.h
++++ b/include/grub/misc.h
+@@ -345,6 +345,8 @@ void EXPORT_FUNC(grub_real_dprintf) (const char *file,
+                                      const int line,
+                                      const char *condition,
+                                      const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 4, 5)));
++void EXPORT_FUNC(grub_qdprintf) (const char *condition,
++				 const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 2, 3)));
+ int EXPORT_FUNC(grub_vprintf) (const char *fmt, va_list args);
+ int EXPORT_FUNC(grub_snprintf) (char *str, grub_size_t n, const char *fmt, ...)
+      __attribute__ ((format (GNU_PRINTF, 3, 4)));
diff --git a/SOURCES/0083-Make-a-gdb-dprintf-that-tells-us-load-addresses.patch b/SOURCES/0083-Make-a-gdb-dprintf-that-tells-us-load-addresses.patch
new file mode 100644
index 0000000..75de764
--- /dev/null
+++ b/SOURCES/0083-Make-a-gdb-dprintf-that-tells-us-load-addresses.patch
@@ -0,0 +1,178 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 25 Jun 2015 15:11:36 -0400
+Subject: [PATCH] Make a "gdb" dprintf that tells us load addresses.
+
+This makes a grub_dprintf() call during platform init and during module
+loading that tells us the virtual addresses of the .text and .data
+sections of grub-core/kernel.exec and any modules it loads.
+
+Specifically, it displays them in the gdb "add-symbol-file" syntax, with
+the presumption that there's a variable $grubdir that reflects the path
+to any such binaries.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/kern/dl.c       | 50 +++++++++++++++++++++++++++++++++++++++++++++++
+ grub-core/kern/efi/efi.c  |  4 ++--
+ grub-core/kern/efi/init.c | 26 +++++++++++++++++++++++-
+ include/grub/efi/efi.h    |  2 +-
+ 4 files changed, 78 insertions(+), 4 deletions(-)
+
+diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
+index 88d2077709e..9557254035e 100644
+--- a/grub-core/kern/dl.c
++++ b/grub-core/kern/dl.c
+@@ -501,6 +501,23 @@ grub_dl_find_section (Elf_Ehdr *e, const char *name)
+       return s;
+   return NULL;
+ }
++static long
++grub_dl_find_section_index (Elf_Ehdr *e, const char *name)
++{
++  Elf_Shdr *s;
++  const char *str;
++  unsigned i;
++
++  s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
++  str = (char *) e + s->sh_offset;
++
++  for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
++       i < e->e_shnum;
++       i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
++    if (grub_strcmp (str + s->sh_name, name) == 0)
++      return (long)i;
++  return -1;
++}
+ 
+ /* Me, Vladimir Serbinenko, hereby I add this module check as per new
+    GNU module policy. Note that this license check is informative only.
+@@ -653,6 +670,37 @@ grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
+ 
+   return GRUB_ERR_NONE;
+ }
++static void
++grub_dl_print_gdb_info (grub_dl_t mod, Elf_Ehdr *e)
++{
++  void *text, *data = NULL;
++  long idx;
++
++  idx = grub_dl_find_section_index (e, ".text");
++  if (idx < 0)
++    return;
++
++  text = grub_dl_get_section_addr (mod, idx);
++  if (!text)
++    return;
++
++  idx = grub_dl_find_section_index (e, ".data");
++  if (idx >= 0)
++    data = grub_dl_get_section_addr (mod, idx);
++
++  if (data)
++    grub_qdprintf ("gdb", "add-symbol-file \\\n"
++		          "/usr/lib/debug/usr/lib/grub/%s-%s/%s.debug "
++			  "\\\n %p -s .data %p\n",
++		  GRUB_TARGET_CPU, GRUB_PLATFORM,
++		  mod->name, text, data);
++  else
++    grub_qdprintf ("gdb", "add-symbol-file \\\n"
++			   "/usr/lib/debug/usr/lib/grub/%s-%s/%s.debug "
++			   "\\\n%p\n",
++		  GRUB_TARGET_CPU, GRUB_PLATFORM,
++		  mod->name, text);
++}
+ 
+ /* Load a module from core memory.  */
+ grub_dl_t
+@@ -712,6 +760,8 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size)
+   grub_dprintf ("modules", "module name: %s\n", mod->name);
+   grub_dprintf ("modules", "init function: %p\n", mod->init);
+ 
++  grub_dl_print_gdb_info (mod, e);
++
+   if (grub_dl_add (mod))
+     {
+       grub_dl_unload (mod);
+diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
+index ae9885edb84..d6a2fb57789 100644
+--- a/grub-core/kern/efi/efi.c
++++ b/grub-core/kern/efi/efi.c
+@@ -296,7 +296,7 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid,
+ /* Search the mods section from the PE32/PE32+ image. This code uses
+    a PE32 header, but should work with PE32+ as well.  */
+ grub_addr_t
+-grub_efi_modules_addr (void)
++grub_efi_section_addr (const char *section_name)
+ {
+   grub_efi_loaded_image_t *image;
+   struct grub_pe32_header *header;
+@@ -321,7 +321,7 @@ grub_efi_modules_addr (void)
+        i < coff_header->num_sections;
+        i++, section++)
+     {
+-      if (grub_strcmp (section->name, "mods") == 0)
++      if (grub_strcmp (section->name, section_name) == 0)
+ 	break;
+     }
+ 
+diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c
+index 6d39bd3ad29..2d12e6188fd 100644
+--- a/grub-core/kern/efi/init.c
++++ b/grub-core/kern/efi/init.c
+@@ -115,10 +115,33 @@ grub_efi_env_init (void)
+   grub_free (envblk_s.buf);
+ }
+ 
++static void
++grub_efi_print_gdb_info (void)
++{
++  grub_addr_t text;
++  grub_addr_t data;
++
++  text = grub_efi_section_addr (".text");
++  if (!text)
++    return;
++
++  data = grub_efi_section_addr (".data");
++  if (data)
++    grub_qdprintf ("gdb",
++		  "add-symbol-file /usr/lib/debug/usr/lib/grub/%s-%s/"
++		  "kernel.exec %p -s .data %p\n",
++		  GRUB_TARGET_CPU, GRUB_PLATFORM, (void *)text, (void *)data);
++  else
++    grub_qdprintf ("gdb",
++		  "add-symbol-file /usr/lib/debug/usr/lib/grub/%s-%s/"
++		  "kernel.exec %p\n",
++		  GRUB_TARGET_CPU, GRUB_PLATFORM, (void *)text);
++}
++
+ void
+ grub_efi_init (void)
+ {
+-  grub_modbase = grub_efi_modules_addr ();
++  grub_modbase = grub_efi_section_addr ("mods");
+   /* First of all, initialize the console so that GRUB can display
+      messages.  */
+   grub_console_init ();
+@@ -142,6 +165,7 @@ grub_efi_init (void)
+ 	      0, 0, 0, NULL);
+ 
+   grub_efi_env_init ();
++  grub_efi_print_gdb_info ();
+   grub_efidisk_init ();
+ }
+ 
+diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
+index 03f9a9d0118..2e0691454b1 100644
+--- a/include/grub/efi/efi.h
++++ b/include/grub/efi/efi.h
+@@ -138,7 +138,7 @@ grub_err_t grub_arch_efi_linux_check_image(struct linux_arch_kernel_header *lh);
+ grub_err_t grub_arch_efi_linux_boot_image(grub_addr_t addr, char *args);
+ #endif
+ 
+-grub_addr_t grub_efi_modules_addr (void);
++grub_addr_t grub_efi_section_addr (const char *section);
+ 
+ void grub_efi_mm_init (void);
+ void grub_efi_mm_fini (void);
diff --git a/SOURCES/0084-Fixup-for-newer-compiler.patch b/SOURCES/0084-Fixup-for-newer-compiler.patch
new file mode 100644
index 0000000..12dd193
--- /dev/null
+++ b/SOURCES/0084-Fixup-for-newer-compiler.patch
@@ -0,0 +1,36 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 10 May 2018 13:40:19 -0400
+Subject: [PATCH] Fixup for newer compiler
+
+---
+ grub-core/fs/btrfs.c         | 2 +-
+ include/grub/gpt_partition.h | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
+index 2b21cbaa67e..4cc86e9b79e 100644
+--- a/grub-core/fs/btrfs.c
++++ b/grub-core/fs/btrfs.c
+@@ -218,7 +218,7 @@ struct grub_btrfs_inode
+   grub_uint64_t size;
+   grub_uint8_t dummy2[0x70];
+   struct grub_btrfs_time mtime;
+-} GRUB_PACKED;
++} GRUB_PACKED  __attribute__ ((aligned(8)));
+ 
+ struct grub_btrfs_extent_data
+ {
+diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h
+index 7a93f43291c..8212697bf6b 100644
+--- a/include/grub/gpt_partition.h
++++ b/include/grub/gpt_partition.h
+@@ -76,7 +76,7 @@ struct grub_gpt_partentry
+   grub_uint64_t end;
+   grub_uint64_t attrib;
+   char name[72];
+-} GRUB_PACKED;
++} GRUB_PACKED  __attribute__ ((aligned(8)));
+ 
+ grub_err_t
+ grub_gpt_partition_map_iterate (grub_disk_t disk,
diff --git a/SOURCES/0085-Don-t-attempt-to-export-the-start-and-_start-symbols.patch b/SOURCES/0085-Don-t-attempt-to-export-the-start-and-_start-symbols.patch
new file mode 100644
index 0000000..0e925ee
--- /dev/null
+++ b/SOURCES/0085-Don-t-attempt-to-export-the-start-and-_start-symbols.patch
@@ -0,0 +1,42 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Sat, 12 May 2018 11:29:07 +0200
+Subject: [PATCH] Don't attempt to export the start and _start symbols for
+ grub-emu
+
+Commit 318ee04aadc ("make better backtraces") reworked the backtrace logic
+but the changes lead to the following build error on the grub-emu platform:
+
+grub_emu_lite-symlist.o:(.data+0xf08): undefined reference to `start'
+collect2: error: ld returned 1 exit status
+make[3]: *** [Makefile:25959: grub-emu-lite] Error 1
+make[3]: *** Waiting for unfinished jobs....
+cat kernel_syms.input | grep -v '^#' | sed -n \
+  -e '/EXPORT_FUNC *([a-zA-Z0-9_]*)/{s/.*EXPORT_FUNC *(\([a-zA-Z0-9_]*\)).*/defined kernel '""'\1/;p;}' \
+  -e '/EXPORT_VAR *([a-zA-Z0-9_]*)/{s/.*EXPORT_VAR *(\([a-zA-Z0-9_]*\)).*/defined kernel '""'\1/;p;}' \
+  | sort -u >kernel_syms.lst
+
+The problem is that start and _start symbols are exported unconditionally,
+but these aren't defined for grub-emu since is an emultaed platform so it
+doesn't have a startup logic. Don't attempt to export those for grub-emu.
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ include/grub/kernel.h | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/include/grub/kernel.h b/include/grub/kernel.h
+index 300a9766cda..55849777eaa 100644
+--- a/include/grub/kernel.h
++++ b/include/grub/kernel.h
+@@ -111,8 +111,10 @@ grub_addr_t grub_modules_get_end (void);
+ 
+ #endif
+ 
++#if !defined(GRUB_MACHINE_EMU)
+ void EXPORT_FUNC(start) (void);
+ void EXPORT_FUNC(_start) (void);
++#endif
+ 
+ /* The start point of the C code.  */
+ void grub_main (void) __attribute__ ((noreturn));
diff --git a/SOURCES/0086-Fixup-for-newer-compiler.patch b/SOURCES/0086-Fixup-for-newer-compiler.patch
new file mode 100644
index 0000000..11ed6e5
--- /dev/null
+++ b/SOURCES/0086-Fixup-for-newer-compiler.patch
@@ -0,0 +1,22 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 10 May 2018 13:40:19 -0400
+Subject: [PATCH] Fixup for newer compiler
+
+---
+ conf/Makefile.common | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/conf/Makefile.common b/conf/Makefile.common
+index 191b1a70c6b..5f0ef969857 100644
+--- a/conf/Makefile.common
++++ b/conf/Makefile.common
+@@ -38,7 +38,7 @@ CFLAGS_KERNEL = $(CFLAGS_PLATFORM) -ffreestanding
+ LDFLAGS_KERNEL = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC)
+ CPPFLAGS_KERNEL = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) -DGRUB_KERNEL=1
+ CCASFLAGS_KERNEL = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM)
+-STRIPFLAGS_KERNEL = -R .eh_frame -R .rel.dyn -R .reginfo -R .note -R .comment -R .drectve -R .note.gnu.gold-version -R .MIPS.abiflags -R .ARM.exidx
++STRIPFLAGS_KERNEL = -R .eh_frame -R .rel.dyn -R .reginfo -R .note -R .comment -R .drectve -R .note.gnu.gold-version -R .MIPS.abiflags -R .ARM.exidx -R .note.gnu.property -R .gnu.build.attributes
+ 
+ CFLAGS_MODULE = $(CFLAGS_PLATFORM) -ffreestanding
+ LDFLAGS_MODULE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-r,-d
diff --git a/SOURCES/0087-Add-support-for-non-Ethernet-network-cards.patch b/SOURCES/0087-Add-support-for-non-Ethernet-network-cards.patch
new file mode 100644
index 0000000..02fb951
--- /dev/null
+++ b/SOURCES/0087-Add-support-for-non-Ethernet-network-cards.patch
@@ -0,0 +1,766 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Andrzej Kacprowski <andrzej.kacprowski@intel.com>
+Date: Wed, 10 Jul 2019 15:22:29 +0200
+Subject: [PATCH] Add support for non-Ethernet network cards
+
+This patch replaces fixed 6-byte link layer address with
+up to 32-byte variable sized address.
+This allows supporting Infiniband and Omni-Path fabric
+which use 20-byte address, but other network card types
+can also take advantage of this change.
+The network card driver is responsible for replacing L2
+header provided by grub2 if needed.
+This approach is compatible with UEFI network stack which
+also allows up to 32-byte variable size link address.
+
+The BOOTP/DHCP packet format is limited to 16 byte client
+hardware address, if link address is more that 16-bytes
+then chaddr field in BOOTP it will be set to 0 as per rfc4390.
+
+Resolves: rhbz#1370642
+
+Signed-off-by: Andrzej Kacprowski <andrzej.kacprowski@intel.com>
+[msalter: Fix max string calculation in grub_net_hwaddr_to_str]
+Signed-off-by: Mark Salter <msalter@redhat.com>
+---
+ grub-core/net/arp.c                    | 155 ++++++++++++++++++++++-----------
+ grub-core/net/bootp.c                  |  15 ++--
+ grub-core/net/drivers/efi/efinet.c     |   8 +-
+ grub-core/net/drivers/emu/emunet.c     |   1 +
+ grub-core/net/drivers/i386/pc/pxe.c    |  13 +--
+ grub-core/net/drivers/ieee1275/ofnet.c |   2 +
+ grub-core/net/drivers/uboot/ubootnet.c |   1 +
+ grub-core/net/ethernet.c               |  88 +++++++++----------
+ grub-core/net/icmp6.c                  |  15 ++--
+ grub-core/net/ip.c                     |   4 +-
+ grub-core/net/net.c                    |  50 ++++++-----
+ include/grub/net.h                     |  19 ++--
+ 12 files changed, 219 insertions(+), 152 deletions(-)
+
+diff --git a/grub-core/net/arp.c b/grub-core/net/arp.c
+index 54306e3b16d..67b409a8acc 100644
+--- a/grub-core/net/arp.c
++++ b/grub-core/net/arp.c
+@@ -31,22 +31,12 @@ enum
+     ARP_REPLY = 2
+   };
+ 
+-enum
+-  {
+-    /* IANA ARP constant to define hardware type as ethernet. */
+-    GRUB_NET_ARPHRD_ETHERNET = 1
+-  };
+-
+-struct arppkt {
++struct arphdr {
+   grub_uint16_t hrd;
+   grub_uint16_t pro;
+   grub_uint8_t hln;
+   grub_uint8_t pln;
+   grub_uint16_t op;
+-  grub_uint8_t sender_mac[6];
+-  grub_uint32_t sender_ip;
+-  grub_uint8_t recv_mac[6];
+-  grub_uint32_t recv_ip;
+ } GRUB_PACKED;
+ 
+ static int have_pending;
+@@ -57,12 +47,16 @@ grub_net_arp_send_request (struct grub_net_network_level_interface *inf,
+ 			   const grub_net_network_level_address_t *proto_addr)
+ {
+   struct grub_net_buff nb;
+-  struct arppkt *arp_packet;
++  struct arphdr *arp_header;
+   grub_net_link_level_address_t target_mac_addr;
+   grub_err_t err;
+   int i;
+   grub_uint8_t *nbd;
+   grub_uint8_t arp_data[128];
++  grub_uint8_t hln;
++  grub_uint8_t pln;
++  grub_uint8_t arp_packet_len;
++  grub_uint8_t *tmp_ptr;
+ 
+   if (proto_addr->type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4)
+     return grub_error (GRUB_ERR_BUG, "unsupported address family");
+@@ -73,23 +67,39 @@ grub_net_arp_send_request (struct grub_net_network_level_interface *inf,
+   grub_netbuff_clear (&nb);
+   grub_netbuff_reserve (&nb, 128);
+ 
+-  err = grub_netbuff_push (&nb, sizeof (*arp_packet));
++  hln = inf->card->default_address.len;
++  pln = sizeof (proto_addr->ipv4);
++  arp_packet_len = sizeof (*arp_header) + 2 * (hln + pln);
++
++  err = grub_netbuff_push (&nb, arp_packet_len);
+   if (err)
+     return err;
+ 
+-  arp_packet = (struct arppkt *) nb.data;
+-  arp_packet->hrd = grub_cpu_to_be16_compile_time (GRUB_NET_ARPHRD_ETHERNET);
+-  arp_packet->hln = 6;
+-  arp_packet->pro = grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP);
+-  arp_packet->pln = 4;
+-  arp_packet->op = grub_cpu_to_be16_compile_time (ARP_REQUEST);
+-  /* Sender hardware address.  */
+-  grub_memcpy (arp_packet->sender_mac, &inf->hwaddress.mac, 6);
+-  arp_packet->sender_ip = inf->address.ipv4;
+-  grub_memset (arp_packet->recv_mac, 0, 6);
+-  arp_packet->recv_ip = proto_addr->ipv4;
+-  /* Target protocol address */
+-  grub_memset (&target_mac_addr.mac, 0xff, 6);
++  arp_header = (struct arphdr *) nb.data;
++  arp_header->hrd = grub_cpu_to_be16 (inf->card->default_address.type);
++  arp_header->hln = hln;
++  arp_header->pro = grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP);
++  arp_header->pln = pln;
++  arp_header->op = grub_cpu_to_be16_compile_time (ARP_REQUEST);
++  tmp_ptr = nb.data + sizeof (*arp_header);
++
++  /* The source hardware address. */
++  grub_memcpy (tmp_ptr, inf->hwaddress.mac, hln);
++  tmp_ptr += hln;
++
++  /* The source protocol address. */
++  grub_memcpy (tmp_ptr, &inf->address.ipv4, pln);
++  tmp_ptr += pln;
++
++  /* The target hardware address. */
++  grub_memset (tmp_ptr, 0, hln);
++  tmp_ptr += hln;
++
++  /* The target protocol address */
++  grub_memcpy (tmp_ptr, &proto_addr->ipv4, pln);
++  tmp_ptr += pln;
++
++  grub_memset (&target_mac_addr.mac, 0xff, hln);
+ 
+   nbd = nb.data;
+   send_ethernet_packet (inf, &nb, target_mac_addr, GRUB_NET_ETHERTYPE_ARP);
+@@ -114,28 +124,53 @@ grub_err_t
+ grub_net_arp_receive (struct grub_net_buff *nb, struct grub_net_card *card,
+                       grub_uint16_t *vlantag)
+ {
+-  struct arppkt *arp_packet = (struct arppkt *) nb->data;
++  struct arphdr *arp_header = (struct arphdr *) nb->data;
+   grub_net_network_level_address_t sender_addr, target_addr;
+   grub_net_link_level_address_t sender_mac_addr;
+   struct grub_net_network_level_interface *inf;
++  grub_uint16_t hw_type;
++  grub_uint8_t hln;
++  grub_uint8_t pln;
++  grub_uint8_t arp_packet_len;
++  grub_uint8_t *tmp_ptr;
+ 
+-  if (arp_packet->pro != grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP)
+-      || arp_packet->pln != 4 || arp_packet->hln != 6
+-      || nb->tail - nb->data < (int) sizeof (*arp_packet))
++  hw_type = card->default_address.type;
++  hln = card->default_address.len;
++  pln = sizeof(sender_addr.ipv4);
++  arp_packet_len = sizeof (*arp_header) + 2 * (pln + hln);
++
++  if (arp_header->pro != grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP)
++      || arp_header->hrd != grub_cpu_to_be16 (hw_type)
++      || arp_header->hln != hln || arp_header->pln != pln
++      || nb->tail - nb->data < (int) arp_packet_len) {
+     return GRUB_ERR_NONE;
++  }
+ 
++  tmp_ptr =  nb->data + sizeof (*arp_header);
++
++  /* The source hardware address. */
++  sender_mac_addr.type = hw_type;
++  sender_mac_addr.len = hln;
++  grub_memcpy (sender_mac_addr.mac, tmp_ptr, hln);
++  tmp_ptr += hln;
++
++  /* The source protocol address. */
+   sender_addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
++  grub_memcpy(&sender_addr.ipv4, tmp_ptr, pln);
++  tmp_ptr += pln;
++
++  grub_net_link_layer_add_address (card, &sender_addr, &sender_mac_addr, 1);
++
++  /* The target hardware address. */
++  tmp_ptr += hln;
++
++  /* The target protocol address. */
+   target_addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
+-  sender_addr.ipv4 = arp_packet->sender_ip;
+-  target_addr.ipv4 = arp_packet->recv_ip;
+-  if (arp_packet->sender_ip == pending_req)
++  grub_memcpy(&target_addr.ipv4, tmp_ptr, pln);
++
++  if (sender_addr.ipv4 == pending_req)
+     have_pending = 1;
+ 
+-  sender_mac_addr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
+-  grub_memcpy (sender_mac_addr.mac, arp_packet->sender_mac,
+-	       sizeof (sender_mac_addr.mac));
+-  grub_net_link_layer_add_address (card, &sender_addr, &sender_mac_addr, 1);
+-
+   FOR_NET_NETWORK_LEVEL_INTERFACES (inf)
+   {
+     /* Verify vlantag id */
+@@ -148,11 +183,11 @@ grub_net_arp_receive (struct grub_net_buff *nb, struct grub_net_card *card,
+ 
+     /* Am I the protocol address target? */
+     if (grub_net_addr_cmp (&inf->address, &target_addr) == 0
+-	&& arp_packet->op == grub_cpu_to_be16_compile_time (ARP_REQUEST))
++	&& arp_header->op == grub_cpu_to_be16_compile_time (ARP_REQUEST))
+       {
+ 	grub_net_link_level_address_t target;
+ 	struct grub_net_buff nb_reply;
+-	struct arppkt *arp_reply;
++	struct arphdr *arp_reply;
+ 	grub_uint8_t arp_data[128];
+ 	grub_err_t err;
+ 
+@@ -161,25 +196,39 @@ grub_net_arp_receive (struct grub_net_buff *nb, struct grub_net_card *card,
+ 	grub_netbuff_clear (&nb_reply);
+ 	grub_netbuff_reserve (&nb_reply, 128);
+ 
+-	err = grub_netbuff_push (&nb_reply, sizeof (*arp_packet));
++	err = grub_netbuff_push (&nb_reply, arp_packet_len);
+ 	if (err)
+ 	  return err;
+ 
+-	arp_reply = (struct arppkt *) nb_reply.data;
++	arp_reply = (struct arphdr *) nb_reply.data;
+ 
+-	arp_reply->hrd = grub_cpu_to_be16_compile_time (GRUB_NET_ARPHRD_ETHERNET);
++	arp_reply->hrd = grub_cpu_to_be16 (hw_type);
+ 	arp_reply->pro = grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP);
+-	arp_reply->pln = 4;
+-	arp_reply->hln = 6;
++	arp_reply->pln = pln;
++	arp_reply->hln = hln;
+ 	arp_reply->op = grub_cpu_to_be16_compile_time (ARP_REPLY);
+-	arp_reply->sender_ip = arp_packet->recv_ip;
+-	arp_reply->recv_ip = arp_packet->sender_ip;
+-	arp_reply->hln = 6;
+-
+-	target.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
+-	grub_memcpy (target.mac, arp_packet->sender_mac, 6);
+-	grub_memcpy (arp_reply->sender_mac, inf->hwaddress.mac, 6);
+-	grub_memcpy (arp_reply->recv_mac, arp_packet->sender_mac, 6);
++
++	tmp_ptr = nb_reply.data + sizeof (*arp_reply);
++
++	/* The source hardware address. */
++	grub_memcpy (tmp_ptr, inf->hwaddress.mac, hln);
++	tmp_ptr += hln;
++
++	/* The source protocol address. */
++	grub_memcpy (tmp_ptr, &target_addr.ipv4, pln);
++	tmp_ptr += pln;
++
++	/* The target hardware address. */
++	grub_memcpy (tmp_ptr, sender_mac_addr.mac, hln);
++	tmp_ptr += hln;
++
++	/* The target protocol address */
++	grub_memcpy (tmp_ptr, &sender_addr.ipv4, pln);
++	tmp_ptr += pln;
++
++	target.type = hw_type;
++	target.len = hln;
++	grub_memcpy (target.mac, sender_mac_addr.mac, hln);
+ 
+ 	/* Change operation to REPLY and send packet */
+ 	send_ethernet_packet (inf, &nb_reply, target, GRUB_NET_ETHERTYPE_ARP);
+diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c
+index e28fb6a09f9..08b6b2b5d6c 100644
+--- a/grub-core/net/bootp.c
++++ b/grub-core/net/bootp.c
+@@ -233,7 +233,6 @@ grub_net_configure_by_dhcp_ack (const char *name,
+ 				int is_def, char **device, char **path)
+ {
+   grub_net_network_level_address_t addr;
+-  grub_net_link_level_address_t hwaddr;
+   struct grub_net_network_level_interface *inter;
+   int mask = -1;
+   char server_ip[sizeof ("xxx.xxx.xxx.xxx")];
+@@ -250,12 +249,8 @@ grub_net_configure_by_dhcp_ack (const char *name,
+   if (path)
+     *path = 0;
+ 
+-  grub_memcpy (hwaddr.mac, bp->mac_addr,
+-	       bp->hw_len < sizeof (hwaddr.mac) ? bp->hw_len
+-	       : sizeof (hwaddr.mac));
+-  hwaddr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
+-
+-  inter = grub_net_add_addr (name, card, &addr, &hwaddr, flags);
++  grub_dprintf("dhcp", "configuring dhcp for %s\n", name);
++  inter = grub_net_add_addr (name, card, &addr, &card->default_address, flags);
+   if (!inter)
+     return 0;
+ 
+@@ -567,7 +562,9 @@ send_dhcp_packet (struct grub_net_network_level_interface *iface)
+   grub_memset (pack, 0, sizeof (*pack));
+   pack->opcode = 1;
+   pack->hw_type = 1;
+-  pack->hw_len = 6;
++  pack->hw_len = iface->hwaddress.len > 16 ? 0
++                                              : iface->hwaddress.len;
++
+   err = grub_get_datetime (&date);
+   if (err || !grub_datetime2unixtime (&date, &t))
+     {
+@@ -580,7 +577,7 @@ send_dhcp_packet (struct grub_net_network_level_interface *iface)
+   else
+     pack->ident = iface->xid;
+ 
+-  grub_memcpy (&pack->mac_addr, &iface->hwaddress.mac, 6);
++  grub_memcpy (&pack->mac_addr, &iface->hwaddress.mac, pack->hw_len);
+ 
+   grub_netbuff_push (nb, sizeof (*udph));
+ 
+diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
+index 173fb63153c..a673bea807a 100644
+--- a/grub-core/net/drivers/efi/efinet.c
++++ b/grub-core/net/drivers/efi/efinet.c
+@@ -279,6 +279,9 @@ grub_efinet_findcards (void)
+ 	/* This should not happen... Why?  */
+ 	continue;
+ 
++      if (net->mode->hwaddr_size > GRUB_NET_MAX_LINK_ADDRESS_SIZE)
++	continue;
++
+       if (net->mode->state == GRUB_EFI_NETWORK_STOPPED
+ 	  && efi_call_1 (net->start, net) != GRUB_EFI_SUCCESS)
+ 	continue;
+@@ -315,10 +318,11 @@ grub_efinet_findcards (void)
+       card->name = grub_xasprintf ("efinet%d", i++);
+       card->driver = &efidriver;
+       card->flags = 0;
+-      card->default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
++      card->default_address.type = net->mode->if_type;
++      card->default_address.len = net->mode->hwaddr_size;
+       grub_memcpy (card->default_address.mac,
+ 		   net->mode->current_address,
+-		   sizeof (card->default_address.mac));
++		   net->mode->hwaddr_size);
+       card->efi_net = net;
+       card->efi_handle = *handle;
+ 
+diff --git a/grub-core/net/drivers/emu/emunet.c b/grub-core/net/drivers/emu/emunet.c
+index b194920861f..5b6c5e16a6d 100644
+--- a/grub-core/net/drivers/emu/emunet.c
++++ b/grub-core/net/drivers/emu/emunet.c
+@@ -46,6 +46,7 @@ static struct grub_net_card emucard =
+     .mtu = 1500,
+     .default_address = {
+ 			 .type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET,
++			 . len = 6,
+ 			 {.mac = {0, 1, 2, 3, 4, 5}}
+ 		       },
+     .flags = 0
+diff --git a/grub-core/net/drivers/i386/pc/pxe.c b/grub-core/net/drivers/i386/pc/pxe.c
+index 3f4152d036c..9f8fb4b6d2b 100644
+--- a/grub-core/net/drivers/i386/pc/pxe.c
++++ b/grub-core/net/drivers/i386/pc/pxe.c
+@@ -386,20 +386,21 @@ GRUB_MOD_INIT(pxe)
+   grub_memset (ui, 0, sizeof (*ui));
+   grub_pxe_call (GRUB_PXENV_UNDI_GET_INFORMATION, ui, pxe_rm_entry);
+ 
++  grub_pxe_card.default_address.len = 6;
+   grub_memcpy (grub_pxe_card.default_address.mac, ui->current_addr,
+-	       sizeof (grub_pxe_card.default_address.mac));
+-  for (i = 0; i < sizeof (grub_pxe_card.default_address.mac); i++)
++	       grub_pxe_card.default_address.len);
++  for (i = 0; i < grub_pxe_card.default_address.len; i++)
+     if (grub_pxe_card.default_address.mac[i] != 0)
+       break;
+-  if (i != sizeof (grub_pxe_card.default_address.mac))
++  if (i != grub_pxe_card.default_address.len)
+     {
+-      for (i = 0; i < sizeof (grub_pxe_card.default_address.mac); i++)
++      for (i = 0; i < grub_pxe_card.default_address.len; i++)
+ 	if (grub_pxe_card.default_address.mac[i] != 0xff)
+ 	  break;
+     }
+-  if (i == sizeof (grub_pxe_card.default_address.mac))
++  if (i == grub_pxe_card.default_address.len)
+     grub_memcpy (grub_pxe_card.default_address.mac, ui->permanent_addr,
+-		 sizeof (grub_pxe_card.default_address.mac));
++		 grub_pxe_card.default_address.len);
+   grub_pxe_card.mtu = ui->mtu;
+ 
+   grub_pxe_card.default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
+diff --git a/grub-core/net/drivers/ieee1275/ofnet.c b/grub-core/net/drivers/ieee1275/ofnet.c
+index 3860b6f78d8..bcb3f9ea02d 100644
+--- a/grub-core/net/drivers/ieee1275/ofnet.c
++++ b/grub-core/net/drivers/ieee1275/ofnet.c
+@@ -160,6 +160,7 @@ grub_ieee1275_parse_bootpath (const char *devpath, char *bootpath,
+   grub_uint16_t vlantag = 0;
+ 
+   hw_addr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
++  hw_addr.len = 6;
+ 
+   args = bootpath + grub_strlen (devpath) + 1;
+   do
+@@ -503,6 +504,7 @@ search_net_devices (struct grub_ieee1275_devalias *alias)
+     grub_memcpy (&lla.mac, pprop, 6);
+ 
+   lla.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
++  lla.len = 6;
+   card->default_address = lla;
+ 
+   card->txbufsize = ALIGN_UP (card->mtu, 64) + 256;
+diff --git a/grub-core/net/drivers/uboot/ubootnet.c b/grub-core/net/drivers/uboot/ubootnet.c
+index 056052e40d5..22ebcbf211e 100644
+--- a/grub-core/net/drivers/uboot/ubootnet.c
++++ b/grub-core/net/drivers/uboot/ubootnet.c
+@@ -131,6 +131,7 @@ GRUB_MOD_INIT (ubootnet)
+ 
+       grub_memcpy (&(card->default_address.mac), &devinfo->di_net.hwaddr, 6);
+       card->default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
++      card->default_address.len = 6;
+ 
+       card->txbufsize = ALIGN_UP (card->mtu, 64) + 256;
+       card->txbuf = grub_zalloc (card->txbufsize);
+diff --git a/grub-core/net/ethernet.c b/grub-core/net/ethernet.c
+index 4d7ceed6f93..9aae83a5eb4 100644
+--- a/grub-core/net/ethernet.c
++++ b/grub-core/net/ethernet.c
+@@ -29,13 +29,6 @@
+ 
+ #define LLCADDRMASK 0x7f
+ 
+-struct etherhdr
+-{
+-  grub_uint8_t dst[6];
+-  grub_uint8_t src[6];
+-  grub_uint16_t type;
+-} GRUB_PACKED;
+-
+ struct llchdr
+ {
+   grub_uint8_t dsap;
+@@ -55,13 +48,15 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf,
+ 		      grub_net_link_level_address_t target_addr,
+ 		      grub_net_ethertype_t ethertype)
+ {
+-  struct etherhdr *eth;
++  grub_uint8_t *eth;
+   grub_err_t err;
+-  grub_uint8_t etherhdr_size;
+-  grub_uint16_t vlantag_id = VLANTAG_IDENTIFIER;
++  grub_uint32_t vlantag = 0;
++  grub_uint8_t hw_addr_len = inf->card->default_address.len;
++  grub_uint8_t etherhdr_size = 2 * hw_addr_len + 2;
+ 
+-  etherhdr_size = sizeof (*eth);
+-  COMPILE_TIME_ASSERT (sizeof (*eth) + 4 < GRUB_NET_MAX_LINK_HEADER_SIZE);
++  /* Source and destination link addresses + ethertype + vlan tag */
++  COMPILE_TIME_ASSERT ((GRUB_NET_MAX_LINK_ADDRESS_SIZE * 2 + 2 + 4) <
++		       GRUB_NET_MAX_LINK_HEADER_SIZE);
+ 
+   /* Increase ethernet header in case of vlantag */
+   if (inf->vlantag != 0)
+@@ -70,11 +65,22 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf,
+   err = grub_netbuff_push (nb, etherhdr_size);
+   if (err)
+     return err;
+-  eth = (struct etherhdr *) nb->data;
+-  grub_memcpy (eth->dst, target_addr.mac, 6);
+-  grub_memcpy (eth->src, inf->hwaddress.mac, 6);
++  eth = nb->data;
++  grub_memcpy (eth, target_addr.mac, hw_addr_len);
++  eth += hw_addr_len;
++  grub_memcpy (eth, inf->hwaddress.mac, hw_addr_len);
++  eth += hw_addr_len;
++
++  /* Check if a vlan-tag is present. */
++  if (vlantag != 0)
++    {
++      *((grub_uint32_t *)eth) = grub_cpu_to_be32 (vlantag);
++      eth += sizeof (vlantag);
++    }
++
++  /* Write ethertype */
++  *((grub_uint16_t*) eth) = grub_cpu_to_be16 (ethertype);
+ 
+-  eth->type = grub_cpu_to_be16 (ethertype);
+   if (!inf->card->opened)
+     {
+       err = GRUB_ERR_NONE;
+@@ -85,18 +91,6 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf,
+       inf->card->opened = 1;
+     }
+ 
+-  /* Check and add a vlan-tag if needed. */
+-  if (inf->vlantag != 0)
+-    {
+-      /* Move eth type to the right */
+-      grub_memcpy ((char *) nb->data + etherhdr_size - 2,
+-                   (char *) nb->data + etherhdr_size - 6, 2);
+-
+-      /* Add the tag in the middle */
+-      grub_memcpy ((char *) nb->data + etherhdr_size - 6, &vlantag_id, 2);
+-      grub_memcpy ((char *) nb->data + etherhdr_size - 4, (char *) &(inf->vlantag), 2);
+-    }
+-
+   return inf->card->driver->send (inf->card, nb);
+ }
+ 
+@@ -104,31 +98,40 @@ grub_err_t
+ grub_net_recv_ethernet_packet (struct grub_net_buff *nb,
+ 			       struct grub_net_card *card)
+ {
+-  struct etherhdr *eth;
++  grub_uint8_t *eth;
+   struct llchdr *llch;
+   struct snaphdr *snaph;
+   grub_net_ethertype_t type;
+   grub_net_link_level_address_t hwaddress;
+   grub_net_link_level_address_t src_hwaddress;
+   grub_err_t err;
+-  grub_uint8_t etherhdr_size = sizeof (*eth);
++  grub_uint8_t hw_addr_len = card->default_address.len;
++  grub_uint8_t etherhdr_size = 2 * hw_addr_len + 2;
+   grub_uint16_t vlantag = 0;
+ 
++  eth = nb->data;
+ 
+-  /* Check if a vlan-tag is present. If so, the ethernet header is 4 bytes */
+-  /* longer than the original one. The vlantag id is extracted and the header */
+-  /* is reseted to the original size. */
+-  if (grub_get_unaligned16 (nb->data + etherhdr_size - 2) == VLANTAG_IDENTIFIER)
++  hwaddress.type = card->default_address.type;
++  hwaddress.len = hw_addr_len;
++  grub_memcpy (hwaddress.mac, eth, hw_addr_len);
++  eth += hw_addr_len;
++
++  src_hwaddress.type = card->default_address.type;
++  src_hwaddress.len = hw_addr_len;
++  grub_memcpy (src_hwaddress.mac, eth, hw_addr_len);
++  eth += hw_addr_len;
++
++  type = grub_be_to_cpu16 (*(grub_uint16_t*)(eth));
++  if (type == VLANTAG_IDENTIFIER)
+     {
+-      vlantag = grub_get_unaligned16 (nb->data + etherhdr_size);
++      /* Skip vlan tag */
++      eth += 2;
++      vlantag = grub_be_to_cpu16 (*(grub_uint16_t*)(eth));
+       etherhdr_size += 4;
+-      /* Move eth type to the original position */
+-      grub_memcpy((char *) nb->data + etherhdr_size - 6,
+-                  (char *) nb->data + etherhdr_size - 2, 2);
++      eth += 2;
++      type = grub_be_to_cpu16 (*(grub_uint16_t*)(eth));
+     }
+ 
+-  eth = (struct etherhdr *) nb->data;
+-  type = grub_be_to_cpu16 (eth->type);
+   err = grub_netbuff_pull (nb, etherhdr_size);
+   if (err)
+     return err;
+@@ -148,11 +151,6 @@ grub_net_recv_ethernet_packet (struct grub_net_buff *nb,
+ 	}
+     }
+ 
+-  hwaddress.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
+-  grub_memcpy (hwaddress.mac, eth->dst, sizeof (hwaddress.mac));
+-  src_hwaddress.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
+-  grub_memcpy (src_hwaddress.mac, eth->src, sizeof (src_hwaddress.mac));
+-
+   switch (type)
+     {
+       /* ARP packet. */
+diff --git a/grub-core/net/icmp6.c b/grub-core/net/icmp6.c
+index 2cbd95dce25..56a3ec5c8e8 100644
+--- a/grub-core/net/icmp6.c
++++ b/grub-core/net/icmp6.c
+@@ -231,8 +231,9 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb,
+ 		&& ohdr->len == 1)
+ 	      {
+ 		grub_net_link_level_address_t ll_address;
+-		ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
+-		grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac));
++		ll_address.type = card->default_address.type;
++		ll_address.len = card->default_address.len;
++		grub_memcpy (ll_address.mac, ohdr + 1, ll_address.len);
+ 		grub_net_link_layer_add_address (card, source, &ll_address, 0);
+ 	      }
+ 	  }
+@@ -335,8 +336,9 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb,
+ 		&& ohdr->len == 1)
+ 	      {
+ 		grub_net_link_level_address_t ll_address;
+-		ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
+-		grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac));
++		ll_address.type = card->default_address.type;
++		ll_address.len = card->default_address.len;
++		grub_memcpy (ll_address.mac, ohdr + 1, ll_address.len);
+ 		grub_net_link_layer_add_address (card, source, &ll_address, 0);
+ 	      }
+ 	  }
+@@ -384,8 +386,9 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb,
+ 		&& ohdr->len == 1)
+ 	      {
+ 		grub_net_link_level_address_t ll_address;
+-		ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
+-		grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac));
++		ll_address.type = card->default_address.type;
++		ll_address.len = card->default_address.len;
++		grub_memcpy (ll_address.mac, ohdr + 1, ll_address.len);
+ 		grub_net_link_layer_add_address (card, source, &ll_address, 0);
+ 	      }
+ 	    if (ohdr->type == OPTION_PREFIX && ohdr->len == 4)
+diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c
+index ea5edf8f1f6..a5896f6dc26 100644
+--- a/grub-core/net/ip.c
++++ b/grub-core/net/ip.c
+@@ -276,8 +276,8 @@ handle_dgram (struct grub_net_buff *nb,
+ 	  if (inf->card == card
+ 	      && inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV
+ 	      && inf->hwaddress.type == GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET
+-	      && grub_memcmp (inf->hwaddress.mac, &bootp->mac_addr,
+-			      sizeof (inf->hwaddress.mac)) == 0)
++	      && (grub_memcmp (inf->hwaddress.mac, &bootp->mac_addr,
++			       bootp->hw_len) == 0 || bootp->hw_len == 0))
+ 	    {
+ 	      grub_net_process_dhcp (nb, inf);
+ 	      grub_netbuff_free (nb);
+diff --git a/grub-core/net/net.c b/grub-core/net/net.c
+index 22f2689aaeb..a46f82362ed 100644
+--- a/grub-core/net/net.c
++++ b/grub-core/net/net.c
+@@ -133,8 +133,9 @@ grub_net_link_layer_resolve (struct grub_net_network_level_interface *inf,
+ 								   << 48)
+ 	  && proto_addr->ipv6[1] == (grub_be_to_cpu64_compile_time (1))))
+     {
+-      hw_addr->type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
+-      grub_memset (hw_addr->mac, -1, 6);
++      hw_addr->type = inf->card->default_address.type;
++      hw_addr->len = inf->card->default_address.len;
++      grub_memset (hw_addr->mac, -1, hw_addr->len);
+       return GRUB_ERR_NONE;
+     }
+ 
+@@ -142,6 +143,7 @@ grub_net_link_layer_resolve (struct grub_net_network_level_interface *inf,
+       && ((grub_be_to_cpu64 (proto_addr->ipv6[0]) >> 56) == 0xff))
+     {
+       hw_addr->type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
++      hw_addr->len = inf->card->default_address.len;
+       hw_addr->mac[0] = 0x33;
+       hw_addr->mac[1] = 0x33;
+       hw_addr->mac[2] = ((grub_be_to_cpu64 (proto_addr->ipv6[1]) >> 24) & 0xff);
+@@ -762,23 +764,23 @@ grub_net_addr_to_str (const grub_net_network_level_address_t *target, char *buf)
+ void
+ grub_net_hwaddr_to_str (const grub_net_link_level_address_t *addr, char *str)
+ {
+-  str[0] = 0;
+-  switch (addr->type)
++  char *ptr;
++  unsigned i;
++  int maxstr;
++
++  if (addr->len > GRUB_NET_MAX_LINK_ADDRESS_SIZE)
+     {
+-    case GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET:
+-      {
+-	char *ptr;
+-	unsigned i;
+-	for (ptr = str, i = 0; i < ARRAY_SIZE (addr->mac); i++)
+-	  {
+-	    grub_snprintf (ptr, GRUB_NET_MAX_STR_HWADDR_LEN - (ptr - str),
+-			   "%02x:", addr->mac[i] & 0xff);
+-	    ptr += (sizeof ("XX:") - 1);
+-	  }
+-      return;
+-      }
++       str[0] = 0;
++       grub_printf (_("Unsupported hw address type %d len %d\n"),
++		    addr->type, addr->len);
++       return;
++    }
++  maxstr = addr->len * grub_strlen ("XX:");
++  for (ptr = str, i = 0; i < addr->len; i++)
++    {
++      ptr += grub_snprintf (ptr, maxstr - (ptr - str),
++		     "%02x:", addr->mac[i] & 0xff);
+     }
+-  grub_printf (_("Unsupported hw address type %d\n"), addr->type);
+ }
+ 
+ int
+@@ -789,13 +791,17 @@ grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a,
+     return -1;
+   if (a->type > b->type)
+     return +1;
+-  switch (a->type)
++  if (a->len < b->len)
++    return -1;
++  if (a->len > b->len)
++    return +1;
++  if (a->len > GRUB_NET_MAX_LINK_ADDRESS_SIZE)
+     {
+-    case GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET:
+-      return grub_memcmp (a->mac, b->mac, sizeof (a->mac));
++      grub_printf (_("Unsupported hw address type %d len %d\n"),
++		   a->type, a->len);
++      return + 1;
+     }
+-  grub_printf (_("Unsupported hw address type %d\n"), a->type);
+-  return 1;
++  return grub_memcmp (a->mac, b->mac, a->len);
+ }
+ 
+ int
+diff --git a/include/grub/net.h b/include/grub/net.h
+index 8a05ec4fe7a..af0404db7e3 100644
+--- a/include/grub/net.h
++++ b/include/grub/net.h
+@@ -29,7 +29,8 @@
+ 
+ enum
+   {
+-    GRUB_NET_MAX_LINK_HEADER_SIZE = 64,
++    GRUB_NET_MAX_LINK_HEADER_SIZE = 96,
++    GRUB_NET_MAX_LINK_ADDRESS_SIZE = 32,
+     GRUB_NET_UDP_HEADER_SIZE = 8,
+     GRUB_NET_TCP_HEADER_SIZE = 20,
+     GRUB_NET_OUR_IPV4_HEADER_SIZE = 20,
+@@ -42,15 +43,17 @@ enum
+ 
+ typedef enum grub_link_level_protocol_id 
+ {
+-  GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET
++  /* IANA ARP constant to define hardware type. */
++  GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET = 1,
+ } grub_link_level_protocol_id_t;
+ 
+ typedef struct grub_net_link_level_address
+ {
+   grub_link_level_protocol_id_t type;
++  grub_uint8_t len;
+   union
+   {
+-    grub_uint8_t mac[6];
++    grub_uint8_t mac[GRUB_NET_MAX_LINK_ADDRESS_SIZE];
+   };
+ } grub_net_link_level_address_t;
+ 
+@@ -566,11 +569,13 @@ grub_net_addr_cmp (const grub_net_network_level_address_t *a,
+ #define GRUB_NET_MAX_STR_ADDR_LEN sizeof ("XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX")
+ 
+ /*
+-  Currently suppoerted adresses:
+-  ethernet:   XX:XX:XX:XX:XX:XX
++  Up to 32 byte hardware address supported, see GRUB_NET_MAX_LINK_ADDRESS_SIZE
+  */
+-
+-#define GRUB_NET_MAX_STR_HWADDR_LEN (sizeof ("XX:XX:XX:XX:XX:XX"))
++#define GRUB_NET_MAX_STR_HWADDR_LEN (sizeof (\
++	"XX:XX:XX:XX:XX:XX:XX:XX:"\
++	"XX:XX:XX:XX:XX:XX:XX:XX:"\
++	"XX:XX:XX:XX:XX:XX:XX:XX:"\
++	"XX:XX:XX:XX:XX:XX:XX:XX"))
+ 
+ void
+ grub_net_addr_to_str (const grub_net_network_level_address_t *target,
diff --git a/SOURCES/0088-net-read-bracketed-ipv6-addrs-and-port-numbers.patch b/SOURCES/0088-net-read-bracketed-ipv6-addrs-and-port-numbers.patch
new file mode 100644
index 0000000..834b96e
--- /dev/null
+++ b/SOURCES/0088-net-read-bracketed-ipv6-addrs-and-port-numbers.patch
@@ -0,0 +1,270 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aaron Miller <aaronmiller@fb.com>
+Date: Fri, 29 Jul 2016 17:41:38 +0800
+Subject: [PATCH] net: read bracketed ipv6 addrs and port numbers
+
+Allow specifying port numbers for http and tftp paths, and allow ipv6 addresses
+to be recognized with brackets around them, which is required to specify a port
+number
+
+Signed-off-by: Aaron Miller <aaronmiller@fb.com>
+[pjones: various bug fixes]
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/net/http.c | 25 ++++++++++++---
+ grub-core/net/net.c  | 87 +++++++++++++++++++++++++++++++++++++++++++++++++---
+ grub-core/net/tftp.c |  8 +++--
+ include/grub/net.h   |  1 +
+ 4 files changed, 109 insertions(+), 12 deletions(-)
+
+diff --git a/grub-core/net/http.c b/grub-core/net/http.c
+index b616cf40b1e..12a2632ea55 100644
+--- a/grub-core/net/http.c
++++ b/grub-core/net/http.c
+@@ -289,7 +289,9 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)),
+ 	  nb2 = grub_netbuff_alloc (data->chunk_rem);
+ 	  if (!nb2)
+ 	    return grub_errno;
+-	  grub_netbuff_put (nb2, data->chunk_rem);
++	  err = grub_netbuff_put (nb2, data->chunk_rem);
++	  if (err)
++	    return grub_errno;
+ 	  grub_memcpy (nb2->data, nb->data, data->chunk_rem);
+ 	  if (file->device->net->packs.count >= 20)
+ 	    {
+@@ -312,12 +314,14 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial)
+   int i;
+   struct grub_net_buff *nb;
+   grub_err_t err;
++  char* server = file->device->net->server;
++  int port = file->device->net->port;
+ 
+   nb = grub_netbuff_alloc (GRUB_NET_TCP_RESERVE_SIZE
+ 			   + sizeof ("GET ") - 1
+ 			   + grub_strlen (data->filename)
+ 			   + sizeof (" HTTP/1.1\r\nHost: ") - 1
+-			   + grub_strlen (file->device->net->server)
++			   + grub_strlen (server) + sizeof (":XXXXXXXXXX")
+ 			   + sizeof ("\r\nUser-Agent: " PACKAGE_STRING
+ 				     "\r\n") - 1
+ 			   + sizeof ("Range: bytes=XXXXXXXXXXXXXXXXXXXX"
+@@ -356,7 +360,7 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial)
+ 	       sizeof (" HTTP/1.1\r\nHost: ") - 1);
+ 
+   ptr = nb->tail;
+-  err = grub_netbuff_put (nb, grub_strlen (file->device->net->server));
++  err = grub_netbuff_put (nb, grub_strlen (server));
+   if (err)
+     {
+       grub_netbuff_free (nb);
+@@ -365,6 +369,15 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial)
+   grub_memcpy (ptr, file->device->net->server,
+ 	       grub_strlen (file->device->net->server));
+ 
++  if (port)
++    {
++      ptr = nb->tail;
++      grub_snprintf ((char *) ptr,
++	  sizeof (":XXXXXXXXXX"),
++	  ":%d",
++	  port);
++    }
++
+   ptr = nb->tail;
+   err = grub_netbuff_put (nb, 
+ 			  sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n")
+@@ -390,8 +403,10 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial)
+   grub_netbuff_put (nb, 2);
+   grub_memcpy (ptr, "\r\n", 2);
+ 
+-  data->sock = grub_net_tcp_open (file->device->net->server,
+-				  HTTP_PORT, http_receive,
++  grub_dprintf ("http", "opening path %s on host %s TCP port %d\n",
++		data->filename, server, port ? port : HTTP_PORT);
++  data->sock = grub_net_tcp_open (server,
++				  port ? port : HTTP_PORT, http_receive,
+ 				  http_err, NULL,
+ 				  file);
+   if (!data->sock)
+diff --git a/grub-core/net/net.c b/grub-core/net/net.c
+index a46f82362ed..0ce5e675ed7 100644
+--- a/grub-core/net/net.c
++++ b/grub-core/net/net.c
+@@ -444,6 +444,13 @@ parse_ip6 (const char *val, grub_uint64_t *ip, const char **rest)
+   grub_uint16_t newip[8];
+   const char *ptr = val;
+   int word, quaddot = -1;
++  int bracketed = 0;
++
++  if (ptr[0] == '[')
++    {
++      bracketed = 1;
++      ptr++;
++    }
+ 
+   if (ptr[0] == ':' && ptr[1] != ':')
+     return 0;
+@@ -482,6 +489,8 @@ parse_ip6 (const char *val, grub_uint64_t *ip, const char **rest)
+       grub_memset (&newip[quaddot], 0, (7 - word) * sizeof (newip[0]));
+     }
+   grub_memcpy (ip, newip, 16);
++  if (bracketed && *ptr == ']')
++    ptr++;
+   if (rest)
+     *rest = ptr;
+   return 1;
+@@ -1343,8 +1352,10 @@ grub_net_open_real (const char *name)
+ {
+   grub_net_app_level_t proto;
+   const char *protname, *server;
++  char *host;
+   grub_size_t protnamelen;
+   int try;
++  int port = 0;
+ 
+   if (grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) == 0)
+     {
+@@ -1382,6 +1393,72 @@ grub_net_open_real (const char *name)
+       return NULL;
+     }  
+ 
++  char* port_start;
++  /* ipv6 or port specified? */
++  if ((port_start = grub_strchr (server, ':')))
++    {
++      char* ipv6_begin;
++      if((ipv6_begin = grub_strchr (server, '[')))
++	{
++	  char* ipv6_end = grub_strchr (server, ']');
++	  if(!ipv6_end)
++	    {
++	      grub_error (GRUB_ERR_NET_BAD_ADDRESS,
++		      N_("mismatched [ in address"));
++	      return NULL;
++	    }
++	  /* port number after bracketed ipv6 addr */
++	  if(ipv6_end[1] == ':')
++	    {
++	      port = grub_strtoul (ipv6_end + 2, NULL, 10);
++	      if(port > 65535)
++		{
++		  grub_error (GRUB_ERR_NET_BAD_ADDRESS,
++			  N_("bad port number"));
++		  return NULL;
++		}
++	    }
++	  host = grub_strndup (ipv6_begin, (ipv6_end - ipv6_begin) + 1);
++	}
++      else
++	{
++	  if (grub_strchr (port_start + 1, ':'))
++	    {
++	      int iplen = grub_strlen (server);
++	      /* bracket bare ipv6 addrs */
++	      host = grub_malloc (iplen + 3);
++	      if(!host)
++		{
++		  return NULL;
++		}
++	      host[0] = '[';
++	      grub_memcpy (host + 1, server, iplen);
++	      host[iplen + 1] = ']';
++	      host[iplen + 2] = '\0';
++	    }
++	  else
++	    {
++	      /* hostname:port or ipv4:port */
++	      port = grub_strtol (port_start + 1, NULL, 10);
++	      if(port > 65535)
++		{
++		  grub_error (GRUB_ERR_NET_BAD_ADDRESS,
++			  N_("bad port number"));
++		  return NULL;
++		}
++	      host = grub_strndup (server, port_start - server);
++	    }
++	}
++    }
++  else
++    {
++      host = grub_strdup (server);
++    }
++  if (!host)
++    {
++      return NULL;
++    }
++
+   for (try = 0; try < 2; try++)
+     {
+       FOR_NET_APP_LEVEL (proto)
+@@ -1391,14 +1468,13 @@ grub_net_open_real (const char *name)
+ 	  {
+ 	    grub_net_t ret = grub_zalloc (sizeof (*ret));
+ 	    if (!ret)
+-	      return NULL;
+-	    ret->protocol = proto;
+-	    ret->server = grub_strdup (server);
+-	    if (!ret->server)
+ 	      {
+-		grub_free (ret);
++		grub_free (host);
+ 		return NULL;
+ 	      }
++	    ret->protocol = proto;
++	    ret->port = port;
++	    ret->server = host;
+ 	    ret->fs = &grub_net_fs;
+ 	    return ret;
+ 	  }
+@@ -1473,6 +1549,7 @@ grub_net_open_real (const char *name)
+   grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' not found"),
+ 	      name);
+ 
++  grub_free (host);
+   return NULL;
+ }
+ 
+diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c
+index 4ab2f5c7357..d54b13f09ff 100644
+--- a/grub-core/net/tftp.c
++++ b/grub-core/net/tftp.c
+@@ -295,6 +295,7 @@ tftp_open (struct grub_file *file, const char *filename)
+   grub_err_t err;
+   grub_uint8_t *nbd;
+   grub_net_network_level_address_t addr;
++  int port = file->device->net->port;
+ 
+   data = grub_zalloc (sizeof (*data));
+   if (!data)
+@@ -362,14 +363,17 @@ tftp_open (struct grub_file *file, const char *filename)
+   err = grub_net_resolve_address (file->device->net->server, &addr);
+   if (err)
+     {
+-      grub_dprintf("tftp", "Address resolution failed: %d\n", err);
++      grub_dprintf ("tftp", "Address resolution failed: %d\n", err);
++      grub_dprintf ("tftp", "file_size is %llu, block_size is %llu\n",
++		    (unsigned long long)data->file_size,
++		    (unsigned long long)data->block_size);
+       grub_free (data);
+       return err;
+     }
+ 
+   grub_dprintf("tftp", "opening connection\n");
+   data->sock = grub_net_udp_open (addr,
+-				  TFTP_SERVER_PORT, tftp_receive,
++				  port ? port : TFTP_SERVER_PORT, tftp_receive,
+ 				  file);
+   if (!data->sock)
+     {
+diff --git a/include/grub/net.h b/include/grub/net.h
+index af0404db7e3..d55d505a03a 100644
+--- a/include/grub/net.h
++++ b/include/grub/net.h
+@@ -273,6 +273,7 @@ typedef struct grub_net
+ {
+   char *server;
+   char *name;
++  int port;
+   grub_net_app_level_t protocol;
+   grub_net_packets_t packs;
+   grub_off_t offset;
diff --git a/SOURCES/0089-bootp-New-net_bootp6-command.patch b/SOURCES/0089-bootp-New-net_bootp6-command.patch
new file mode 100644
index 0000000..21b3871
--- /dev/null
+++ b/SOURCES/0089-bootp-New-net_bootp6-command.patch
@@ -0,0 +1,1368 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Chang <mchang@suse.com>
+Date: Wed, 10 Jul 2019 15:42:36 +0200
+Subject: [PATCH] bootp: New net_bootp6 command
+
+Implement new net_bootp6 command for IPv6 network auto configuration via the
+DHCPv6 protocol (RFC3315).
+
+Signed-off-by: Michael Chang <mchang@suse.com>
+Signed-off-by: Ken Lin <ken.lin@hpe.com>
+[pjones: Put back our code to add a local route]
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/net/bootp.c              | 1059 ++++++++++++++++++++++++++++++------
+ grub-core/net/drivers/efi/efinet.c |   20 +-
+ grub-core/net/ip.c                 |   39 ++
+ include/grub/efi/api.h             |    2 +-
+ include/grub/net.h                 |   91 ++--
+ 5 files changed, 1002 insertions(+), 209 deletions(-)
+
+diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c
+index 08b6b2b5d6c..fe93b80f1cf 100644
+--- a/grub-core/net/bootp.c
++++ b/grub-core/net/bootp.c
+@@ -24,6 +24,98 @@
+ #include <grub/net/netbuff.h>
+ #include <grub/net/udp.h>
+ #include <grub/datetime.h>
++#include <grub/time.h>
++#include <grub/list.h>
++
++static int
++dissect_url (const char *url, char **proto, char **host, char **path)
++{
++  const char *p, *ps;
++  grub_size_t l;
++
++  *proto = *host = *path = NULL;
++  ps = p = url;
++
++  while ((p = grub_strchr (p, ':')))
++    {
++      if (grub_strlen (p) < sizeof ("://") - 1)
++	break;
++      if (grub_memcmp (p, "://", sizeof ("://") - 1) == 0)
++	{
++	  l = p - ps;
++	  *proto = grub_malloc (l + 1);
++	  if (!*proto)
++	    {
++	      grub_print_error ();
++	      return 0;
++	    }
++
++	  grub_memcpy (*proto, ps, l);
++	  (*proto)[l] = '\0';
++	  p +=  sizeof ("://") - 1;
++	  break;
++	}
++      ++p;
++    }
++
++  if (!*proto)
++    {
++      grub_dprintf ("bootp", "url: %s is not valid, protocol not found\n", url);
++      return 0;
++    }
++
++  ps = p;
++  p = grub_strchr (p, '/');
++
++  if (!p)
++    {
++      grub_dprintf ("bootp", "url: %s is not valid, host/path not found\n", url);
++      grub_free (*proto);
++      *proto = NULL;
++      return 0;
++    }
++
++  l = p - ps;
++
++  if (l > 2 && ps[0] == '[' && ps[l - 1] == ']')
++    {
++      *host = grub_malloc (l - 1);
++      if (!*host)
++	{
++	  grub_print_error ();
++	  grub_free (*proto);
++	  *proto = NULL;
++	  return 0;
++	}
++      grub_memcpy (*host, ps + 1, l - 2);
++      (*host)[l - 2] = 0;
++    }
++  else
++    {
++      *host = grub_malloc (l + 1);
++      if (!*host)
++	{
++	  grub_print_error ();
++	  grub_free (*proto);
++	  *proto = NULL;
++	  return 0;
++	}
++      grub_memcpy (*host, ps, l);
++      (*host)[l] = 0;
++    }
++
++  *path = grub_strdup (p);
++  if (!*path)
++    {
++      grub_print_error ();
++      grub_free (*host);
++      grub_free (*proto);
++      *host = NULL;
++      *proto = NULL;
++      return 0;
++    }
++  return 1;
++}
+ 
+ struct grub_dhcp_discover_options
+ {
+@@ -604,6 +696,584 @@ out:
+   return err;
+ }
+ 
++/* The default netbuff size for sending DHCPv6 packets which should be
++   large enough to hold the information */
++#define GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE 512
++
++struct grub_dhcp6_options
++{
++  grub_uint8_t *client_duid;
++  grub_uint16_t client_duid_len;
++  grub_uint8_t *server_duid;
++  grub_uint16_t server_duid_len;
++  grub_uint32_t iaid;
++  grub_uint32_t t1;
++  grub_uint32_t t2;
++  grub_net_network_level_address_t *ia_addr;
++  grub_uint32_t preferred_lifetime;
++  grub_uint32_t valid_lifetime;
++  grub_net_network_level_address_t *dns_server_addrs;
++  grub_uint16_t num_dns_server;
++  char *boot_file_proto;
++  char *boot_file_server_ip;
++  char *boot_file_path;
++};
++
++typedef struct grub_dhcp6_options *grub_dhcp6_options_t;
++
++struct grub_dhcp6_session
++{
++  struct grub_dhcp6_session *next;
++  struct grub_dhcp6_session **prev;
++  grub_uint32_t iaid;
++  grub_uint32_t transaction_id:24;
++  grub_uint64_t start_time;
++  struct grub_net_dhcp6_option_duid_ll duid;
++  struct grub_net_network_level_interface *iface;
++
++  /* The associated dhcpv6 options */
++  grub_dhcp6_options_t adv;
++  grub_dhcp6_options_t reply;
++};
++
++typedef struct grub_dhcp6_session *grub_dhcp6_session_t;
++
++typedef void (*dhcp6_option_hook_fn) (const struct grub_net_dhcp6_option *opt, void *data);
++
++static void
++foreach_dhcp6_option (const struct grub_net_dhcp6_option *opt, grub_size_t size,
++		      dhcp6_option_hook_fn hook, void *hook_data);
++
++static void
++parse_dhcp6_iaaddr (const struct grub_net_dhcp6_option *opt, void *data)
++{
++  grub_dhcp6_options_t dhcp6 = (grub_dhcp6_options_t )data;
++
++  grub_uint16_t code = grub_be_to_cpu16 (opt->code);
++  grub_uint16_t len = grub_be_to_cpu16 (opt->len);
++
++  if (code == GRUB_NET_DHCP6_OPTION_IAADDR)
++    {
++      const struct grub_net_dhcp6_option_iaaddr *iaaddr;
++      iaaddr = (const struct grub_net_dhcp6_option_iaaddr *)opt->data;
++
++      if (len < sizeof (*iaaddr))
++	{
++	  grub_dprintf ("bootp", "DHCPv6: code %u with insufficient length %u\n", code, len);
++	  return;
++	}
++      if (!dhcp6->ia_addr)
++	{
++	  dhcp6->ia_addr = grub_malloc (sizeof(*dhcp6->ia_addr));
++	  dhcp6->ia_addr->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
++	  dhcp6->ia_addr->ipv6[0] = grub_get_unaligned64 (iaaddr->addr);
++	  dhcp6->ia_addr->ipv6[1] = grub_get_unaligned64 (iaaddr->addr + 8);
++	  dhcp6->preferred_lifetime = grub_be_to_cpu32 (iaaddr->preferred_lifetime);
++	  dhcp6->valid_lifetime = grub_be_to_cpu32 (iaaddr->valid_lifetime);
++	}
++    }
++}
++
++static void
++parse_dhcp6_option (const struct grub_net_dhcp6_option *opt, void *data)
++{
++  grub_dhcp6_options_t dhcp6 = (grub_dhcp6_options_t)data;
++  grub_uint16_t code = grub_be_to_cpu16 (opt->code);
++  grub_uint16_t len = grub_be_to_cpu16 (opt->len);
++
++  switch (code)
++    {
++      case GRUB_NET_DHCP6_OPTION_CLIENTID:
++
++	if (dhcp6->client_duid || !len)
++	  {
++	    grub_dprintf ("bootp", "Skipped DHCPv6 CLIENTID with length %u\n", len);
++	    break;
++	  }
++	dhcp6->client_duid = grub_malloc (len);
++	grub_memcpy (dhcp6->client_duid, opt->data, len);
++	dhcp6->client_duid_len = len;
++	break;
++
++      case GRUB_NET_DHCP6_OPTION_SERVERID:
++
++	if (dhcp6->server_duid || !len)
++	  {
++	    grub_dprintf ("bootp", "Skipped DHCPv6 SERVERID with length %u\n", len);
++	    break;
++	  }
++	dhcp6->server_duid = grub_malloc (len);
++	grub_memcpy (dhcp6->server_duid, opt->data, len);
++	dhcp6->server_duid_len = len;
++	break;
++
++      case GRUB_NET_DHCP6_OPTION_IA_NA:
++	{
++	  const struct grub_net_dhcp6_option_iana *ia_na;
++	  grub_uint16_t data_len;
++
++	  if (dhcp6->iaid || len < sizeof (*ia_na))
++	    {
++	      grub_dprintf ("bootp", "Skipped DHCPv6 IA_NA with length %u\n", len);
++	      break;
++	    }
++	  ia_na = (const struct grub_net_dhcp6_option_iana *)opt->data;
++	  dhcp6->iaid = grub_be_to_cpu32 (ia_na->iaid);
++	  dhcp6->t1 = grub_be_to_cpu32 (ia_na->t1);
++	  dhcp6->t2 = grub_be_to_cpu32 (ia_na->t2);
++
++	  data_len = len - sizeof (*ia_na);
++	  if (data_len)
++	    foreach_dhcp6_option ((const struct grub_net_dhcp6_option *)ia_na->data, data_len, parse_dhcp6_iaaddr, dhcp6);
++	}
++	break;
++
++      case GRUB_NET_DHCP6_OPTION_DNS_SERVERS:
++	{
++	  const grub_uint8_t *po;
++	  grub_uint16_t ln;
++	  grub_net_network_level_address_t *la;
++
++	  if (!len || len & 0xf)
++	    {
++	      grub_dprintf ("bootp", "Skip invalid length DHCPv6 DNS_SERVERS \n");
++	      break;
++	    }
++	  dhcp6->num_dns_server = ln = len >> 4;
++	  dhcp6->dns_server_addrs = la = grub_zalloc (ln * sizeof (*la));
++
++	  for (po = opt->data; ln > 0; po += 0x10, la++, ln--)
++	    {
++	      la->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
++	      la->ipv6[0] = grub_get_unaligned64 (po);
++	      la->ipv6[1] = grub_get_unaligned64 (po + 8);
++	      la->option = DNS_OPTION_PREFER_IPV6;
++	    }
++	}
++	break;
++
++      case GRUB_NET_DHCP6_OPTION_BOOTFILE_URL:
++	dissect_url ((const char *)opt->data,
++		      &dhcp6->boot_file_proto,
++		      &dhcp6->boot_file_server_ip,
++		      &dhcp6->boot_file_path);
++	break;
++
++      default:
++	break;
++    }
++}
++
++static void
++foreach_dhcp6_option (const struct grub_net_dhcp6_option *opt, grub_size_t size, dhcp6_option_hook_fn hook, void *hook_data)
++{
++  while (size)
++    {
++      grub_uint16_t code, len;
++
++      if (size < sizeof (*opt))
++	{
++	  grub_dprintf ("bootp", "DHCPv6: Options stopped with remaining size %" PRIxGRUB_SIZE "\n", size);
++	  break;
++	}
++      size -= sizeof (*opt);
++      len = grub_be_to_cpu16 (opt->len);
++      code = grub_be_to_cpu16 (opt->code);
++      if (size < len)
++	{
++	  grub_dprintf ("bootp", "DHCPv6: Options stopped at out of bound length %u for option %u\n", len, code);
++	  break;
++	}
++      if (!len)
++	{
++	  grub_dprintf ("bootp", "DHCPv6: Options stopped at zero length option %u\n", code);
++	  break;
++	}
++      else
++	{
++	  if (hook)
++	    hook (opt, hook_data);
++	  size -= len;
++	  opt = (const struct grub_net_dhcp6_option *)((grub_uint8_t *)opt + len + sizeof (*opt));
++	}
++    }
++}
++
++static grub_dhcp6_options_t
++grub_dhcp6_options_get (const struct grub_net_dhcp6_packet *v6h,
++			grub_size_t size)
++{
++  grub_dhcp6_options_t options;
++
++  if (size < sizeof (*v6h))
++    {
++      grub_error (GRUB_ERR_OUT_OF_RANGE, N_("DHCPv6 packet size too small"));
++      return NULL;
++    }
++
++  options = grub_zalloc (sizeof(*options));
++  if (!options)
++    return NULL;
++
++  foreach_dhcp6_option ((const struct grub_net_dhcp6_option *)v6h->dhcp_options,
++		       size - sizeof (*v6h), parse_dhcp6_option, options);
++
++  return options;
++}
++
++static void
++grub_dhcp6_options_free (grub_dhcp6_options_t options)
++{
++  if (options->client_duid)
++    grub_free (options->client_duid);
++  if (options->server_duid)
++    grub_free (options->server_duid);
++  if (options->ia_addr)
++    grub_free (options->ia_addr);
++  if (options->dns_server_addrs)
++    grub_free (options->dns_server_addrs);
++  if (options->boot_file_proto)
++    grub_free (options->boot_file_proto);
++  if (options->boot_file_server_ip)
++    grub_free (options->boot_file_server_ip);
++  if (options->boot_file_path)
++    grub_free (options->boot_file_path);
++
++  grub_free (options);
++}
++
++static grub_dhcp6_session_t grub_dhcp6_sessions;
++#define FOR_DHCP6_SESSIONS_SAFE(var, next) FOR_LIST_ELEMENTS_SAFE (var, next, grub_dhcp6_sessions)
++#define FOR_DHCP6_SESSIONS(var) FOR_LIST_ELEMENTS (var, grub_dhcp6_sessions)
++
++static void
++grub_net_configure_by_dhcp6_info (const char *name,
++	  struct grub_net_card *card,
++	  grub_dhcp6_options_t dhcp6,
++	  int is_def,
++	  int flags,
++	  struct grub_net_network_level_interface **ret_inf)
++{
++  grub_net_network_level_netaddress_t netaddr;
++  struct grub_net_network_level_interface *inf;
++
++  if (dhcp6->ia_addr)
++    {
++      inf = grub_net_add_addr (name, card, dhcp6->ia_addr, &card->default_address, flags);
++
++      netaddr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
++      netaddr.ipv6.base[0] = dhcp6->ia_addr->ipv6[0];
++      netaddr.ipv6.base[1] = 0;
++      netaddr.ipv6.masksize = 64;
++      grub_net_add_route (name, netaddr, inf);
++
++      if (ret_inf)
++	*ret_inf = inf;
++    }
++
++  if (dhcp6->dns_server_addrs)
++    {
++      grub_uint16_t i;
++
++      for (i = 0; i < dhcp6->num_dns_server; ++i)
++	grub_net_add_dns_server (dhcp6->dns_server_addrs + i);
++    }
++
++  if (dhcp6->boot_file_path)
++    grub_env_set_net_property (name, "boot_file", dhcp6->boot_file_path,
++			  grub_strlen (dhcp6->boot_file_path));
++
++  if (is_def && dhcp6->boot_file_server_ip)
++    {
++      grub_net_default_server = grub_strdup (dhcp6->boot_file_server_ip);
++      grub_env_set ("net_default_interface", name);
++      grub_env_export ("net_default_interface");
++    }
++}
++
++static void
++grub_dhcp6_session_add (struct grub_net_network_level_interface *iface,
++			grub_uint32_t iaid)
++{
++  grub_dhcp6_session_t se;
++  struct grub_datetime date;
++  grub_err_t err;
++  grub_int32_t t = 0;
++
++  se = grub_malloc (sizeof (*se));
++
++  err = grub_get_datetime (&date);
++  if (err || !grub_datetime2unixtime (&date, &t))
++    {
++      grub_errno = GRUB_ERR_NONE;
++      t = 0;
++    }
++
++  se->iface = iface;
++  se->iaid = iaid;
++  se->transaction_id = t;
++  se->start_time = grub_get_time_ms ();
++  se->duid.type = grub_cpu_to_be16_compile_time (3) ;
++  se->duid.hw_type = grub_cpu_to_be16_compile_time (1);
++  grub_memcpy (&se->duid.hwaddr, &iface->hwaddress.mac, sizeof (se->duid.hwaddr));
++  se->adv = NULL;
++  se->reply = NULL;
++  grub_list_push (GRUB_AS_LIST_P (&grub_dhcp6_sessions), GRUB_AS_LIST (se));
++}
++
++static void
++grub_dhcp6_session_remove (grub_dhcp6_session_t se)
++{
++  grub_list_remove (GRUB_AS_LIST (se));
++  if (se->adv)
++    grub_dhcp6_options_free (se->adv);
++  if (se->reply)
++    grub_dhcp6_options_free (se->reply);
++  grub_free (se);
++}
++
++static void
++grub_dhcp6_session_remove_all (void)
++{
++  grub_dhcp6_session_t se, next;
++
++  FOR_DHCP6_SESSIONS_SAFE (se, next)
++    {
++      grub_dhcp6_session_remove (se);
++    }
++  grub_dhcp6_sessions = NULL;
++}
++
++static grub_err_t
++grub_dhcp6_session_configure_network (grub_dhcp6_session_t se)
++{
++  char *name;
++
++  name = grub_xasprintf ("%s:dhcp6", se->iface->card->name);
++  if (!name)
++    return grub_errno;
++
++  grub_net_configure_by_dhcp6_info (name, se->iface->card, se->reply, 1, 0, 0);
++  grub_free (name);
++
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_dhcp6_session_send_request (grub_dhcp6_session_t se)
++{
++  struct grub_net_buff *nb;
++  struct grub_net_dhcp6_option *opt;
++  struct grub_net_dhcp6_packet *v6h;
++  struct grub_net_dhcp6_option_iana *ia_na;
++  struct grub_net_dhcp6_option_iaaddr *iaaddr;
++  struct udphdr *udph;
++  grub_net_network_level_address_t multicast;
++  grub_net_link_level_address_t ll_multicast;
++  grub_uint64_t elapsed;
++  struct grub_net_network_level_interface *inf = se->iface;
++  grub_dhcp6_options_t dhcp6 = se->adv;
++  grub_err_t err = GRUB_ERR_NONE;
++
++  multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
++  multicast.ipv6[0] = grub_cpu_to_be64_compile_time (0xff02ULL << 48);
++  multicast.ipv6[1] = grub_cpu_to_be64_compile_time (0x10002ULL);
++
++  err = grub_net_link_layer_resolve (inf, &multicast, &ll_multicast);
++  if (err)
++    return err;
++
++  nb = grub_netbuff_alloc (GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE);
++
++  if (!nb)
++    return grub_errno;
++
++  err = grub_netbuff_reserve (nb, GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE);
++  if (err)
++    {
++      grub_netbuff_free (nb);
++      return err;
++    }
++
++  err = grub_netbuff_push (nb, dhcp6->client_duid_len + sizeof (*opt));
++  if (err)
++    {
++      grub_netbuff_free (nb);
++      return err;
++    }
++  opt = (struct grub_net_dhcp6_option *)nb->data;
++  opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_CLIENTID);
++  opt->len = grub_cpu_to_be16 (dhcp6->client_duid_len);
++  grub_memcpy (opt->data, dhcp6->client_duid , dhcp6->client_duid_len);
++
++  err = grub_netbuff_push (nb, dhcp6->server_duid_len + sizeof (*opt));
++  if (err)
++    {
++      grub_netbuff_free (nb);
++      return err;
++    }
++  opt = (struct grub_net_dhcp6_option *)nb->data;
++  opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_SERVERID);
++  opt->len = grub_cpu_to_be16 (dhcp6->server_duid_len);
++  grub_memcpy (opt->data, dhcp6->server_duid , dhcp6->server_duid_len);
++
++  err = grub_netbuff_push (nb, sizeof (*ia_na) + sizeof (*opt));
++  if (err)
++    {
++      grub_netbuff_free (nb);
++      return err;
++    }
++
++  if (dhcp6->ia_addr)
++    {
++      err = grub_netbuff_push (nb, sizeof(*iaaddr) + sizeof (*opt));
++      if (err)
++	{
++	  grub_netbuff_free (nb);
++	  return err;
++	}
++    }
++  opt = (struct grub_net_dhcp6_option *)nb->data;
++  opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA);
++  opt->len = grub_cpu_to_be16 (sizeof (*ia_na));
++  if (dhcp6->ia_addr)
++    opt->len += grub_cpu_to_be16 (sizeof(*iaaddr) + sizeof (*opt));
++
++  ia_na = (struct grub_net_dhcp6_option_iana *)opt->data;
++  ia_na->iaid = grub_cpu_to_be32 (dhcp6->iaid);
++
++  ia_na->t1 = grub_cpu_to_be32 (dhcp6->t1);
++  ia_na->t2 = grub_cpu_to_be32 (dhcp6->t2);
++
++  if (dhcp6->ia_addr)
++    {
++      opt = (struct grub_net_dhcp6_option *)ia_na->data;
++      opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IAADDR);
++      opt->len = grub_cpu_to_be16 (sizeof (*iaaddr));
++      iaaddr = (struct grub_net_dhcp6_option_iaaddr *)opt->data;
++      grub_set_unaligned64 (iaaddr->addr, dhcp6->ia_addr->ipv6[0]);
++      grub_set_unaligned64 (iaaddr->addr + 8, dhcp6->ia_addr->ipv6[1]);
++
++      iaaddr->preferred_lifetime = grub_cpu_to_be32 (dhcp6->preferred_lifetime);
++      iaaddr->valid_lifetime = grub_cpu_to_be32 (dhcp6->valid_lifetime);
++    }
++
++  err = grub_netbuff_push (nb, sizeof (*opt) + 2 * sizeof (grub_uint16_t));
++  if (err)
++    {
++      grub_netbuff_free (nb);
++      return err;
++    }
++
++  opt = (struct grub_net_dhcp6_option*) nb->data;
++  opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ORO);
++  opt->len = grub_cpu_to_be16_compile_time (2 * sizeof (grub_uint16_t));
++  grub_set_unaligned16 (opt->data, grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_BOOTFILE_URL));
++  grub_set_unaligned16 (opt->data + 2, grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_DNS_SERVERS));
++
++  err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (grub_uint16_t));
++  if (err)
++    {
++      grub_netbuff_free (nb);
++      return err;
++    }
++  opt = (struct grub_net_dhcp6_option*) nb->data;
++  opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ELAPSED_TIME);
++  opt->len = grub_cpu_to_be16_compile_time (sizeof (grub_uint16_t));
++
++  /* the time is expressed in hundredths of a second */
++  elapsed = grub_divmod64 (grub_get_time_ms () - se->start_time, 10, 0);
++
++  if (elapsed > 0xffff)
++    elapsed = 0xffff;
++
++  grub_set_unaligned16 (opt->data,  grub_cpu_to_be16 ((grub_uint16_t)elapsed));
++
++  err = grub_netbuff_push (nb, sizeof (*v6h));
++  if (err)
++    {
++      grub_netbuff_free (nb);
++      return err;
++    }
++
++  v6h = (struct grub_net_dhcp6_packet *) nb->data;
++  v6h->message_type = GRUB_NET_DHCP6_REQUEST;
++  v6h->transaction_id = se->transaction_id;
++
++  err = grub_netbuff_push (nb, sizeof (*udph));
++  if (err)
++    {
++      grub_netbuff_free (nb);
++      return err;
++    }
++
++  udph = (struct udphdr *) nb->data;
++  udph->src = grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT);
++  udph->dst = grub_cpu_to_be16_compile_time (DHCP6_SERVER_PORT);
++  udph->chksum = 0;
++  udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
++
++  udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
++						 &inf->address,
++						 &multicast);
++  err = grub_net_send_ip_packet (inf, &multicast, &ll_multicast, nb,
++				 GRUB_NET_IP_UDP);
++
++  grub_netbuff_free (nb);
++
++  return err;
++}
++
++struct grub_net_network_level_interface *
++grub_net_configure_by_dhcpv6_reply (const char *name,
++	struct grub_net_card *card,
++	grub_net_interface_flags_t flags,
++	const struct grub_net_dhcp6_packet *v6h,
++	grub_size_t size,
++	int is_def,
++	char **device, char **path)
++{
++  struct grub_net_network_level_interface *inf;
++  grub_dhcp6_options_t dhcp6;
++  int mask = -1;
++
++  dhcp6 = grub_dhcp6_options_get (v6h, size);
++  if (!dhcp6)
++    {
++      grub_print_error ();
++      return NULL;
++    }
++
++  grub_net_configure_by_dhcp6_info (name, card, dhcp6, is_def, flags, &inf);
++
++  if (device && dhcp6->boot_file_proto && dhcp6->boot_file_server_ip)
++    {
++      *device = grub_xasprintf ("%s,%s", dhcp6->boot_file_proto, dhcp6->boot_file_server_ip);
++      grub_print_error ();
++    }
++  if (path && dhcp6->boot_file_path)
++    {
++      *path = grub_strdup (dhcp6->boot_file_path);
++      grub_print_error ();
++      if (*path)
++	{
++	  char *slash;
++	  slash = grub_strrchr (*path, '/');
++	  if (slash)
++	    *slash = 0;
++	  else
++	    **path = 0;
++	}
++    }
++
++  grub_dhcp6_options_free (dhcp6);
++
++  if (inf)
++    grub_net_add_ipv6_local (inf, mask);
++
++  return inf;
++}
++
+ /*
+  * This is called directly from net/ip.c:handle_dgram(), because those
+  * BOOTP/DHCP packets are a bit special due to their improper
+@@ -672,6 +1342,77 @@ grub_net_process_dhcp (struct grub_net_buff *nb,
+     }
+ }
+ 
++grub_err_t
++grub_net_process_dhcp6 (struct grub_net_buff *nb,
++                       struct grub_net_card *card __attribute__ ((unused)))
++{
++  const struct grub_net_dhcp6_packet *v6h;
++  grub_dhcp6_session_t se;
++  grub_size_t size;
++  grub_dhcp6_options_t options;
++
++  v6h = (const struct grub_net_dhcp6_packet *) nb->data;
++  size = nb->tail - nb->data;
++
++  options = grub_dhcp6_options_get (v6h, size);
++  if (!options)
++    return grub_errno;
++
++  if (!options->client_duid || !options->server_duid || !options->ia_addr)
++    {
++      grub_dhcp6_options_free (options);
++      return grub_error (GRUB_ERR_BAD_ARGUMENT, "Bad DHCPv6 Packet");
++    }
++
++  FOR_DHCP6_SESSIONS (se)
++    {
++      if (se->transaction_id == v6h->transaction_id &&
++         grub_memcmp (options->client_duid, &se->duid, sizeof (se->duid)) == 0 &&
++         se->iaid == options->iaid)
++       break;
++    }
++
++  if (!se)
++    {
++      grub_dprintf ("bootp", "DHCPv6 session not found\n");
++      grub_dhcp6_options_free (options);
++      return GRUB_ERR_NONE;
++    }
++
++  if (v6h->message_type == GRUB_NET_DHCP6_ADVERTISE)
++    {
++      if (se->adv)
++       {
++         grub_dprintf ("bootp", "Skipped DHCPv6 Advertised .. \n");
++         grub_dhcp6_options_free (options);
++         return GRUB_ERR_NONE;
++       }
++
++      se->adv = options;
++      return grub_dhcp6_session_send_request (se);
++    }
++  else if (v6h->message_type == GRUB_NET_DHCP6_REPLY)
++    {
++      if (!se->adv)
++       {
++         grub_dprintf ("bootp", "Skipped DHCPv6 Reply .. \n");
++         grub_dhcp6_options_free (options);
++         return GRUB_ERR_NONE;
++       }
++
++      se->reply = options;
++      grub_dhcp6_session_configure_network (se);
++      grub_dhcp6_session_remove (se);
++      return GRUB_ERR_NONE;
++    }
++  else
++    {
++      grub_dhcp6_options_free (options);
++    }
++
++  return GRUB_ERR_NONE;
++}
++
+ static grub_err_t
+ grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)),
+ 		  int argc, char **args)
+@@ -897,180 +1638,174 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
+   return err;
+ }
+ 
+-static grub_command_t cmd_getdhcp, cmd_bootp, cmd_dhcp;
+-
+-struct grub_net_network_level_interface *
+-grub_net_configure_by_dhcpv6_ack (const char *name,
+-				  struct grub_net_card *card,
+-				  grub_net_interface_flags_t flags
+-				    __attribute__((__unused__)),
+-				  const grub_net_link_level_address_t *hwaddr,
+-				  const struct grub_net_dhcpv6_packet *packet,
+-				  int is_def, char **device, char **path)
++static grub_err_t
++grub_cmd_bootp6 (struct grub_command *cmd __attribute__ ((unused)),
++                 int argc, char **args)
+ {
+-  struct grub_net_network_level_interface *inter = NULL;
+-  struct grub_net_network_level_address addr;
+-  int mask = -1;
++  struct grub_net_card *card;
++  grub_uint32_t iaid = 0;
++  int interval;
++  grub_err_t err;
++  grub_dhcp6_session_t se;
+ 
+-  if (!device || !path)
+-    return NULL;
++  err = GRUB_ERR_NONE;
+ 
+-  *device = 0;
+-  *path = 0;
++  FOR_NET_CARDS (card)
++  {
++    struct grub_net_network_level_interface *iface;
+ 
+-  grub_dprintf ("net", "mac address is %02x:%02x:%02x:%02x:%02x:%02x\n",
+-		hwaddr->mac[0], hwaddr->mac[1], hwaddr->mac[2],
+-		hwaddr->mac[3], hwaddr->mac[4], hwaddr->mac[5]);
++    if (argc > 0 && grub_strcmp (card->name, args[0]) != 0)
++      continue;
+ 
+-  if (is_def)
+-    grub_net_default_server = 0;
++    iface = grub_net_ipv6_get_link_local (card, &card->default_address);
++    if (!iface)
++      {
++       grub_dhcp6_session_remove_all ();
++       return grub_errno;
++      }
+ 
+-  if (is_def && !grub_net_default_server && packet)
++    grub_dhcp6_session_add (iface, iaid++);
++  }
++
++  for (interval = 200; interval < 10000; interval *= 2)
+     {
+-      const grub_uint8_t *options = packet->dhcp_options;
+-      unsigned int option_max = 1024 - OFFSET_OF (dhcp_options, packet);
+-      unsigned int i;
+-
+-      for (i = 0; i < option_max - sizeof (grub_net_dhcpv6_option_t); )
+-	{
+-	  grub_uint16_t num, len;
+-	  grub_net_dhcpv6_option_t *opt =
+-	    (grub_net_dhcpv6_option_t *)(options + i);
+-
+-	  num = grub_be_to_cpu16(opt->option_num);
+-	  len = grub_be_to_cpu16(opt->option_len);
+-
+-	  grub_dprintf ("net", "got dhcpv6 option %d len %d\n", num, len);
+-
+-	  if (len == 0)
+-	    break;
+-
+-	  if (len + i > 1024)
+-	    break;
+-
+-	  if (num == GRUB_NET_DHCP6_BOOTFILE_URL)
+-	    {
+-	      char *scheme, *userinfo, *host, *file;
+-	      char *tmp;
+-	      int hostlen;
+-	      int port;
+-	      int rc = extract_url_info ((const char *)opt->option_data,
+-					 (grub_size_t)len,
+-					 &scheme, &userinfo, &host, &port,
+-					 &file);
+-	      if (rc < 0)
+-		continue;
+-
+-	      /* right now this only handles tftp. */
+-	      if (grub_strcmp("tftp", scheme))
+-		{
+-		  grub_free (scheme);
+-		  grub_free (userinfo);
+-		  grub_free (host);
+-		  grub_free (file);
+-		  continue;
+-		}
+-	      grub_free (userinfo);
+-
+-	      hostlen = grub_strlen (host);
+-	      if (hostlen > 2 && host[0] == '[' && host[hostlen-1] == ']')
+-		{
+-		  tmp = host+1;
+-		  host[hostlen-1] = '\0';
+-		}
+-	      else
+-		tmp = host;
+-
+-	      *device = grub_xasprintf ("%s,%s", scheme, tmp);
+-	      grub_free (scheme);
+-	      grub_free (host);
+-
+-	      if (file && *file)
+-		{
+-		  tmp = grub_strrchr (file, '/');
+-		  if (tmp)
+-		    *(tmp+1) = '\0';
+-		  else
+-		    file[0] = '\0';
+-		}
+-	      else if (!file)
+-		file = grub_strdup ("");
+-
+-	      if (file[0] == '/')
+-		{
+-		  *path = grub_strdup (file+1);
+-		  grub_free (file);
+-		}
+-	      else
+-		*path = file;
+-	    }
+-	  else if (num == GRUB_NET_DHCP6_IA_NA)
+-	    {
+-	      const grub_net_dhcpv6_option_t *ia_na_opt;
+-	      const grub_net_dhcpv6_opt_ia_na_t *ia_na =
+-		(const grub_net_dhcpv6_opt_ia_na_t *)opt;
+-	      unsigned int left = len - OFFSET_OF (options, ia_na);
+-	      unsigned int j;
+-
+-	      if ((grub_uint8_t *)ia_na + left >
+-		  (grub_uint8_t *)options + option_max)
+-		left -= ((grub_uint8_t *)ia_na + left)
+-		        - ((grub_uint8_t *)options + option_max);
+-
+-	      if (len < OFFSET_OF (option_data, opt)
+-			+ sizeof (grub_net_dhcpv6_option_t))
+-		{
+-		  grub_dprintf ("net",
+-				"found dhcpv6 ia_na option with no address\n");
+-		  continue;
+-		}
+-
+-	      for (j = 0; left > sizeof (grub_net_dhcpv6_option_t); )
+-		{
+-		  ia_na_opt = (const grub_net_dhcpv6_option_t *)
+-			       (ia_na->options + j);
+-		  grub_uint16_t ia_na_opt_num, ia_na_opt_len;
+-
+-		  ia_na_opt_num = grub_be_to_cpu16 (ia_na_opt->option_num);
+-		  ia_na_opt_len = grub_be_to_cpu16 (ia_na_opt->option_len);
+-		  if (ia_na_opt_len == 0)
+-		    break;
+-		  if (j + ia_na_opt_len > left)
+-		    break;
+-		  if (ia_na_opt_num == GRUB_NET_DHCP6_IA_ADDRESS)
+-		    {
+-		      const grub_net_dhcpv6_opt_ia_address_t *ia_addr;
+-
+-		      ia_addr = (const grub_net_dhcpv6_opt_ia_address_t *)
+-				 ia_na_opt;
+-		      addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
+-		      grub_memcpy(addr.ipv6, ia_addr->ipv6_address,
+-				  sizeof (ia_addr->ipv6_address));
+-		      inter = grub_net_add_addr (name, card, &addr, hwaddr, 0);
+-		    }
+-
+-		  j += ia_na_opt_len;
+-		  left -= ia_na_opt_len;
+-		}
+-	    }
+-
+-	  i += len + 4;
+-	}
+-
+-      grub_print_error ();
++      int done = 1;
++
++      FOR_DHCP6_SESSIONS (se)
++       {
++         struct grub_net_buff *nb;
++         struct grub_net_dhcp6_option *opt;
++         struct grub_net_dhcp6_packet *v6h;
++         struct grub_net_dhcp6_option_duid_ll *duid;
++         struct grub_net_dhcp6_option_iana *ia_na;
++         grub_net_network_level_address_t multicast;
++         grub_net_link_level_address_t ll_multicast;
++         struct udphdr *udph;
++
++         multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
++         multicast.ipv6[0] = grub_cpu_to_be64_compile_time (0xff02ULL << 48);
++         multicast.ipv6[1] = grub_cpu_to_be64_compile_time (0x10002ULL);
++
++         err = grub_net_link_layer_resolve (se->iface,
++                   &multicast, &ll_multicast);
++         if (err)
++           {
++             grub_dhcp6_session_remove_all ();
++             return err;
++           }
++
++         nb = grub_netbuff_alloc (GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE);
++
++         if (!nb)
++           {
++             grub_dhcp6_session_remove_all ();
++             return grub_errno;
++           }
++
++         err = grub_netbuff_reserve (nb, GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE);
++         if (err)
++           {
++             grub_dhcp6_session_remove_all ();
++             grub_netbuff_free (nb);
++             return err;
++           }
++
++         err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (grub_uint16_t));
++         if (err)
++           {
++             grub_dhcp6_session_remove_all ();
++             grub_netbuff_free (nb);
++             return err;
++           }
++
++         opt = (struct grub_net_dhcp6_option *)nb->data;
++         opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ELAPSED_TIME);
++         opt->len = grub_cpu_to_be16_compile_time (sizeof (grub_uint16_t));
++         grub_set_unaligned16 (opt->data, 0);
++
++         err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (*duid));
++         if (err)
++           {
++             grub_dhcp6_session_remove_all ();
++             grub_netbuff_free (nb);
++             return err;
++           }
++
++         opt = (struct grub_net_dhcp6_option *)nb->data;
++         opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_CLIENTID);
++         opt->len = grub_cpu_to_be16 (sizeof (*duid));
++
++         duid = (struct grub_net_dhcp6_option_duid_ll *) opt->data;
++         grub_memcpy (duid, &se->duid, sizeof (*duid));
++
++         err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (*ia_na));
++         if (err)
++           {
++             grub_dhcp6_session_remove_all ();
++             grub_netbuff_free (nb);
++             return err;
++           }
++
++         opt = (struct grub_net_dhcp6_option *)nb->data;
++         opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA);
++         opt->len = grub_cpu_to_be16 (sizeof (*ia_na));
++         ia_na = (struct grub_net_dhcp6_option_iana *)opt->data;
++         ia_na->iaid = grub_cpu_to_be32 (se->iaid);
++         ia_na->t1 = 0;
++         ia_na->t2 = 0;
++
++         err = grub_netbuff_push (nb, sizeof (*v6h));
++         if (err)
++           {
++             grub_dhcp6_session_remove_all ();
++             grub_netbuff_free (nb);
++             return err;
++           }
++
++         v6h = (struct grub_net_dhcp6_packet *)nb->data;
++         v6h->message_type = GRUB_NET_DHCP6_SOLICIT;
++         v6h->transaction_id = se->transaction_id;
++
++         grub_netbuff_push (nb, sizeof (*udph));
++
++         udph = (struct udphdr *) nb->data;
++         udph->src = grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT);
++         udph->dst = grub_cpu_to_be16_compile_time (DHCP6_SERVER_PORT);
++         udph->chksum = 0;
++         udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
++
++         udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
++                           &se->iface->address, &multicast);
++
++         err = grub_net_send_ip_packet (se->iface, &multicast,
++                   &ll_multicast, nb, GRUB_NET_IP_UDP);
++         done = 0;
++         grub_netbuff_free (nb);
++
++         if (err)
++           {
++             grub_dhcp6_session_remove_all ();
++             return err;
++           }
++       }
++      if (!done)
++       grub_net_poll_cards (interval, 0);
+     }
+ 
+-  if (is_def)
++  FOR_DHCP6_SESSIONS (se)
+     {
+-      grub_env_set ("net_default_interface", name);
+-      grub_env_export ("net_default_interface");
++      grub_error_push ();
++      err = grub_error (GRUB_ERR_FILE_NOT_FOUND,
++                       N_("couldn't autoconfigure %s"),
++                       se->iface->card->name);
+     }
+ 
+-    if (inter)
+-      grub_net_add_ipv6_local (inter, mask);
+-    return inter;
++  grub_dhcp6_session_remove_all ();
++
++  return err;
+ }
+ 
++static grub_command_t cmd_getdhcp, cmd_bootp, cmd_dhcp, cmd_bootp6;
+ 
+ void
+ grub_bootp_init (void)
+@@ -1084,11 +1819,15 @@ grub_bootp_init (void)
+   cmd_getdhcp = grub_register_command ("net_get_dhcp_option", grub_cmd_dhcpopt,
+ 				       N_("VAR INTERFACE NUMBER DESCRIPTION"),
+ 				       N_("retrieve DHCP option and save it into VAR. If VAR is - then print the value."));
++  cmd_bootp6 = grub_register_command ("net_bootp6", grub_cmd_bootp6,
++				     N_("[CARD]"),
++				     N_("perform a DHCPv6 autoconfiguration"));
+ }
+ 
+ void
+ grub_bootp_fini (void)
+ {
++  grub_unregister_command (cmd_bootp6);
+   grub_unregister_command (cmd_getdhcp);
+   grub_unregister_command (cmd_bootp);
+   grub_unregister_command (cmd_dhcp);
+diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
+index a673bea807a..8e25680db0c 100644
+--- a/grub-core/net/drivers/efi/efinet.c
++++ b/grub-core/net/drivers/efi/efinet.c
+@@ -393,9 +393,6 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+     pxe_mode = pxe->mode;
+     if (pxe_mode->using_ipv6)
+       {
+-	grub_net_link_level_address_t hwaddr;
+-	struct grub_net_network_level_interface *intf;
+-
+ 	grub_dprintf ("efinet", "using ipv6 and dhcpv6\n");
+ 	grub_dprintf ("efinet", "dhcp_ack_received: %s%s\n",
+ 		      pxe_mode->dhcp_ack_received ? "yes" : "no",
+@@ -403,15 +400,14 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+ 	if (!pxe_mode->dhcp_ack_received)
+ 	  continue;
+ 
+-	hwaddr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
+-	grub_memcpy (hwaddr.mac,
+-		     card->efi_net->mode->current_address,
+-		     sizeof (hwaddr.mac));
+-
+-	intf = grub_net_configure_by_dhcpv6_ack (card->name, card, 0, &hwaddr,
+-	      (const struct grub_net_dhcpv6_packet *)&pxe_mode->dhcp_ack.dhcpv6,
+-	      1, device, path);
+-	if (intf && device && path)
++	grub_net_configure_by_dhcpv6_reply (card->name, card, 0,
++					    (struct grub_net_dhcp6_packet *)
++					    &pxe_mode->dhcp_ack,
++					    sizeof (pxe_mode->dhcp_ack),
++					    1, device, path);
++	if (grub_errno)
++	  grub_print_error ();
++	if (device && path)
+ 	  grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path);
+       }
+     else
+diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c
+index a5896f6dc26..ce6bdc75c6d 100644
+--- a/grub-core/net/ip.c
++++ b/grub-core/net/ip.c
+@@ -239,6 +239,45 @@ handle_dgram (struct grub_net_buff *nb,
+   {
+     struct udphdr *udph;
+     udph = (struct udphdr *) nb->data;
++
++    if (proto == GRUB_NET_IP_UDP && udph->dst == grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT))
++      {
++	if (udph->chksum)
++	  {
++	    grub_uint16_t chk, expected;
++	    chk = udph->chksum;
++	    udph->chksum = 0;
++	    expected = grub_net_ip_transport_checksum (nb,
++						       GRUB_NET_IP_UDP,
++						       source,
++						       dest);
++	    if (expected != chk)
++	      {
++		grub_dprintf ("net", "Invalid UDP checksum. "
++			      "Expected %x, got %x\n",
++			      grub_be_to_cpu16 (expected),
++			      grub_be_to_cpu16 (chk));
++		grub_netbuff_free (nb);
++		return GRUB_ERR_NONE;
++	      }
++	    udph->chksum = chk;
++	  }
++
++	err = grub_netbuff_pull (nb, sizeof (*udph));
++	if (err)
++	  {
++	    grub_netbuff_free (nb);
++	    return err;
++	  }
++
++	err = grub_net_process_dhcp6 (nb, card);
++	if (err)
++	  grub_print_error ();
++
++	grub_netbuff_free (nb);
++	return GRUB_ERR_NONE;
++      }
++
+     if (proto == GRUB_NET_IP_UDP && grub_be_to_cpu16 (udph->dst) == 68)
+       {
+ 	const struct grub_net_bootp_packet *bootp;
+diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
+index 99628801478..7614b58dca8 100644
+--- a/include/grub/efi/api.h
++++ b/include/grub/efi/api.h
+@@ -1532,7 +1532,7 @@ typedef struct grub_efi_pxe_ip_filter
+ {
+   grub_efi_uint8_t filters;
+   grub_efi_uint8_t ip_count;
+-  grub_efi_uint8_t reserved;
++  grub_efi_uint16_t reserved;
+   grub_efi_ip_address_t ip_list[GRUB_EFI_PXE_MAX_IPCNT];
+ } grub_efi_pxe_ip_filter_t;
+ 
+diff --git a/include/grub/net.h b/include/grub/net.h
+index d55d505a03a..543251f7273 100644
+--- a/include/grub/net.h
++++ b/include/grub/net.h
+@@ -451,50 +451,65 @@ struct grub_net_bootp_packet
+   grub_uint8_t vendor[0];
+ } GRUB_PACKED;
+ 
+-enum
+-  {
+-    GRUB_NET_DHCP6_IA_NA = 3,
+-    GRUB_NET_DHCP6_IA_ADDRESS = 5,
+-    GRUB_NET_DHCP6_BOOTFILE_URL = 59,
+-  };
+-
+-struct grub_net_dhcpv6_option
++struct grub_net_dhcp6_packet
+ {
+-  grub_uint16_t option_num;
+-  grub_uint16_t option_len;
+-  grub_uint8_t option_data[];
++  grub_uint32_t message_type:8;
++  grub_uint32_t transaction_id:24;
++  grub_uint8_t dhcp_options[0];
+ } GRUB_PACKED;
+-typedef struct grub_net_dhcpv6_option grub_net_dhcpv6_option_t;
+ 
+-struct grub_net_dhcpv6_opt_ia_na
+-{
+-  grub_uint16_t option_num;
+-  grub_uint16_t option_len;
++struct grub_net_dhcp6_option {
++  grub_uint16_t code;
++  grub_uint16_t len;
++  grub_uint8_t data[0];
++} GRUB_PACKED;
++
++struct grub_net_dhcp6_option_iana {
+   grub_uint32_t iaid;
+   grub_uint32_t t1;
+   grub_uint32_t t2;
+-  grub_uint8_t options[];
++  grub_uint8_t data[0];
+ } GRUB_PACKED;
+-typedef struct grub_net_dhcpv6_opt_ia_na grub_net_dhcpv6_opt_ia_na_t;
+ 
+-struct grub_net_dhcpv6_opt_ia_address
+-{
+-  grub_uint16_t option_num;
+-  grub_uint16_t option_len;
+-  grub_uint64_t ipv6_address[2];
++struct grub_net_dhcp6_option_iaaddr {
++  grub_uint8_t addr[16];
+   grub_uint32_t preferred_lifetime;
+   grub_uint32_t valid_lifetime;
+-  grub_uint8_t options[];
++  grub_uint8_t data[0];
+ } GRUB_PACKED;
+-typedef struct grub_net_dhcpv6_opt_ia_address grub_net_dhcpv6_opt_ia_address_t;
+ 
+-struct grub_net_dhcpv6_packet
++struct grub_net_dhcp6_option_duid_ll
+ {
+-  grub_uint32_t message_type:8;
+-  grub_uint32_t transaction_id:24;
+-  grub_uint8_t dhcp_options[1024];
++  grub_uint16_t type;
++  grub_uint16_t hw_type;
++  grub_uint8_t hwaddr[6];
+ } GRUB_PACKED;
+-typedef struct grub_net_dhcpv6_packet grub_net_dhcpv6_packet_t;
++
++enum
++  {
++    GRUB_NET_DHCP6_SOLICIT = 1,
++    GRUB_NET_DHCP6_ADVERTISE = 2,
++    GRUB_NET_DHCP6_REQUEST = 3,
++    GRUB_NET_DHCP6_REPLY = 7
++  };
++
++enum
++  {
++    DHCP6_CLIENT_PORT = 546,
++    DHCP6_SERVER_PORT = 547
++  };
++
++enum
++  {
++    GRUB_NET_DHCP6_OPTION_CLIENTID = 1,
++    GRUB_NET_DHCP6_OPTION_SERVERID = 2,
++    GRUB_NET_DHCP6_OPTION_IA_NA = 3,
++    GRUB_NET_DHCP6_OPTION_IAADDR = 5,
++    GRUB_NET_DHCP6_OPTION_ORO = 6,
++    GRUB_NET_DHCP6_OPTION_ELAPSED_TIME = 8,
++    GRUB_NET_DHCP6_OPTION_DNS_SERVERS = 23,
++    GRUB_NET_DHCP6_OPTION_BOOTFILE_URL = 59
++  };
+ 
+ #define	GRUB_NET_BOOTP_RFC1048_MAGIC_0	0x63
+ #define	GRUB_NET_BOOTP_RFC1048_MAGIC_1	0x82
+@@ -532,12 +547,12 @@ grub_net_configure_by_dhcp_ack (const char *name,
+ 				int is_def, char **device, char **path);
+ 
+ struct grub_net_network_level_interface *
+-grub_net_configure_by_dhcpv6_ack (const char *name,
+-				 struct grub_net_card *card,
+-				 grub_net_interface_flags_t flags,
+-				 const grub_net_link_level_address_t *hwaddr,
+-				 const struct grub_net_dhcpv6_packet *packet,
+-				 int is_def, char **device, char **path);
++grub_net_configure_by_dhcpv6_reply (const char *name,
++				    struct grub_net_card *card,
++				    grub_net_interface_flags_t flags,
++				    const struct grub_net_dhcp6_packet *v6,
++				    grub_size_t size,
++				    int is_def, char **device, char **path);
+ 
+ int
+ grub_ipv6_get_masksize(grub_uint16_t *mask);
+@@ -554,6 +569,10 @@ void
+ grub_net_process_dhcp (struct grub_net_buff *nb,
+ 		       struct grub_net_network_level_interface *iface);
+ 
++grub_err_t
++grub_net_process_dhcp6 (struct grub_net_buff *nb,
++			struct grub_net_card *card);
++
+ int
+ grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a,
+ 		     const grub_net_link_level_address_t *b);
diff --git a/SOURCES/0090-efinet-UEFI-IPv6-PXE-support.patch b/SOURCES/0090-efinet-UEFI-IPv6-PXE-support.patch
new file mode 100644
index 0000000..d8b22b8
--- /dev/null
+++ b/SOURCES/0090-efinet-UEFI-IPv6-PXE-support.patch
@@ -0,0 +1,126 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Chang <mchang@suse.com>
+Date: Wed, 15 Apr 2015 14:48:30 +0800
+Subject: [PATCH] efinet: UEFI IPv6 PXE support
+
+When grub2 image is booted from UEFI IPv6 PXE, the DHCPv6 Reply packet is
+cached in firmware buffer which can be obtained by PXE Base Code protocol. The
+network interface can be setup through the parameters in that obtained packet.
+
+Signed-off-by: Michael Chang <mchang@suse.com>
+Signed-off-by: Ken Lin <ken.lin@hpe.com>
+---
+ grub-core/net/drivers/efi/efinet.c |  2 ++
+ include/grub/efi/api.h             | 71 +++++++++++++++++++++++---------------
+ 2 files changed, 46 insertions(+), 27 deletions(-)
+
+diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
+index 8e25680db0c..014e5bf9802 100644
+--- a/grub-core/net/drivers/efi/efinet.c
++++ b/grub-core/net/drivers/efi/efinet.c
+@@ -409,6 +409,8 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+ 	  grub_print_error ();
+ 	if (device && path)
+ 	  grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path);
++	if (grub_errno)
++	  grub_print_error ();
+       }
+     else
+       {
+diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
+index 7614b58dca8..91ab528e4d0 100644
+--- a/include/grub/efi/api.h
++++ b/include/grub/efi/api.h
+@@ -1524,31 +1524,6 @@ typedef union
+   grub_efi_pxe_dhcpv6_packet_t dhcpv6;
+ } grub_efi_pxe_packet_t;
+ 
+-#define GRUB_EFI_PXE_MAX_IPCNT 8
+-#define GRUB_EFI_PXE_MAX_ARP_ENTRIES 8
+-#define GRUB_EFI_PXE_MAX_ROUTE_ENTRIES 8
+-
+-typedef struct grub_efi_pxe_ip_filter
+-{
+-  grub_efi_uint8_t filters;
+-  grub_efi_uint8_t ip_count;
+-  grub_efi_uint16_t reserved;
+-  grub_efi_ip_address_t ip_list[GRUB_EFI_PXE_MAX_IPCNT];
+-} grub_efi_pxe_ip_filter_t;
+-
+-typedef struct grub_efi_pxe_arp_entry
+-{
+-  grub_efi_ip_address_t ip_addr;
+-  grub_efi_mac_address_t mac_addr;
+-} grub_efi_pxe_arp_entry_t;
+-
+-typedef struct grub_efi_pxe_route_entry
+-{
+-  grub_efi_ip_address_t ip_addr;
+-  grub_efi_ip_address_t subnet_mask;
+-  grub_efi_ip_address_t gateway_addr;
+-} grub_efi_pxe_route_entry_t;
+-
+ typedef struct grub_efi_pxe_icmp_error
+ {
+   grub_efi_uint8_t type;
+@@ -1574,6 +1549,48 @@ typedef struct grub_efi_pxe_tftp_error
+   grub_efi_char8_t error_string[127];
+ } grub_efi_pxe_tftp_error_t;
+ 
++typedef struct {
++  grub_uint8_t addr[4];
++} grub_efi_pxe_ipv4_address_t;
++
++typedef struct {
++  grub_uint8_t addr[16];
++} grub_efi_pxe_ipv6_address_t;
++
++typedef struct {
++  grub_uint8_t addr[32];
++} grub_efi_pxe_mac_address_t;
++
++typedef union {
++  grub_uint32_t addr[4];
++  grub_efi_pxe_ipv4_address_t v4;
++  grub_efi_pxe_ipv6_address_t v6;
++} grub_efi_pxe_ip_address_t;
++
++#define GRUB_EFI_PXE_BASE_CODE_MAX_IPCNT 8
++typedef struct grub_efi_pxe_ip_filter
++{
++  grub_efi_uint8_t filters;
++  grub_efi_uint8_t ip_count;
++  grub_efi_uint16_t reserved;
++  grub_efi_ip_address_t ip_list[GRUB_EFI_PXE_BASE_CODE_MAX_IPCNT];
++} grub_efi_pxe_ip_filter_t;
++
++typedef struct {
++  grub_efi_pxe_ip_address_t ip_addr;
++  grub_efi_pxe_mac_address_t mac_addr;
++} grub_efi_pxe_arp_entry_t;
++
++typedef struct {
++  grub_efi_pxe_ip_address_t ip_addr;
++  grub_efi_pxe_ip_address_t subnet_mask;
++  grub_efi_pxe_ip_address_t gw_addr;
++} grub_efi_pxe_route_entry_t;
++
++
++#define GRUB_EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES 8
++#define GRUB_EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES 8
++
+ typedef struct grub_efi_pxe_mode
+ {
+   grub_efi_boolean_t started;
+@@ -1605,9 +1622,9 @@ typedef struct grub_efi_pxe_mode
+   grub_efi_pxe_packet_t pxe_bis_reply;
+   grub_efi_pxe_ip_filter_t ip_filter;
+   grub_efi_uint32_t arp_cache_entries;
+-  grub_efi_pxe_arp_entry_t arp_cache[GRUB_EFI_PXE_MAX_ARP_ENTRIES];
++  grub_efi_pxe_arp_entry_t arp_cache[GRUB_EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES];
+   grub_efi_uint32_t route_table_entries;
+-  grub_efi_pxe_route_entry_t route_table[GRUB_EFI_PXE_MAX_ROUTE_ENTRIES];
++  grub_efi_pxe_route_entry_t route_table[GRUB_EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES];
+   grub_efi_pxe_icmp_error_t icmp_error;
+   grub_efi_pxe_tftp_error_t tftp_error;
+ } grub_efi_pxe_mode_t;
diff --git a/SOURCES/0091-grub.texi-Add-net_bootp6-doument.patch b/SOURCES/0091-grub.texi-Add-net_bootp6-doument.patch
new file mode 100644
index 0000000..3f8ec97
--- /dev/null
+++ b/SOURCES/0091-grub.texi-Add-net_bootp6-doument.patch
@@ -0,0 +1,48 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Chang <mchang@suse.com>
+Date: Tue, 5 May 2015 14:19:24 +0800
+Subject: [PATCH] grub.texi: Add net_bootp6 doument
+
+Update grub documentation for net_bootp6 command.
+
+Signed-off-by: Michael Chang <mchang@suse.com>
+Signed-off-by: Ken Lin <ken.lin@hpe.com>
+---
+ docs/grub.texi | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+diff --git a/docs/grub.texi b/docs/grub.texi
+index 0615d0ed97e..04ed6ac1f07 100644
+--- a/docs/grub.texi
++++ b/docs/grub.texi
+@@ -5487,6 +5487,7 @@ This command is only available on AArch64 systems.
+ * net_add_dns::                 Add a DNS server
+ * net_add_route::               Add routing entry
+ * net_bootp::                   Perform a bootp/DHCP autoconfiguration
++* net_bootp6::                  Perform a DHCPv6 autoconfiguration
+ * net_del_addr::                Remove IP address from interface
+ * net_del_dns::                 Remove a DNS server
+ * net_del_route::               Remove a route entry
+@@ -5611,6 +5612,22 @@ Sets environment variable @samp{net_}@var{<card>}@samp{_boot_file}
+ 
+ @end deffn
+ 
++@node net_bootp6
++@subsection net_bootp6
++
++@deffn Command net_bootp6 [@var{card}]
++Perform configuration of @var{card} using DHCPv6 protocol. If no card name is
++specified, try to configure all existing cards. If configuration was
++successful, interface with name @var{card}@samp{:dhcp6} and configured address
++is added to @var{card}.
++
++@table @samp
++@item 1 (Domain Name Server)
++Adds all servers from option value to the list of servers used during name
++resolution.
++@end table
++
++@end deffn
+ 
+ @node net_get_dhcp_option
+ @subsection net_get_dhcp_option
diff --git a/SOURCES/0092-bootp-Add-processing-DHCPACK-packet-from-HTTP-Boot.patch b/SOURCES/0092-bootp-Add-processing-DHCPACK-packet-from-HTTP-Boot.patch
new file mode 100644
index 0000000..e800dd2
--- /dev/null
+++ b/SOURCES/0092-bootp-Add-processing-DHCPACK-packet-from-HTTP-Boot.patch
@@ -0,0 +1,108 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Chang <mchang@suse.com>
+Date: Wed, 10 Jul 2019 23:58:28 +0200
+Subject: [PATCH] bootp: Add processing DHCPACK packet from HTTP Boot
+
+The vendor class identifier with the string "HTTPClient" is used to denote the
+packet as responding to HTTP boot request. In DHCP4 config, the filename for
+HTTP boot is the URL of the boot file while for PXE boot it is the path to the
+boot file. As a consequence, the next-server becomes obseleted because the HTTP
+URL already contains the server address for the boot file. For DHCP6 config,
+there's no difference definition in existing config as dhcp6.bootfile-url can
+be used to specify URL for both HTTP and PXE boot file.
+
+This patch adds processing for "HTTPClient" vendor class identifier in DHCPACK
+packet by treating it as HTTP format, not as the PXE format.
+
+Signed-off-by: Michael Chang <mchang@suse.com>
+Signed-off-by: Ken Lin <ken.lin@hpe.com>
+---
+ grub-core/net/bootp.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ include/grub/net.h    |  1 +
+ 2 files changed, 56 insertions(+)
+
+diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c
+index fe93b80f1cf..8fb8918ae7e 100644
+--- a/grub-core/net/bootp.c
++++ b/grub-core/net/bootp.c
+@@ -20,6 +20,7 @@
+ #include <grub/env.h>
+ #include <grub/i18n.h>
+ #include <grub/command.h>
++#include <grub/net.h>
+ #include <grub/net/ip.h>
+ #include <grub/net/netbuff.h>
+ #include <grub/net/udp.h>
+@@ -500,6 +501,60 @@ grub_net_configure_by_dhcp_ack (const char *name,
+   if (opt && opt_len)
+     grub_env_set_net_property (name, "rootpath", (const char *) opt, opt_len);
+ 
++  opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER, &opt_len);
++  if (opt && opt_len)
++    {
++      grub_env_set_net_property (name, "vendor_class_identifier", (const char *) opt, opt_len);
++      if (opt && grub_strcmp (opt, "HTTPClient") == 0)
++        {
++          char *proto, *ip, *pa;
++
++          if (!dissect_url (bp->boot_file, &proto, &ip, &pa))
++            return inter;
++
++          grub_env_set_net_property (name, "boot_file", pa, grub_strlen (pa));
++          if (is_def)
++            {
++              grub_net_default_server = grub_strdup (ip);
++              grub_env_set ("net_default_interface", name);
++             grub_env_export ("net_default_interface");
++            }
++          if (device && !*device)
++            {
++              *device = grub_xasprintf ("%s,%s", proto, ip);
++              grub_print_error ();
++            }
++          if (path)
++            {
++              *path = grub_strdup (pa);
++              grub_print_error ();
++              if (*path)
++                {
++                  char *slash;
++                  slash = grub_strrchr (*path, '/');
++                  if (slash)
++                    *slash = 0;
++                  else
++                    **path = 0;
++                }
++            }
++          grub_net_add_ipv4_local (inter, mask);
++          inter->dhcp_ack = grub_malloc (size);
++          if (inter->dhcp_ack)
++            {
++              grub_memcpy (inter->dhcp_ack, bp, size);
++              inter->dhcp_acklen = size;
++            }
++          else
++            grub_errno = GRUB_ERR_NONE;
++
++          grub_free (proto);
++          grub_free (ip);
++          grub_free (pa);
++          return inter;
++        }
++    }
++
+   opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_EXTENSIONS_PATH, &opt_len);
+   if (opt && opt_len)
+     grub_env_set_net_property (name, "extensionspath", (const char *) opt, opt_len);
+diff --git a/include/grub/net.h b/include/grub/net.h
+index 543251f7273..42af7de250a 100644
+--- a/include/grub/net.h
++++ b/include/grub/net.h
+@@ -531,6 +531,7 @@ enum
+     GRUB_NET_DHCP_MESSAGE_TYPE = 53,
+     GRUB_NET_DHCP_SERVER_IDENTIFIER = 54,
+     GRUB_NET_DHCP_PARAMETER_REQUEST_LIST = 55,
++    GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER = 60,
+     GRUB_NET_BOOTP_CLIENT_ID = 61,
+     GRUB_NET_DHCP_TFTP_SERVER_NAME = 66,
+     GRUB_NET_DHCP_BOOTFILE_NAME = 67,
diff --git a/SOURCES/0093-efinet-Setting-network-from-UEFI-device-path.patch b/SOURCES/0093-efinet-Setting-network-from-UEFI-device-path.patch
new file mode 100644
index 0000000..11b2a72
--- /dev/null
+++ b/SOURCES/0093-efinet-Setting-network-from-UEFI-device-path.patch
@@ -0,0 +1,405 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Chang <mchang@suse.com>
+Date: Sun, 10 Jul 2016 23:46:31 +0800
+Subject: [PATCH] efinet: Setting network from UEFI device path
+
+The PXE Base Code protocol used to obtain cached PXE DHCPACK packet is no
+longer provided for HTTP Boot. Instead, we have to get the HTTP boot
+information from the device path nodes defined in following UEFI Specification
+sections.
+
+ 9.3.5.12 IPv4 Device Path
+ 9.3.5.13 IPv6 Device Path
+ 9.3.5.23 Uniform Resource Identifiers (URI) Device Path
+
+This patch basically does:
+
+include/grub/efi/api.h:
+Add new structure of Uniform Resource Identifiers (URI) Device Path
+
+grub-core/net/drivers/efi/efinet.c:
+Check if PXE Base Code is available, if not it will try to obtain the netboot
+information from the device path where the image booted from. The DHCPACK
+packet is recoverd from the information in device patch and feed into the same
+DHCP packet processing functions to ensure the network interface is setting up
+the same way it used to be.
+
+Signed-off-by: Michael Chang <mchang@suse.com>
+Signed-off-by: Ken Lin <ken.lin@hpe.com>
+---
+ grub-core/net/drivers/efi/efinet.c | 284 +++++++++++++++++++++++++++++++++++--
+ include/grub/efi/api.h             |  11 ++
+ 2 files changed, 280 insertions(+), 15 deletions(-)
+
+diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
+index 014e5bf9802..8171ecaa5e4 100644
+--- a/grub-core/net/drivers/efi/efinet.c
++++ b/grub-core/net/drivers/efi/efinet.c
+@@ -26,6 +26,7 @@
+ #include <grub/i18n.h>
+ #include <grub/lib/hexdump.h>
+ #include <grub/types.h>
++#include <grub/net/netbuff.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -331,6 +332,227 @@ grub_efinet_findcards (void)
+   grub_free (handles);
+ }
+ 
++static struct grub_net_buff *
++grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *use_ipv6)
++{
++  grub_efi_uint16_t uri_len;
++  grub_efi_device_path_t *ldp, *ddp;
++  grub_efi_uri_device_path_t *uri_dp;
++  struct grub_net_buff *nb;
++  grub_err_t err;
++
++  ddp = grub_efi_duplicate_device_path (dp);
++  if (!ddp)
++    return NULL;
++
++  ldp = grub_efi_find_last_device_path (ddp);
++
++  if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
++      || GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)
++    {
++      grub_free (ddp);
++      return NULL;
++    }
++
++  uri_len = GRUB_EFI_DEVICE_PATH_LENGTH (ldp) > 4 ? GRUB_EFI_DEVICE_PATH_LENGTH (ldp) - 4  : 0;
++
++  if (!uri_len)
++    {
++      grub_free (ddp);
++      return NULL;
++    }
++
++  uri_dp = (grub_efi_uri_device_path_t *) ldp;
++
++  ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
++  ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
++  ldp->length = sizeof (*ldp);
++
++  ldp = grub_efi_find_last_device_path (ddp);
++
++  if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
++      || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE
++          && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE))
++    {
++      grub_free (ddp);
++      return NULL;
++    }
++
++  nb = grub_netbuff_alloc (512);
++  if (!nb)
++    {
++      grub_free (ddp);
++      return NULL;
++    }
++
++  if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE)
++    {
++      grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) ldp;
++      struct grub_net_bootp_packet *bp;
++      grub_uint8_t *ptr;
++
++      bp = (struct grub_net_bootp_packet *) nb->tail;
++      err = grub_netbuff_put (nb, sizeof (*bp) + 4);
++      if (err)
++	{
++	  grub_free (ddp);
++	  grub_netbuff_free (nb);
++	  return NULL;
++	}
++
++      if (sizeof(bp->boot_file) < uri_len)
++	{
++	  grub_free (ddp);
++	  grub_netbuff_free (nb);
++	  return NULL;
++	}
++      grub_memcpy (bp->boot_file, uri_dp->uri, uri_len);
++      grub_memcpy (&bp->your_ip, ipv4->local_ip_address, sizeof (bp->your_ip));
++      grub_memcpy (&bp->server_ip, ipv4->remote_ip_address, sizeof (bp->server_ip));
++
++      bp->vendor[0] = GRUB_NET_BOOTP_RFC1048_MAGIC_0;
++      bp->vendor[1] = GRUB_NET_BOOTP_RFC1048_MAGIC_1;
++      bp->vendor[2] = GRUB_NET_BOOTP_RFC1048_MAGIC_2;
++      bp->vendor[3] = GRUB_NET_BOOTP_RFC1048_MAGIC_3;
++
++      ptr = nb->tail;
++      err = grub_netbuff_put (nb, sizeof (ipv4->subnet_mask) + 2);
++      if (err)
++	{
++	  grub_free (ddp);
++	  grub_netbuff_free (nb);
++	  return NULL;
++	}
++      *ptr++ = GRUB_NET_BOOTP_NETMASK;
++      *ptr++ = sizeof (ipv4->subnet_mask);
++      grub_memcpy (ptr, ipv4->subnet_mask, sizeof (ipv4->subnet_mask));
++
++      ptr = nb->tail;
++      err = grub_netbuff_put (nb, sizeof (ipv4->gateway_ip_address) + 2);
++      if (err)
++	{
++	  grub_free (ddp);
++	  grub_netbuff_free (nb);
++	  return NULL;
++	}
++      *ptr++ = GRUB_NET_BOOTP_ROUTER;
++      *ptr++ = sizeof (ipv4->gateway_ip_address);
++      grub_memcpy (ptr, ipv4->gateway_ip_address, sizeof (ipv4->gateway_ip_address));
++
++      ptr = nb->tail;
++      err = grub_netbuff_put (nb, sizeof ("HTTPClient") + 1);
++      if (err)
++	{
++	  grub_free (ddp);
++	  grub_netbuff_free (nb);
++	  return NULL;
++	}
++      *ptr++ = GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER;
++      *ptr++ = sizeof ("HTTPClient") - 1;
++      grub_memcpy (ptr, "HTTPClient", sizeof ("HTTPClient") - 1);
++
++      ptr = nb->tail;
++      err = grub_netbuff_put (nb, 1);
++      if (err)
++	{
++	  grub_free (ddp);
++	  grub_netbuff_free (nb);
++	  return NULL;
++	}
++      *ptr = GRUB_NET_BOOTP_END;
++      *use_ipv6 = 0;
++
++      ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
++      ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
++      ldp->length = sizeof (*ldp);
++      ldp = grub_efi_find_last_device_path (ddp);
++
++      if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) ==  GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE)
++	{
++	  grub_efi_mac_address_device_path_t *mac = (grub_efi_mac_address_device_path_t *) ldp;
++	  bp->hw_type = mac->if_type;
++	  bp->hw_len = sizeof (bp->mac_addr);
++	  grub_memcpy (bp->mac_addr, mac->mac_address, bp->hw_len);
++	}
++    }
++  else
++    {
++      grub_efi_ipv6_device_path_t *ipv6 = (grub_efi_ipv6_device_path_t *) ldp;
++
++      struct grub_net_dhcp6_packet *d6p;
++      struct grub_net_dhcp6_option *opt;
++      struct grub_net_dhcp6_option_iana *iana;
++      struct grub_net_dhcp6_option_iaaddr *iaaddr;
++
++      d6p = (struct grub_net_dhcp6_packet *)nb->tail;
++      err = grub_netbuff_put (nb, sizeof(*d6p));
++      if (err)
++	{
++	  grub_free (ddp);
++	  grub_netbuff_free (nb);
++	  return NULL;
++	}
++      d6p->message_type = GRUB_NET_DHCP6_REPLY;
++
++      opt = (struct grub_net_dhcp6_option *)nb->tail;
++      err = grub_netbuff_put (nb, sizeof(*opt));
++      if (err)
++	{
++	  grub_free (ddp);
++	  grub_netbuff_free (nb);
++	  return NULL;
++	}
++      opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA);
++      opt->len = grub_cpu_to_be16_compile_time (sizeof(*iana) + sizeof(*opt) + sizeof(*iaaddr));
++
++      err = grub_netbuff_put (nb, sizeof(*iana));
++      if (err)
++	{
++	  grub_free (ddp);
++	  grub_netbuff_free (nb);
++	  return NULL;
++	}
++
++      opt = (struct grub_net_dhcp6_option *)nb->tail;
++      err = grub_netbuff_put (nb, sizeof(*opt));
++      if (err)
++	{
++	  grub_free (ddp);
++	  grub_netbuff_free (nb);
++	  return NULL;
++	}
++      opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IAADDR);
++      opt->len = grub_cpu_to_be16_compile_time (sizeof (*iaaddr));
++
++      iaaddr = (struct grub_net_dhcp6_option_iaaddr *)nb->tail;
++      err = grub_netbuff_put (nb, sizeof(*iaaddr));
++      if (err)
++	{
++	  grub_free (ddp);
++	  grub_netbuff_free (nb);
++	  return NULL;
++	}
++      grub_memcpy (iaaddr->addr, ipv6->local_ip_address, sizeof(ipv6->local_ip_address));
++
++      opt = (struct grub_net_dhcp6_option *)nb->tail;
++      err = grub_netbuff_put (nb, sizeof(*opt) + uri_len);
++      if (err)
++	{
++	  grub_free (ddp);
++	  grub_netbuff_free (nb);
++	  return NULL;
++	}
++      opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_BOOTFILE_URL);
++      opt->len = grub_cpu_to_be16 (uri_len);
++      grub_memcpy (opt->data, uri_dp->uri, uri_len);
++
++      *use_ipv6 = 1;
++    }
++
++  grub_free (ddp);
++  return nb;
++}
++
+ static void
+ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+ 			  char **path)
+@@ -346,7 +568,11 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+   {
+     grub_efi_device_path_t *cdp;
+     struct grub_efi_pxe *pxe;
+-    struct grub_efi_pxe_mode *pxe_mode;
++    struct grub_efi_pxe_mode *pxe_mode = NULL;
++    grub_uint8_t *packet_buf;
++    grub_size_t packet_bufsz ;
++    int ipv6;
++    struct grub_net_buff *nb = NULL;
+ 
+     if (card->driver != &efidriver)
+       continue;
+@@ -370,11 +596,21 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+          */
+ 	if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
+ 	    || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE
+-		&& GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE))
++		&& GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE
++		&& GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE))
+ 	  continue;
+ 	dup_dp = grub_efi_duplicate_device_path (dp);
+ 	if (!dup_dp)
+ 	  continue;
++
++	if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)
++	  {
++	    dup_ldp = grub_efi_find_last_device_path (dup_dp);
++	    dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
++	    dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
++	    dup_ldp->length = sizeof (*dup_ldp);
++	  }
++
+ 	dup_ldp = grub_efi_find_last_device_path (dup_dp);
+ 	dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
+ 	dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
+@@ -387,23 +623,37 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+ 
+     pxe = grub_efi_open_protocol (hnd, &pxe_io_guid,
+ 				  GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+-    if (! pxe)
+-      continue;
++    if (!pxe)
++      {
++	nb = grub_efinet_create_dhcp_ack_from_device_path (dp, &ipv6);
++	if (!nb)
++	  {
++	    grub_print_error ();
++	    continue;
++	  }
++	packet_buf = nb->head;
++	packet_bufsz = nb->tail - nb->head;
++      }
++    else
++      {
++	pxe_mode = pxe->mode;
++	packet_buf = (grub_uint8_t *) &pxe_mode->dhcp_ack;
++	packet_bufsz = sizeof (pxe_mode->dhcp_ack);
++	ipv6 = pxe_mode->using_ipv6;
++      }
+ 
+-    pxe_mode = pxe->mode;
+-    if (pxe_mode->using_ipv6)
++    if (ipv6)
+       {
+ 	grub_dprintf ("efinet", "using ipv6 and dhcpv6\n");
+-	grub_dprintf ("efinet", "dhcp_ack_received: %s%s\n",
+-		      pxe_mode->dhcp_ack_received ? "yes" : "no",
+-		      pxe_mode->dhcp_ack_received ? "" : " cannot continue");
+-	if (!pxe_mode->dhcp_ack_received)
+-	  continue;
++	if (pxe_mode)
++	  grub_dprintf ("efinet", "dhcp_ack_received: %s%s\n",
++			pxe_mode->dhcp_ack_received ? "yes" : "no",
++			pxe_mode->dhcp_ack_received ? "" : " cannot continue");
+ 
+ 	grub_net_configure_by_dhcpv6_reply (card->name, card, 0,
+ 					    (struct grub_net_dhcp6_packet *)
+-					    &pxe_mode->dhcp_ack,
+-					    sizeof (pxe_mode->dhcp_ack),
++					    packet_buf,
++					    packet_bufsz,
+ 					    1, device, path);
+ 	if (grub_errno)
+ 	  grub_print_error ();
+@@ -417,11 +667,15 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+ 	grub_dprintf ("efinet", "using ipv4 and dhcp\n");
+ 	grub_net_configure_by_dhcp_ack (card->name, card, 0,
+ 					(struct grub_net_bootp_packet *)
+-					&pxe_mode->dhcp_ack,
+-					sizeof (pxe_mode->dhcp_ack),
++					packet_buf,
++					packet_bufsz,
+ 					1, device, path);
+ 	grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path);
+       }
++
++    if (nb)
++      grub_netbuff_free (nb);
++
+     return;
+   }
+ }
+diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
+index 91ab528e4d0..4a51667adb1 100644
+--- a/include/grub/efi/api.h
++++ b/include/grub/efi/api.h
+@@ -864,6 +864,8 @@ struct grub_efi_ipv4_device_path
+   grub_efi_uint16_t remote_port;
+   grub_efi_uint16_t protocol;
+   grub_efi_uint8_t static_ip_address;
++  grub_efi_ipv4_address_t gateway_ip_address;
++  grub_efi_ipv4_address_t subnet_mask;
+ } GRUB_PACKED;
+ typedef struct grub_efi_ipv4_device_path grub_efi_ipv4_device_path_t;
+ 
+@@ -918,6 +920,15 @@ struct grub_efi_sata_device_path
+ } GRUB_PACKED;
+ typedef struct grub_efi_sata_device_path grub_efi_sata_device_path_t;
+ 
++#define GRUB_EFI_URI_DEVICE_PATH_SUBTYPE		24
++
++struct grub_efi_uri_device_path
++{
++  grub_efi_device_path_t header;
++  grub_efi_uint8_t uri[0];
++} GRUB_PACKED;
++typedef struct grub_efi_uri_device_path grub_efi_uri_device_path_t;
++
+ #define GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE	10
+ 
+ /* Media Device Path.  */
diff --git a/SOURCES/0094-efinet-Setting-DNS-server-from-UEFI-protocol.patch b/SOURCES/0094-efinet-Setting-DNS-server-from-UEFI-protocol.patch
new file mode 100644
index 0000000..1460c8a
--- /dev/null
+++ b/SOURCES/0094-efinet-Setting-DNS-server-from-UEFI-protocol.patch
@@ -0,0 +1,336 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Chang <mchang@suse.com>
+Date: Thu, 14 Jul 2016 17:48:45 +0800
+Subject: [PATCH] efinet: Setting DNS server from UEFI protocol
+
+In the URI device path node, any name rahter than address can be used for
+looking up the resources so that DNS service become needed to get answer of the
+name's address. Unfortunately the DNS is not defined in any of the device path
+nodes so that we use the EFI_IP4_CONFIG2_PROTOCOL and EFI_IP6_CONFIG_PROTOCOL
+to obtain it.
+
+These two protcols are defined the sections of UEFI specification.
+
+ 27.5 EFI IPv4 Configuration II Protocol
+ 27.7 EFI IPv6 Configuration Protocol
+
+include/grub/efi/api.h:
+Add new structure and protocol UUID of EFI_IP4_CONFIG2_PROTOCOL and
+EFI_IP6_CONFIG_PROTOCOL.
+
+grub-core/net/drivers/efi/efinet.c:
+Use the EFI_IP4_CONFIG2_PROTOCOL and EFI_IP6_CONFIG_PROTOCOL to obtain the list
+of DNS server address for IPv4 and IPv6 respectively. The address of DNS
+servers is structured into DHCPACK packet and feed into the same DHCP packet
+processing functions to ensure the network interface is setting up the same way
+it used to be.
+
+Signed-off-by: Michael Chang <mchang@suse.com>
+Signed-off-by: Ken Lin <ken.lin@hpe.com>
+---
+ grub-core/net/drivers/efi/efinet.c | 163 +++++++++++++++++++++++++++++++++++++
+ include/grub/efi/api.h             |  75 +++++++++++++++++
+ 2 files changed, 238 insertions(+)
+
+diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
+index 8171ecaa5e4..715a6168d77 100644
+--- a/grub-core/net/drivers/efi/efinet.c
++++ b/grub-core/net/drivers/efi/efinet.c
+@@ -33,6 +33,8 @@ GRUB_MOD_LICENSE ("GPLv3+");
+ /* GUID.  */
+ static grub_efi_guid_t net_io_guid = GRUB_EFI_SIMPLE_NETWORK_GUID;
+ static grub_efi_guid_t pxe_io_guid = GRUB_EFI_PXE_GUID;
++static grub_efi_guid_t ip4_config_guid = GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID;
++static grub_efi_guid_t ip6_config_guid = GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID;
+ 
+ static grub_err_t
+ send_card_buffer (struct grub_net_card *dev,
+@@ -332,6 +334,125 @@ grub_efinet_findcards (void)
+   grub_free (handles);
+ }
+ 
++static grub_efi_handle_t
++grub_efi_locate_device_path (grub_efi_guid_t *protocol, grub_efi_device_path_t *device_path,
++			    grub_efi_device_path_t **r_device_path)
++{
++  grub_efi_handle_t handle;
++  grub_efi_status_t status;
++
++  status = efi_call_3 (grub_efi_system_table->boot_services->locate_device_path,
++		      protocol, &device_path, &handle);
++
++  if (status != GRUB_EFI_SUCCESS)
++    return 0;
++
++  if (r_device_path)
++    *r_device_path = device_path;
++
++  return handle;
++}
++
++static grub_efi_ipv4_address_t *
++grub_dns_server_ip4_address (grub_efi_device_path_t *dp, grub_efi_uintn_t *num_dns)
++{
++  grub_efi_handle_t hnd;
++  grub_efi_status_t status;
++  grub_efi_ip4_config2_protocol_t *conf;
++  grub_efi_ipv4_address_t *addrs;
++  grub_efi_uintn_t data_size = 1 * sizeof (grub_efi_ipv4_address_t);
++
++  hnd = grub_efi_locate_device_path (&ip4_config_guid, dp, NULL);
++
++  if (!hnd)
++    return 0;
++
++  conf = grub_efi_open_protocol (hnd, &ip4_config_guid,
++				GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
++
++  if (!conf)
++    return 0;
++
++  addrs  = grub_malloc (data_size);
++  if (!addrs)
++    return 0;
++
++  status = efi_call_4 (conf->get_data, conf,
++		      GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER,
++		      &data_size, addrs);
++
++  if (status == GRUB_EFI_BUFFER_TOO_SMALL)
++    {
++      grub_free (addrs);
++      addrs  = grub_malloc (data_size);
++      if (!addrs)
++	return 0;
++
++      status = efi_call_4 (conf->get_data,  conf,
++			  GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER,
++			  &data_size, addrs);
++    }
++
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      grub_free (addrs);
++      return 0;
++    }
++
++  *num_dns = data_size / sizeof (grub_efi_ipv4_address_t);
++  return addrs;
++}
++
++static grub_efi_ipv6_address_t *
++grub_dns_server_ip6_address (grub_efi_device_path_t *dp, grub_efi_uintn_t *num_dns)
++{
++  grub_efi_handle_t hnd;
++  grub_efi_status_t status;
++  grub_efi_ip6_config_protocol_t *conf;
++  grub_efi_ipv6_address_t *addrs;
++  grub_efi_uintn_t data_size = 1 * sizeof (grub_efi_ipv6_address_t);
++
++  hnd = grub_efi_locate_device_path (&ip6_config_guid, dp, NULL);
++
++  if (!hnd)
++    return 0;
++
++  conf = grub_efi_open_protocol (hnd, &ip6_config_guid,
++				GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
++
++  if (!conf)
++    return 0;
++
++  addrs  = grub_malloc (data_size);
++  if (!addrs)
++    return 0;
++
++  status = efi_call_4 (conf->get_data, conf,
++		      GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER,
++		      &data_size, addrs);
++
++  if (status == GRUB_EFI_BUFFER_TOO_SMALL)
++    {
++      grub_free (addrs);
++      addrs  = grub_malloc (data_size);
++      if (!addrs)
++	return 0;
++
++      status = efi_call_4 (conf->get_data,  conf,
++			  GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER,
++			  &data_size, addrs);
++    }
++
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      grub_free (addrs);
++      return 0;
++    }
++
++  *num_dns = data_size / sizeof (grub_efi_ipv6_address_t);
++  return addrs;
++}
++
+ static struct grub_net_buff *
+ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *use_ipv6)
+ {
+@@ -390,6 +511,8 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u
+       grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) ldp;
+       struct grub_net_bootp_packet *bp;
+       grub_uint8_t *ptr;
++      grub_efi_ipv4_address_t *dns;
++      grub_efi_uintn_t num_dns;
+ 
+       bp = (struct grub_net_bootp_packet *) nb->tail;
+       err = grub_netbuff_put (nb, sizeof (*bp) + 4);
+@@ -451,6 +574,25 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u
+       *ptr++ = sizeof ("HTTPClient") - 1;
+       grub_memcpy (ptr, "HTTPClient", sizeof ("HTTPClient") - 1);
+ 
++      dns = grub_dns_server_ip4_address (dp, &num_dns);
++      if (dns)
++	{
++	  grub_efi_uintn_t size_dns = sizeof (*dns) * num_dns;
++
++	  ptr = nb->tail;
++	  err = grub_netbuff_put (nb, size_dns + 2);
++	  if (err)
++	    {
++	      grub_free (ddp);
++	      grub_netbuff_free (nb);
++	      return NULL;
++	    }
++	  *ptr++ = GRUB_NET_BOOTP_DNS;
++	  *ptr++ = size_dns;
++	  grub_memcpy (ptr, dns, size_dns);
++	  grub_free (dns);
++	}
++
+       ptr = nb->tail;
+       err = grub_netbuff_put (nb, 1);
+       if (err)
+@@ -483,6 +625,8 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u
+       struct grub_net_dhcp6_option *opt;
+       struct grub_net_dhcp6_option_iana *iana;
+       struct grub_net_dhcp6_option_iaaddr *iaaddr;
++      grub_efi_ipv6_address_t *dns;
++      grub_efi_uintn_t num_dns;
+ 
+       d6p = (struct grub_net_dhcp6_packet *)nb->tail;
+       err = grub_netbuff_put (nb, sizeof(*d6p));
+@@ -546,6 +690,25 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u
+       opt->len = grub_cpu_to_be16 (uri_len);
+       grub_memcpy (opt->data, uri_dp->uri, uri_len);
+ 
++      dns = grub_dns_server_ip6_address (dp, &num_dns);
++      if (dns)
++	{
++	  grub_efi_uintn_t size_dns = sizeof (*dns) * num_dns;
++
++	  opt = (struct grub_net_dhcp6_option *)nb->tail;
++	  err = grub_netbuff_put (nb, sizeof(*opt) + size_dns);
++	  if (err)
++	  {
++	    grub_free (ddp);
++	    grub_netbuff_free (nb);
++	    return NULL;
++	  }
++	  opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_DNS_SERVERS);
++	  opt->len = grub_cpu_to_be16 (size_dns);
++	  grub_memcpy (opt->data, dns, size_dns);
++	  grub_free (dns);
++	}
++
+       *use_ipv6 = 1;
+     }
+ 
+diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
+index 4a51667adb1..0b490195ad9 100644
+--- a/include/grub/efi/api.h
++++ b/include/grub/efi/api.h
+@@ -352,6 +352,15 @@
+ #define GRUB_EFI_RNG_PROTOCOL_GUID \
+   { 0x3152bca5, 0xeade, 0x433d, \
+     { 0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44 } \
++
++#define GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID \
++  { 0x5b446ed1, 0xe30b, 0x4faa, \
++      { 0x87, 0x1a, 0x36, 0x54, 0xec, 0xa3, 0x60, 0x80 } \
++  }
++
++#define GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID \
++  { 0x937fe521, 0x95ae, 0x4d1a, \
++      { 0x89, 0x29, 0x48, 0xbc, 0xd9, 0x0a, 0xd3, 0x1a } \
+   }
+ 
+ struct grub_efi_sal_system_table
+@@ -1883,6 +1892,72 @@ struct grub_efi_rng_protocol
+ };
+ typedef struct grub_efi_rng_protocol grub_efi_rng_protocol_t;
+ 
++enum grub_efi_ip4_config2_data_type {
++  GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO,
++  GRUB_EFI_IP4_CONFIG2_DATA_TYPE_POLICY,
++  GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
++  GRUB_EFI_IP4_CONFIG2_DATA_TYPE_GATEWAY,
++  GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER,
++  GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MAXIMUM
++};
++typedef enum grub_efi_ip4_config2_data_type grub_efi_ip4_config2_data_type_t;
++
++struct grub_efi_ip4_config2_protocol
++{
++  grub_efi_status_t (*set_data) (struct grub_efi_ip4_config2_protocol *this,
++				 grub_efi_ip4_config2_data_type_t data_type,
++				 grub_efi_uintn_t data_size,
++				 void *data);
++
++  grub_efi_status_t (*get_data) (struct grub_efi_ip4_config2_protocol *this,
++				 grub_efi_ip4_config2_data_type_t data_type,
++				 grub_efi_uintn_t *data_size,
++				 void *data);
++
++  grub_efi_status_t (*register_data_notify) (struct grub_efi_ip4_config2_protocol *this,
++					     grub_efi_ip4_config2_data_type_t data_type,
++					     grub_efi_event_t event);
++
++  grub_efi_status_t (*unregister_datanotify) (struct grub_efi_ip4_config2_protocol *this,
++					     grub_efi_ip4_config2_data_type_t data_type,
++					     grub_efi_event_t event);
++};
++typedef struct grub_efi_ip4_config2_protocol grub_efi_ip4_config2_protocol_t;
++
++enum grub_efi_ip6_config_data_type {
++  GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO,
++  GRUB_EFI_IP6_CONFIG_DATA_TYPE_ALT_INTERFACEID,
++  GRUB_EFI_IP6_CONFIG_DATA_TYPE_POLICY,
++  GRUB_EFI_IP6_CONFIG_DATA_TYPE_DUP_ADDR_DETECT_TRANSMITS,
++  GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS,
++  GRUB_EFI_IP6_CONFIG_DATA_TYPE_GATEWAY,
++  GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER,
++  GRUB_EFI_IP6_CONFIG_DATA_TYPE_MAXIMUM
++};
++typedef enum grub_efi_ip6_config_data_type grub_efi_ip6_config_data_type_t;
++
++struct grub_efi_ip6_config_protocol
++{
++  grub_efi_status_t (*set_data) (struct grub_efi_ip6_config_protocol *this,
++				 grub_efi_ip6_config_data_type_t data_type,
++				 grub_efi_uintn_t data_size,
++				 void *data);
++
++  grub_efi_status_t (*get_data) (struct grub_efi_ip6_config_protocol *this,
++				 grub_efi_ip6_config_data_type_t data_type,
++				 grub_efi_uintn_t *data_size,
++				 void *data);
++
++  grub_efi_status_t (*register_data_notify) (struct grub_efi_ip6_config_protocol *this,
++					     grub_efi_ip6_config_data_type_t data_type,
++					     grub_efi_event_t event);
++
++  grub_efi_status_t (*unregister_datanotify) (struct grub_efi_ip6_config_protocol *this,
++					     grub_efi_ip6_config_data_type_t data_type,
++					     grub_efi_event_t event);
++};
++typedef struct grub_efi_ip6_config_protocol grub_efi_ip6_config_protocol_t;
++
+ #if (GRUB_TARGET_SIZEOF_VOID_P == 4) || defined (__ia64__) \
+   || defined (__aarch64__) || defined (__MINGW64__) || defined (__CYGWIN__) \
+   || defined(__riscv)
diff --git a/SOURCES/0095-Support-UEFI-networking-protocols.patch b/SOURCES/0095-Support-UEFI-networking-protocols.patch
new file mode 100644
index 0000000..fb14386
--- /dev/null
+++ b/SOURCES/0095-Support-UEFI-networking-protocols.patch
@@ -0,0 +1,5053 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Chang <mchang@suse.com>
+Date: Wed, 22 Feb 2017 14:27:50 +0800
+Subject: [PATCH] Support UEFI networking protocols
+
+References: fate#320130, bsc#1015589, bsc#1076132
+Patch-Mainline: no
+
+V1:
+  * Add preliminary support of UEFI networking protocols
+  * Support UEFI HTTPS Boot
+
+V2:
+  * Workaround http data access in firmware
+  * Fix DNS device path parsing for efinet device
+  * Relaxed UEFI Protocol requirement
+  * Support Intel OPA (Omni-Path Architecture) PXE Boot
+
+V3:
+  * Fix bufio in calculating address of next_buf
+  * Check HTTP respond code
+  * Use HEAD request method to test before GET
+  * Finish HTTP transaction in one go
+  * Fix bsc#1076132
+
+Signed-off-by: Michael Chang <mchang@suse.com>
+[pjones: make efi_netfs not duplicate symbols from efinet]
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/Makefile.core.def        |   12 +
+ grub-core/io/bufio.c               |    2 +-
+ grub-core/kern/efi/efi.c           |   96 ++-
+ grub-core/net/drivers/efi/efinet.c |   27 +
+ grub-core/net/efi/dhcp.c           |  397 ++++++++++
+ grub-core/net/efi/efi_netfs.c      |   57 ++
+ grub-core/net/efi/http.c           |  419 +++++++++++
+ grub-core/net/efi/ip4_config.c     |  398 ++++++++++
+ grub-core/net/efi/ip6_config.c     |  422 +++++++++++
+ grub-core/net/efi/net.c            | 1428 ++++++++++++++++++++++++++++++++++++
+ grub-core/net/efi/pxe.c            |  424 +++++++++++
+ grub-core/net/net.c                |   74 ++
+ util/grub-mknetdir.c               |   23 +-
+ include/grub/efi/api.h             |  180 ++++-
+ include/grub/efi/dhcp.h            |  343 +++++++++
+ include/grub/efi/http.h            |  215 ++++++
+ include/grub/net/efi.h             |  144 ++++
+ 17 files changed, 4620 insertions(+), 41 deletions(-)
+ create mode 100644 grub-core/net/efi/dhcp.c
+ create mode 100644 grub-core/net/efi/efi_netfs.c
+ create mode 100644 grub-core/net/efi/http.c
+ create mode 100644 grub-core/net/efi/ip4_config.c
+ create mode 100644 grub-core/net/efi/ip6_config.c
+ create mode 100644 grub-core/net/efi/net.c
+ create mode 100644 grub-core/net/efi/pxe.c
+ create mode 100644 include/grub/efi/dhcp.h
+ create mode 100644 include/grub/efi/http.h
+ create mode 100644 include/grub/net/efi.h
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 4b7c45a7b06..c40170f2dd2 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -2299,6 +2299,12 @@ module = {
+   common = hook/datehook.c;
+ };
+ 
++module = {
++  name = efi_netfs;
++  common = net/efi/efi_netfs.c;
++  enable = efi;
++};
++
+ module = {
+   name = net;
+   common = net/net.c;
+@@ -2312,6 +2318,12 @@ module = {
+   common = net/ethernet.c;
+   common = net/arp.c;
+   common = net/netbuff.c;
++  efi = net/efi/net.c;
++  efi = net/efi/http.c;
++  efi = net/efi/pxe.c;
++  efi = net/efi/ip4_config.c;
++  efi = net/efi/ip6_config.c;
++  efi = net/efi/dhcp.c;
+ };
+ 
+ module = {
+diff --git a/grub-core/io/bufio.c b/grub-core/io/bufio.c
+index a458c3aca78..1637731535e 100644
+--- a/grub-core/io/bufio.c
++++ b/grub-core/io/bufio.c
+@@ -139,7 +139,7 @@ grub_bufio_read (grub_file_t file, char *buf, grub_size_t len)
+     return res;
+ 
+   /* Need to read some more.  */
+-  next_buf = (file->offset + res + len - 1) & ~((grub_off_t) bufio->block_size - 1);
++  next_buf = (grub_divmod64 (file->offset + res + len - 1, bufio->block_size, NULL)) * bufio->block_size;
+   /* Now read between file->offset + res and bufio->buffer_at.  */
+   if (file->offset + res < next_buf)
+     {
+diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
+index d6a2fb57789..2a446f5031b 100644
+--- a/grub-core/kern/efi/efi.c
++++ b/grub-core/kern/efi/efi.c
+@@ -755,7 +755,7 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp)
+ 	      {
+ 		grub_efi_ipv4_device_path_t *ipv4
+ 		  = (grub_efi_ipv4_device_path_t *) dp;
+-		grub_printf ("/IPv4(%u.%u.%u.%u,%u.%u.%u.%u,%u,%u,%x,%x)",
++		grub_printf ("/IPv4(%u.%u.%u.%u,%u.%u.%u.%u,%u,%u,%x,%x",
+ 			     (unsigned) ipv4->local_ip_address[0],
+ 			     (unsigned) ipv4->local_ip_address[1],
+ 			     (unsigned) ipv4->local_ip_address[2],
+@@ -768,33 +768,60 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp)
+ 			     (unsigned) ipv4->remote_port,
+ 			     (unsigned) ipv4->protocol,
+ 			     (unsigned) ipv4->static_ip_address);
++		if (len == sizeof (*ipv4))
++		  {
++		    grub_printf (",%u.%u.%u.%u,%u.%u.%u.%u",
++			(unsigned) ipv4->gateway_ip_address[0],
++			(unsigned) ipv4->gateway_ip_address[1],
++			(unsigned) ipv4->gateway_ip_address[2],
++			(unsigned) ipv4->gateway_ip_address[3],
++			(unsigned) ipv4->subnet_mask[0],
++			(unsigned) ipv4->subnet_mask[1],
++			(unsigned) ipv4->subnet_mask[2],
++			(unsigned) ipv4->subnet_mask[3]);
++		  }
++		grub_printf (")");
+ 	      }
+ 	      break;
+ 	    case GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE:
+ 	      {
+ 		grub_efi_ipv6_device_path_t *ipv6
+ 		  = (grub_efi_ipv6_device_path_t *) dp;
+-		grub_printf ("/IPv6(%x:%x:%x:%x:%x:%x:%x:%x,%x:%x:%x:%x:%x:%x:%x:%x,%u,%u,%x,%x)",
+-			     (unsigned) ipv6->local_ip_address[0],
+-			     (unsigned) ipv6->local_ip_address[1],
+-			     (unsigned) ipv6->local_ip_address[2],
+-			     (unsigned) ipv6->local_ip_address[3],
+-			     (unsigned) ipv6->local_ip_address[4],
+-			     (unsigned) ipv6->local_ip_address[5],
+-			     (unsigned) ipv6->local_ip_address[6],
+-			     (unsigned) ipv6->local_ip_address[7],
+-			     (unsigned) ipv6->remote_ip_address[0],
+-			     (unsigned) ipv6->remote_ip_address[1],
+-			     (unsigned) ipv6->remote_ip_address[2],
+-			     (unsigned) ipv6->remote_ip_address[3],
+-			     (unsigned) ipv6->remote_ip_address[4],
+-			     (unsigned) ipv6->remote_ip_address[5],
+-			     (unsigned) ipv6->remote_ip_address[6],
+-			     (unsigned) ipv6->remote_ip_address[7],
++		grub_printf ("/IPv6(%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x,%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x,%u,%u,%x,%x",
++			     (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[0]),
++			     (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[1]),
++			     (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[2]),
++			     (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[3]),
++			     (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[4]),
++			     (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[5]),
++			     (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[6]),
++			     (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[7]),
++			     (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[0]),
++			     (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[1]),
++			     (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[2]),
++			     (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[3]),
++			     (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[4]),
++			     (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[5]),
++			     (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[6]),
++			     (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[7]),
+ 			     (unsigned) ipv6->local_port,
+ 			     (unsigned) ipv6->remote_port,
+ 			     (unsigned) ipv6->protocol,
+ 			     (unsigned) ipv6->static_ip_address);
++		if (len == sizeof (*ipv6))
++		  {
++		    grub_printf (",%u,%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
++			(unsigned) ipv6->prefix_length,
++			(unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[0]),
++			(unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[1]),
++			(unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[2]),
++			(unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[3]),
++			(unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[4]),
++			(unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[5]),
++			(unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[6]),
++			(unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[7]));
++		  }
++		grub_printf (")");
+ 	      }
+ 	      break;
+ 	    case GRUB_EFI_INFINIBAND_DEVICE_PATH_SUBTYPE:
+@@ -834,6 +861,39 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp)
+ 	      dump_vendor_path ("Messaging",
+ 				(grub_efi_vendor_device_path_t *) dp);
+ 	      break;
++	    case GRUB_EFI_URI_DEVICE_PATH_SUBTYPE:
++	      {
++		grub_efi_uri_device_path_t *uri
++		  = (grub_efi_uri_device_path_t *) dp;
++		grub_printf ("/URI(%s)", uri->uri);
++	      }
++	      break;
++	    case GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE:
++	      {
++		grub_efi_dns_device_path_t *dns
++		  = (grub_efi_dns_device_path_t *) dp;
++		if (dns->is_ipv6)
++		  {
++		    grub_printf ("/DNS(%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x)",
++			    (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[0]) >> 16),
++			    (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[0])),
++			    (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[1]) >> 16),
++			    (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[1])),
++			    (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[2]) >> 16),
++			    (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[2])),
++			    (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[3]) >> 16),
++			    (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[3])));
++		  }
++		else
++		  {
++		    grub_printf ("/DNS(%d.%d.%d.%d)",
++			  dns->dns_server_ip[0].v4.addr[0],
++			  dns->dns_server_ip[0].v4.addr[1],
++			  dns->dns_server_ip[0].v4.addr[2],
++			  dns->dns_server_ip[0].v4.addr[3]);
++		  }
++	      }
++	      break;
+ 	    default:
+ 	      grub_printf ("/UnknownMessaging(%x)", (unsigned) subtype);
+ 	      break;
+diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
+index 715a6168d77..e11d759f19a 100644
+--- a/grub-core/net/drivers/efi/efinet.c
++++ b/grub-core/net/drivers/efi/efinet.c
+@@ -27,6 +27,7 @@
+ #include <grub/lib/hexdump.h>
+ #include <grub/types.h>
+ #include <grub/net/netbuff.h>
++#include <grub/env.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -491,6 +492,17 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u
+ 
+   ldp = grub_efi_find_last_device_path (ddp);
+ 
++  /* Skip the DNS Device */
++  if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
++      && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE)
++    {
++      ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
++      ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
++      ldp->length = sizeof (*ldp);
++
++      ldp = grub_efi_find_last_device_path (ddp);
++    }
++
+   if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
+       || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE
+           && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE))
+@@ -760,6 +772,7 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+ 	if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
+ 	    || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE
+ 		&& GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE
++		&& GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE
+ 		&& GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE))
+ 	  continue;
+ 	dup_dp = grub_efi_duplicate_device_path (dp);
+@@ -774,6 +787,15 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+ 	    dup_ldp->length = sizeof (*dup_ldp);
+ 	  }
+ 
++	dup_ldp = grub_efi_find_last_device_path (dup_dp);
++	if (GRUB_EFI_DEVICE_PATH_SUBTYPE (dup_ldp) == GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE)
++	  {
++	    dup_ldp = grub_efi_find_last_device_path (dup_dp);
++	    dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
++	    dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
++	    dup_ldp->length = sizeof (*dup_ldp);
++	  }
++
+ 	dup_ldp = grub_efi_find_last_device_path (dup_dp);
+ 	dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
+ 	dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
+@@ -845,6 +867,9 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+ 
+ GRUB_MOD_INIT(efinet)
+ {
++  if (grub_efi_net_config)
++    return;
++
+   grub_efinet_findcards ();
+   grub_efi_net_config = grub_efi_net_config_real;
+ }
+@@ -856,5 +881,7 @@ GRUB_MOD_FINI(efinet)
+   FOR_NET_CARDS_SAFE (card, next) 
+     if (card->driver == &efidriver)
+       grub_net_card_unregister (card);
++
++  grub_efi_net_config = NULL;
+ }
+ 
+diff --git a/grub-core/net/efi/dhcp.c b/grub-core/net/efi/dhcp.c
+new file mode 100644
+index 00000000000..dbef63d8c08
+--- /dev/null
++++ b/grub-core/net/efi/dhcp.c
+@@ -0,0 +1,397 @@
++#include <grub/mm.h>
++#include <grub/command.h>
++#include <grub/efi/api.h>
++#include <grub/efi/efi.h>
++#include <grub/misc.h>
++#include <grub/net/efi.h>
++#include <grub/charset.h>
++
++#ifdef GRUB_EFI_NET_DEBUG
++static void
++dhcp4_mode_print (grub_efi_dhcp4_mode_data_t *mode)
++{
++    switch (mode->state)
++      {
++	case GRUB_EFI_DHCP4_STOPPED:
++	  grub_printf ("STATE: STOPPED\n");
++	  break;
++	case GRUB_EFI_DHCP4_INIT:
++	  grub_printf ("STATE: INIT\n");
++	  break;
++	case GRUB_EFI_DHCP4_SELECTING:
++	  grub_printf ("STATE: SELECTING\n");
++	  break;
++	case GRUB_EFI_DHCP4_REQUESTING:
++	  grub_printf ("STATE: REQUESTING\n");
++	  break;
++	case GRUB_EFI_DHCP4_BOUND:
++	  grub_printf ("STATE: BOUND\n");
++	  break;
++	case GRUB_EFI_DHCP4_RENEWING:
++	  grub_printf ("STATE: RENEWING\n");
++	  break;
++	case GRUB_EFI_DHCP4_REBINDING:
++	  grub_printf ("STATE: REBINDING\n");
++	  break;
++	case GRUB_EFI_DHCP4_INIT_REBOOT:
++	  grub_printf ("STATE: INIT_REBOOT\n");
++	  break;
++	case GRUB_EFI_DHCP4_REBOOTING:
++	  grub_printf ("STATE: REBOOTING\n");
++	  break;
++	default:
++	  grub_printf ("STATE: UNKNOWN\n");
++	  break;
++      }
++
++    grub_printf ("CLIENT_ADDRESS: %u.%u.%u.%u\n",
++      mode->client_address[0],
++      mode->client_address[1],
++      mode->client_address[2],
++      mode->client_address[3]);
++    grub_printf ("SERVER_ADDRESS: %u.%u.%u.%u\n",
++      mode->server_address[0],
++      mode->server_address[1],
++      mode->server_address[2],
++      mode->server_address[3]);
++    grub_printf ("SUBNET_MASK: %u.%u.%u.%u\n",
++      mode->subnet_mask[0],
++      mode->subnet_mask[1],
++      mode->subnet_mask[2],
++      mode->subnet_mask[3]);
++    grub_printf ("ROUTER_ADDRESS: %u.%u.%u.%u\n",
++      mode->router_address[0],
++      mode->router_address[1],
++      mode->router_address[2],
++      mode->router_address[3]);
++}
++#endif
++
++static grub_efi_ipv4_address_t *
++grub_efi_dhcp4_parse_dns (grub_efi_dhcp4_protocol_t *dhcp4, grub_efi_dhcp4_packet_t *reply_packet)
++{
++  grub_efi_dhcp4_packet_option_t **option_list;
++  grub_efi_status_t status;
++  grub_efi_uint32_t option_count = 0;
++  grub_efi_uint32_t i;
++
++  status = efi_call_4 (dhcp4->parse, dhcp4, reply_packet, &option_count, NULL);
++
++  if (status != GRUB_EFI_BUFFER_TOO_SMALL)
++    return NULL;
++
++  option_list = grub_malloc (option_count * sizeof(*option_list));
++  if (!option_list)
++    return NULL;
++
++  status = efi_call_4 (dhcp4->parse, dhcp4, reply_packet, &option_count, option_list);
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      grub_free (option_list);
++      return NULL;
++    }
++
++  for (i = 0; i < option_count; ++i)
++    {
++      if (option_list[i]->op_code == 6)
++	{
++	  grub_efi_ipv4_address_t *dns_address;
++
++	  if (((option_list[i]->length & 0x3) != 0) || (option_list[i]->length == 0))
++	    continue;
++
++	  /* We only contact primary dns */
++	  dns_address = grub_malloc (sizeof (*dns_address));
++	  if (!dns_address)
++	    {
++	      grub_free (option_list);
++	      return NULL;
++	    }
++	  grub_memcpy (dns_address, option_list[i]->data, sizeof (dns_address));
++	  grub_free (option_list);
++	  return dns_address;
++	}
++    }
++
++  grub_free (option_list);
++  return NULL;
++}
++
++#if 0
++/* Somehow this doesn't work ... */
++static grub_err_t
++grub_cmd_efi_bootp (struct grub_command *cmd __attribute__ ((unused)),
++		    int argc __attribute__ ((unused)),
++		    char **args __attribute__ ((unused)))
++{
++  struct grub_efi_net_device *dev;
++  for (dev = net_devices; dev; dev = dev->next)
++    {
++      grub_efi_pxe_t *pxe = dev->ip4_pxe;
++      grub_efi_pxe_mode_t *mode = pxe->mode;
++      grub_efi_status_t status;
++
++      if (!mode->started)
++	{
++	  status = efi_call_2 (pxe->start, pxe, 0);
++
++	  if (status != GRUB_EFI_SUCCESS)
++	      grub_printf ("Couldn't start PXE\n");
++	}
++
++      status = efi_call_2 (pxe->dhcp, pxe, 0);
++      if (status != GRUB_EFI_SUCCESS)
++	{
++	  grub_printf ("dhcp4 configure failed, %d\n", (int)status);
++	  continue;
++	}
++
++      dev->prefer_ip6 = 0;
++    }
++
++  return GRUB_ERR_NONE;
++}
++#endif
++
++static grub_err_t
++grub_cmd_efi_bootp (struct grub_command *cmd __attribute__ ((unused)),
++		    int argc,
++		    char **args)
++{
++  struct grub_efi_net_device *netdev;
++
++  for (netdev = net_devices; netdev; netdev = netdev->next)
++    {
++      grub_efi_status_t status;
++      grub_efi_dhcp4_mode_data_t mode;
++      grub_efi_dhcp4_config_data_t config;
++      grub_efi_dhcp4_packet_option_t *options;
++      grub_efi_ipv4_address_t *dns_address;
++      grub_efi_net_ip_manual_address_t net_ip;
++      grub_efi_net_ip_address_t ip_addr;
++      grub_efi_net_interface_t *inf = NULL;
++
++      if (argc > 0 && grub_strcmp (netdev->card_name, args[0]) != 0)
++	continue;
++
++      grub_memset (&config, 0, sizeof(config));
++
++      config.option_count = 1;
++      options = grub_malloc (sizeof(*options) + 2);
++      /* Parameter request list */
++      options->op_code = 55;
++      options->length = 3;
++      /* subnet mask */
++      options->data[0] = 1;
++      /* router */
++      options->data[1] = 3;
++      /* DNS */
++      options->data[2] = 6;
++      config.option_list = &options;
++
++      /* FIXME: What if the dhcp has bounded */
++      status = efi_call_2 (netdev->dhcp4->configure, netdev->dhcp4, &config);
++      grub_free (options);
++      if (status != GRUB_EFI_SUCCESS)
++	{
++	  grub_printf ("dhcp4 configure failed, %d\n", (int)status);
++	  continue;
++	}
++
++      status = efi_call_2 (netdev->dhcp4->start, netdev->dhcp4, NULL);
++      if (status != GRUB_EFI_SUCCESS)
++	{
++	  grub_printf ("dhcp4 start failed, %d\n", (int)status);
++	  continue;
++	}
++
++      status = efi_call_2 (netdev->dhcp4->get_mode_data, netdev->dhcp4, &mode);
++      if (status != GRUB_EFI_SUCCESS)
++	{
++	  grub_printf ("dhcp4 get mode failed, %d\n", (int)status);
++	  continue;
++	}
++
++#ifdef GRUB_EFI_NET_DEBUG
++      dhcp4_mode_print (&mode);
++#endif
++
++      for (inf = netdev->net_interfaces; inf; inf = inf->next)
++	if (inf->prefer_ip6 == 0)
++	  break;
++
++      grub_memcpy (net_ip.ip4.address, mode.client_address, sizeof (net_ip.ip4.address));
++      grub_memcpy (net_ip.ip4.subnet_mask, mode.subnet_mask, sizeof (net_ip.ip4.subnet_mask));
++
++      if (!inf)
++	{
++	  char *name = grub_xasprintf ("%s:dhcp", netdev->card_name);
++
++	  net_ip.is_ip6 = 0;
++	  inf = grub_efi_net_create_interface (netdev,
++		    name,
++		    &net_ip,
++		    1);
++	  grub_free (name);
++	}
++      else
++	{
++	  efi_net_interface_set_address (inf, &net_ip, 1);
++	}
++
++      grub_memcpy (ip_addr.ip4, mode.router_address, sizeof (ip_addr.ip4));
++      efi_net_interface_set_gateway (inf, &ip_addr);
++
++      dns_address = grub_efi_dhcp4_parse_dns (netdev->dhcp4, mode.reply_packet);
++      if (dns_address)
++	efi_net_interface_set_dns (inf, (grub_efi_net_ip_address_t *)&dns_address);
++
++    }
++
++  return GRUB_ERR_NONE;
++}
++
++
++static grub_err_t
++grub_cmd_efi_bootp6 (struct grub_command *cmd __attribute__ ((unused)),
++		    int argc,
++		    char **args)
++{
++  struct grub_efi_net_device *dev;
++  grub_efi_uint32_t ia_id;
++
++  for (dev = net_devices, ia_id = 0; dev; dev = dev->next, ia_id++)
++    {
++      grub_efi_dhcp6_config_data_t config;
++      grub_efi_dhcp6_packet_option_t *option_list[1];
++      grub_efi_dhcp6_packet_option_t *opt;
++      grub_efi_status_t status;
++      grub_efi_dhcp6_mode_data_t mode;
++      grub_efi_dhcp6_retransmission_t retrans;
++      grub_efi_net_ip_manual_address_t net_ip;
++      grub_efi_boot_services_t *b = grub_efi_system_table->boot_services;
++      grub_efi_net_interface_t *inf = NULL;
++
++      if (argc > 0 && grub_strcmp (dev->card_name, args[0]) != 0)
++	continue;
++
++      opt = grub_malloc (sizeof(*opt) + 2 * sizeof (grub_efi_uint16_t));
++
++#define GRUB_EFI_DHCP6_OPT_ORO 6
++
++      opt->op_code = grub_cpu_to_be16_compile_time (GRUB_EFI_DHCP6_OPT_ORO);
++      opt->op_len = grub_cpu_to_be16_compile_time (2 * sizeof (grub_efi_uint16_t));
++
++#define GRUB_EFI_DHCP6_OPT_BOOT_FILE_URL 59
++#define GRUB_EFI_DHCP6_OPT_DNS_SERVERS 23
++
++      grub_set_unaligned16 (opt->data, grub_cpu_to_be16_compile_time(GRUB_EFI_DHCP6_OPT_BOOT_FILE_URL));
++      grub_set_unaligned16 (opt->data + 1 * sizeof (grub_efi_uint16_t),
++	      grub_cpu_to_be16_compile_time(GRUB_EFI_DHCP6_OPT_DNS_SERVERS));
++
++      option_list[0] = opt;
++      retrans.irt = 4;
++      retrans.mrc = 4;
++      retrans.mrt = 32;
++      retrans.mrd = 60;
++
++      config.dhcp6_callback = NULL;
++      config.callback_context = NULL;
++      config.option_count = 1;
++      config.option_list = option_list;
++      config.ia_descriptor.ia_id = ia_id;
++      config.ia_descriptor.type = GRUB_EFI_DHCP6_IA_TYPE_NA;
++      config.ia_info_event = NULL;
++      config.reconfigure_accept = 0;
++      config.rapid_commit = 0;
++      config.solicit_retransmission = &retrans;
++
++      status = efi_call_2 (dev->dhcp6->configure, dev->dhcp6, &config);
++      grub_free (opt);
++      if (status != GRUB_EFI_SUCCESS)
++	{
++	  grub_printf ("dhcp6 configure failed, %d\n", (int)status);
++	  continue;
++	}
++      status = efi_call_1 (dev->dhcp6->start, dev->dhcp6);
++      if (status != GRUB_EFI_SUCCESS)
++	{
++	  grub_printf ("dhcp6 start failed, %d\n", (int)status);
++	  continue;
++	}
++
++      status = efi_call_3 (dev->dhcp6->get_mode_data, dev->dhcp6, &mode, NULL);
++      if (status != GRUB_EFI_SUCCESS)
++	{
++	  grub_printf ("dhcp4 get mode failed, %d\n", (int)status);
++	  continue;
++	}
++
++      for (inf = dev->net_interfaces; inf; inf = inf->next)
++	if (inf->prefer_ip6 == 1)
++	  break;
++
++      grub_memcpy (net_ip.ip6.address, mode.ia->ia_address[0].ip_address, sizeof (net_ip.ip6.address));
++      net_ip.ip6.prefix_length = 64;
++      net_ip.ip6.is_anycast = 0;
++      net_ip.is_ip6 = 1;
++
++      if (!inf)
++	{
++	  char *name = grub_xasprintf ("%s:dhcp", dev->card_name);
++
++	  inf = grub_efi_net_create_interface (dev,
++		    name,
++		    &net_ip,
++		    1);
++	  grub_free (name);
++	}
++      else
++	{
++	  efi_net_interface_set_address (inf, &net_ip, 1);
++	}
++
++      {
++	grub_efi_uint32_t count = 0;
++	grub_efi_dhcp6_packet_option_t **options = NULL;
++	grub_efi_uint32_t i;
++
++	status = efi_call_4 (dev->dhcp6->parse, dev->dhcp6, mode.ia->reply_packet, &count, NULL);
++
++	if (status == GRUB_EFI_BUFFER_TOO_SMALL && count)
++	  {
++	    options = grub_malloc (count * sizeof(*options));
++	    status = efi_call_4 (dev->dhcp6->parse, dev->dhcp6, mode.ia->reply_packet, &count, options);
++	  }
++
++	if (status != GRUB_EFI_SUCCESS)
++	  {
++	    if (options)
++	      grub_free (options);
++	    continue;
++	  }
++
++	for (i = 0; i < count; ++i)
++	  {
++	    if (options[i]->op_code == grub_cpu_to_be16_compile_time(GRUB_EFI_DHCP6_OPT_DNS_SERVERS))
++	      {
++		grub_efi_net_ip_address_t dns;
++		grub_memcpy (dns.ip6, options[i]->data, sizeof(net_ip.ip6));
++		efi_net_interface_set_dns (inf, &dns);
++		break;
++	      }
++	  }
++
++	if (options)
++	  grub_free (options);
++      }
++
++      efi_call_1 (b->free_pool, mode.client_id);
++      efi_call_1 (b->free_pool, mode.ia);
++    }
++
++  return GRUB_ERR_NONE;
++}
++
++grub_command_func_t grub_efi_net_bootp = grub_cmd_efi_bootp;
++grub_command_func_t grub_efi_net_bootp6 = grub_cmd_efi_bootp6;
+diff --git a/grub-core/net/efi/efi_netfs.c b/grub-core/net/efi/efi_netfs.c
+new file mode 100644
+index 00000000000..ef371d885ea
+--- /dev/null
++++ b/grub-core/net/efi/efi_netfs.c
+@@ -0,0 +1,57 @@
++#include <grub/dl.h>
++#include <grub/env.h>
++#define EFI_NET_CMD_PREFIX "net_efi"
++#include <grub/net/efi.h>
++
++GRUB_MOD_LICENSE ("GPLv3+");
++
++static grub_command_t cmd_efi_lsroutes;
++static grub_command_t cmd_efi_lscards;
++static grub_command_t cmd_efi_lsaddrs;
++static grub_command_t cmd_efi_addaddr;
++static grub_command_t cmd_efi_bootp;
++static grub_command_t cmd_efi_bootp6;
++
++static int initialized;
++
++GRUB_MOD_INIT(efi_netfs)
++{
++  if (grub_net_open)
++    return;
++
++  if (grub_efi_net_fs_init ())
++    {
++      cmd_efi_lsroutes = grub_register_command ("net_efi_ls_routes", grub_efi_net_list_routes,
++					    "", N_("list network routes"));
++      cmd_efi_lscards = grub_register_command ("net_efi_ls_cards", grub_efi_net_list_cards,
++					   "", N_("list network cards"));
++      cmd_efi_lsaddrs = grub_register_command ("net_efi_ls_addr", grub_efi_net_list_addrs,
++					  "", N_("list network addresses"));
++      cmd_efi_addaddr = grub_register_command ("net_efi_add_addr", grub_efi_net_add_addr,
++					  N_("SHORTNAME CARD ADDRESS [HWADDRESS]"),
++					  N_("Add a network address."));
++      cmd_efi_bootp = grub_register_command ("net_efi_bootp", grub_efi_net_bootp,
++					 N_("[CARD]"),
++					 N_("perform a bootp autoconfiguration"));
++      cmd_efi_bootp6 = grub_register_command ("net_efi_bootp6", grub_efi_net_bootp6,
++					 N_("[CARD]"),
++					 N_("perform a bootp autoconfiguration"));
++      initialized = 1;
++    }
++}
++
++GRUB_MOD_FINI(efi_netfs)
++{
++  if (initialized)
++    {
++      grub_unregister_command (cmd_efi_lsroutes);
++      grub_unregister_command (cmd_efi_lscards);
++      grub_unregister_command (cmd_efi_lsaddrs);
++      grub_unregister_command (cmd_efi_addaddr);
++      grub_unregister_command (cmd_efi_bootp);
++      grub_unregister_command (cmd_efi_bootp6);
++      grub_efi_net_fs_fini ();
++      initialized = 0;
++      return;
++    }
++}
+diff --git a/grub-core/net/efi/http.c b/grub-core/net/efi/http.c
+new file mode 100644
+index 00000000000..3f61fd2fa5b
+--- /dev/null
++++ b/grub-core/net/efi/http.c
+@@ -0,0 +1,419 @@
++
++#include <grub/efi/api.h>
++#include <grub/efi/efi.h>
++#include <grub/misc.h>
++#include <grub/net/efi.h>
++#include <grub/charset.h>
++
++static void
++http_configure (struct grub_efi_net_device *dev, int prefer_ip6)
++{
++  grub_efi_http_config_data_t http_config;
++  grub_efi_httpv4_access_point_t httpv4_node;
++  grub_efi_httpv6_access_point_t httpv6_node;
++  grub_efi_status_t status;
++
++  grub_efi_http_t *http = dev->http;
++
++  grub_memset (&http_config, 0, sizeof(http_config));
++  http_config.http_version = GRUB_EFI_HTTPVERSION11;
++  http_config.timeout_millisec = 5000;
++
++  if (prefer_ip6)
++    {
++      grub_efi_uintn_t sz;
++      grub_efi_ip6_config_manual_address_t manual_address;
++
++      http_config.local_address_is_ipv6 = 1;
++      sz = sizeof (manual_address);
++      status = efi_call_4 (dev->ip6_config->get_data, dev->ip6_config,
++			GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS,
++			&sz, &manual_address);
++
++      if (status == GRUB_EFI_NOT_FOUND)
++	{
++	  grub_printf ("The MANUAL ADDRESS is not found\n");
++	}
++
++      /* FIXME: The manual interface would return BUFFER TOO SMALL !!! */
++      if (status != GRUB_EFI_SUCCESS)
++	{
++	  grub_printf ("??? %d\n",(int) status);
++	  return;
++	}
++
++      grub_memcpy (httpv6_node.local_address, manual_address.address, sizeof (httpv6_node.local_address));
++      httpv6_node.local_port = 0;
++      http_config.access_point.ipv6_node = &httpv6_node;
++    }
++  else
++    {
++      http_config.local_address_is_ipv6 = 0;
++      grub_memset (&httpv4_node, 0, sizeof(httpv4_node));
++      httpv4_node.use_default_address = 1;
++
++      /* Use random port here */
++      /* See TcpBind() in edk2/NetworkPkg/TcpDxe/TcpDispatcher.c */
++      httpv4_node.local_port = 0;
++      http_config.access_point.ipv4_node = &httpv4_node;
++    }
++
++  status = efi_call_2 (http->configure, http, &http_config);
++
++  if (status == GRUB_EFI_ALREADY_STARTED)
++    {
++      /* XXX: This hangs HTTPS boot */
++#if 0
++      if (efi_call_2 (http->configure, http, NULL) != GRUB_EFI_SUCCESS)
++	{
++	  grub_error (GRUB_ERR_IO, N_("couldn't reset http instance"));
++	  grub_print_error ();
++	  return;
++	}
++      status = efi_call_2 (http->configure, http, &http_config);
++#endif
++      return;
++    }
++
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      grub_error (GRUB_ERR_IO, N_("couldn't configure http protocol, reason: %d"), (int)status);
++      grub_print_error ();
++      return ;
++    }
++}
++
++static grub_efi_boolean_t request_callback_done;
++static grub_efi_boolean_t response_callback_done;
++
++static void
++grub_efi_http_request_callback (grub_efi_event_t event __attribute__ ((unused)),
++				void *context __attribute__ ((unused)))
++{
++  request_callback_done = 1;
++}
++
++static void
++grub_efi_http_response_callback (grub_efi_event_t event __attribute__ ((unused)),
++				void *context __attribute__ ((unused)))
++{
++  response_callback_done = 1;
++}
++
++static grub_err_t
++efihttp_request (grub_efi_http_t *http, char *server, char *name, int use_https, int headeronly, grub_off_t *file_size)
++{
++  grub_efi_http_request_data_t request_data;
++  grub_efi_http_message_t request_message;
++  grub_efi_http_token_t request_token;
++  grub_efi_http_response_data_t response_data;
++  grub_efi_http_message_t response_message;
++  grub_efi_http_token_t response_token;
++  grub_efi_http_header_t request_headers[3];
++
++  grub_efi_status_t status;
++  grub_efi_boot_services_t *b = grub_efi_system_table->boot_services;
++  char *url = NULL;
++
++  request_headers[0].field_name = (grub_efi_char8_t *)"Host";
++  request_headers[0].field_value = (grub_efi_char8_t *)server;
++  request_headers[1].field_name = (grub_efi_char8_t *)"Accept";
++  request_headers[1].field_value = (grub_efi_char8_t *)"*/*";
++  request_headers[2].field_name = (grub_efi_char8_t *)"User-Agent";
++  request_headers[2].field_value = (grub_efi_char8_t *)"UefiHttpBoot/1.0";
++
++  {
++    grub_efi_ipv6_address_t address;
++    const char *rest;
++    grub_efi_char16_t *ucs2_url;
++    grub_size_t url_len, ucs2_url_len;
++    const char *protocol = (use_https == 1) ? "https" : "http";
++
++    if (grub_efi_string_to_ip6_address (server, &address, &rest) && *rest == 0)
++      url = grub_xasprintf ("%s://[%s]%s", protocol, server, name);
++    else
++      url = grub_xasprintf ("%s://%s%s", protocol, server, name);
++
++    if (!url)
++      {
++	return grub_errno;
++      }
++
++    url_len = grub_strlen (url);
++    ucs2_url_len = url_len * GRUB_MAX_UTF16_PER_UTF8;
++    ucs2_url = grub_malloc ((ucs2_url_len + 1) * sizeof (ucs2_url[0]));
++
++    if (!ucs2_url)
++      {
++	grub_free (url);
++	return grub_errno;
++      }
++
++    ucs2_url_len = grub_utf8_to_utf16 (ucs2_url, ucs2_url_len, (grub_uint8_t *)url, url_len, NULL); /* convert string format from ascii to usc2 */
++    ucs2_url[ucs2_url_len] = 0;
++    grub_free (url);
++    request_data.url = ucs2_url;
++  }
++
++  request_data.method = (headeronly > 0) ? GRUB_EFI_HTTPMETHODHEAD : GRUB_EFI_HTTPMETHODGET;
++
++  request_message.data.request = &request_data;
++  request_message.header_count = 3;
++  request_message.headers = request_headers;
++  request_message.body_length = 0;
++  request_message.body = NULL;
++
++  /* request token */
++  request_token.event = NULL;
++  request_token.status = GRUB_EFI_NOT_READY;
++  request_token.message = &request_message;
++
++  request_callback_done = 0;
++  status = efi_call_5 (b->create_event,
++                       GRUB_EFI_EVT_NOTIFY_SIGNAL,
++                       GRUB_EFI_TPL_CALLBACK,
++                       grub_efi_http_request_callback,
++                       NULL,
++                       &request_token.event);
++
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      grub_free (request_data.url);
++      return grub_error (GRUB_ERR_IO, "Fail to create an event! status=0x%x\n", status);
++    }
++
++  status = efi_call_2 (http->request, http, &request_token);
++
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      efi_call_1 (b->close_event, request_token.event);
++      grub_free (request_data.url);
++      return grub_error (GRUB_ERR_IO, "Fail to send a request! status=0x%x\n", status);
++    }
++  /* TODO: Add Timeout */
++  while (!request_callback_done)
++    efi_call_1(http->poll, http);
++
++  response_data.status_code = GRUB_EFI_HTTP_STATUS_UNSUPPORTED_STATUS;
++  response_message.data.response = &response_data;
++  /* herader_count will be updated by the HTTP driver on response */
++  response_message.header_count = 0;
++  /* headers will be populated by the driver on response */
++  response_message.headers = NULL;
++  /* use zero BodyLength to only receive the response headers */
++  response_message.body_length = 0;
++  response_message.body = NULL;
++  response_token.event = NULL;
++
++  status = efi_call_5 (b->create_event,
++              GRUB_EFI_EVT_NOTIFY_SIGNAL,
++              GRUB_EFI_TPL_CALLBACK,
++              grub_efi_http_response_callback,
++              NULL,
++              &response_token.event);
++
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      efi_call_1 (b->close_event, request_token.event);
++      grub_free (request_data.url);
++      return grub_error (GRUB_ERR_IO, "Fail to create an event! status=0x%x\n", status);
++    }
++
++  response_token.status = GRUB_EFI_SUCCESS;
++  response_token.message = &response_message;
++
++  /* wait for HTTP response */
++  response_callback_done = 0;
++  status = efi_call_2 (http->response, http, &response_token);
++
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      efi_call_1 (b->close_event, response_token.event);
++      efi_call_1 (b->close_event, request_token.event);
++      grub_free (request_data.url);
++      return grub_error (GRUB_ERR_IO, "Fail to receive a response! status=%d\n", (int)status);
++    }
++
++  /* TODO: Add Timeout */
++  while (!response_callback_done)
++    efi_call_1 (http->poll, http);
++
++  if (response_message.data.response->status_code != GRUB_EFI_HTTP_STATUS_200_OK)
++    {
++      grub_efi_http_status_code_t status_code = response_message.data.response->status_code;
++
++      if (response_message.headers)
++	efi_call_1 (b->free_pool, response_message.headers);
++      efi_call_1 (b->close_event, response_token.event);
++      efi_call_1 (b->close_event, request_token.event);
++      grub_free (request_data.url);
++      if (status_code == GRUB_EFI_HTTP_STATUS_404_NOT_FOUND)
++	{
++	  return grub_error (GRUB_ERR_FILE_NOT_FOUND, _("file `%s' not found"), name);
++	}
++      else
++	{
++	  return grub_error (GRUB_ERR_NET_UNKNOWN_ERROR,
++		  _("unsupported uefi http status code 0x%x"), status_code);
++	}
++    }
++
++  if (file_size)
++    {
++      int i;
++      /* parse the length of the file from the ContentLength header */
++      for (*file_size = 0, i = 0; i < (int)response_message.header_count; ++i)
++	{
++	  if (!grub_strcmp((const char*)response_message.headers[i].field_name, "Content-Length"))
++	    {
++	      *file_size = grub_strtoul((const char*)response_message.headers[i].field_value, 0, 10);
++	      break;
++	    }
++	}
++    }
++
++  if (response_message.headers)
++    efi_call_1 (b->free_pool, response_message.headers);
++  efi_call_1 (b->close_event, response_token.event);
++  efi_call_1 (b->close_event, request_token.event);
++  grub_free (request_data.url);
++
++  return GRUB_ERR_NONE;
++}
++
++static grub_ssize_t
++efihttp_read (struct grub_efi_net_device *dev,
++		  char *buf,
++		  grub_size_t len)
++{
++  grub_efi_http_message_t response_message;
++  grub_efi_http_token_t response_token;
++
++  grub_efi_status_t status;
++  grub_size_t sum = 0;
++  grub_efi_boot_services_t *b = grub_efi_system_table->boot_services;
++  grub_efi_http_t *http = dev->http;
++
++  if (!len)
++    {
++      grub_error (GRUB_ERR_BUG, "Invalid arguments to EFI HTTP Read");
++      return -1;
++    }
++
++  efi_call_5 (b->create_event,
++              GRUB_EFI_EVT_NOTIFY_SIGNAL,
++              GRUB_EFI_TPL_CALLBACK,
++              grub_efi_http_response_callback,
++              NULL,
++              &response_token.event);
++
++  while (len)
++    {
++      response_message.data.response = NULL;
++      response_message.header_count = 0;
++      response_message.headers = NULL;
++      response_message.body_length = len;
++      response_message.body = buf;
++
++      response_token.message = &response_message;
++      response_token.status = GRUB_EFI_NOT_READY;
++
++      response_callback_done = 0;
++
++      status = efi_call_2 (http->response, http, &response_token);
++      if (status != GRUB_EFI_SUCCESS)
++	{
++	  efi_call_1 (b->close_event, response_token.event);
++	  grub_error (GRUB_ERR_IO, "Error! status=%d\n", (int)status);
++	  return -1;
++	}
++
++      while (!response_callback_done)
++	efi_call_1(http->poll, http);
++
++      sum += response_message.body_length;
++      buf += response_message.body_length;
++      len -= response_message.body_length;
++    }
++
++  efi_call_1 (b->close_event, response_token.event);
++
++  return sum;
++}
++
++static grub_err_t
++grub_efihttp_open (struct grub_efi_net_device *dev,
++		  int prefer_ip6 __attribute__ ((unused)),
++		  grub_file_t file,
++		  const char *filename __attribute__ ((unused)),
++		  int type)
++{
++  grub_err_t err;
++  grub_off_t size;
++  char *buf;
++
++  err = efihttp_request (dev->http, file->device->net->server, file->device->net->name, type, 1, 0);
++  if (err != GRUB_ERR_NONE)
++    return err;
++
++  err = efihttp_request (dev->http, file->device->net->server, file->device->net->name, type, 0, &size);
++  if (err != GRUB_ERR_NONE)
++    return err;
++
++  buf = grub_malloc (size);
++  efihttp_read (dev, buf, size);
++
++  file->size = size;
++  file->data = buf;
++  file->not_easily_seekable = 0;
++  file->device->net->offset = 0;
++
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_efihttp_close (struct grub_efi_net_device *dev __attribute__ ((unused)),
++		    int prefer_ip6 __attribute__ ((unused)),
++		    grub_file_t file)
++{
++  if (file->data)
++    grub_free (file->data);
++
++  file->data = 0;
++  file->offset = 0;
++  file->size = 0;
++  file->device->net->offset = 0;
++  return GRUB_ERR_NONE;
++}
++
++static grub_ssize_t
++grub_efihttp_read (struct grub_efi_net_device *dev __attribute__((unused)),
++		  int prefer_ip6 __attribute__((unused)),
++		  grub_file_t file,
++		  char *buf,
++		  grub_size_t len)
++{
++  grub_size_t r = len;
++
++  if (!file->data || !buf || !len)
++    return 0;
++
++  if ((file->device->net->offset + len) > file->size)
++    r = file->size - file->device->net->offset;
++
++  if (r)
++    {
++      grub_memcpy (buf, (char *)file->data + file->device->net->offset, r);
++      file->device->net->offset += r;
++    }
++
++  return r;
++}
++
++struct grub_efi_net_io io_http =
++  {
++    .configure = http_configure,
++    .open = grub_efihttp_open,
++    .read = grub_efihttp_read,
++    .close = grub_efihttp_close
++  };
+diff --git a/grub-core/net/efi/ip4_config.c b/grub-core/net/efi/ip4_config.c
+new file mode 100644
+index 00000000000..b711a5d9457
+--- /dev/null
++++ b/grub-core/net/efi/ip4_config.c
+@@ -0,0 +1,398 @@
++
++#include <grub/efi/api.h>
++#include <grub/efi/efi.h>
++#include <grub/misc.h>
++#include <grub/net/efi.h>
++#include <grub/charset.h>
++
++char *
++grub_efi_hw_address_to_string (grub_efi_uint32_t hw_address_size, grub_efi_mac_address_t hw_address)
++{
++  char *hw_addr, *p;
++  int sz, s;
++  int i;
++
++  sz = (int)hw_address_size * (sizeof ("XX:") - 1) + 1;
++
++  hw_addr = grub_malloc (sz);
++  if (!hw_addr)
++    return NULL;
++
++  p = hw_addr;
++  s = sz;
++  for (i = 0; i < (int)hw_address_size; i++)
++    {
++      grub_snprintf (p, sz, "%02x:", hw_address[i]);
++      p +=  sizeof ("XX:") - 1;
++      s -=  sizeof ("XX:") - 1;
++    }
++
++  hw_addr[sz - 2] = '\0';
++  return hw_addr;
++}
++
++char *
++grub_efi_ip4_address_to_string (grub_efi_ipv4_address_t *address)
++{
++  char *addr;
++
++  addr = grub_malloc (sizeof ("XXX.XXX.XXX.XXX"));
++  if (!addr)
++      return NULL;
++
++  /* FIXME: Use grub_xasprintf ? */
++  grub_snprintf (addr,
++	  sizeof ("XXX.XXX.XXX.XXX"),
++	  "%u.%u.%u.%u",
++	  (*address)[0],
++	  (*address)[1],
++	  (*address)[2],
++	  (*address)[3]);
++
++  return addr;
++}
++
++int
++grub_efi_string_to_ip4_address (const char *val, grub_efi_ipv4_address_t *address, const char **rest)
++{
++  grub_uint32_t newip = 0;
++  int i;
++  const char *ptr = val;
++
++  for (i = 0; i < 4; i++)
++    {
++      unsigned long t;
++      t = grub_strtoul (ptr, (char **) &ptr, 0);
++      if (grub_errno)
++	{
++	  grub_errno = GRUB_ERR_NONE;
++	  return 0;
++	}
++      if (*ptr != '.' && i == 0)
++	{
++	  /* XXX: t is in host byte order */
++	  newip = t;
++	  break;
++	}
++      if (t & ~0xff)
++	return 0;
++      newip <<= 8;
++      newip |= t;
++      if (i != 3 && *ptr != '.')
++	return 0;
++      ptr++;
++    }
++
++  newip =  grub_cpu_to_be32 (newip);
++
++  grub_memcpy (address, &newip, sizeof(*address));
++
++  if (rest)
++    *rest = (ptr - 1);
++  return 1;
++}
++
++static grub_efi_ip4_config2_interface_info_t *
++efi_ip4_config_interface_info (grub_efi_ip4_config2_protocol_t *ip4_config)
++{
++  grub_efi_uintn_t sz;
++  grub_efi_status_t status;
++  grub_efi_ip4_config2_interface_info_t *interface_info;
++
++  sz = sizeof (*interface_info) + sizeof (*interface_info->route_table);
++  interface_info = grub_malloc (sz);
++  if (!interface_info)
++    return NULL;
++
++  status = efi_call_4 (ip4_config->get_data, ip4_config,
++		GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO,
++		&sz, interface_info);
++
++  if (status == GRUB_EFI_BUFFER_TOO_SMALL)
++    {
++      grub_free (interface_info);
++      interface_info = grub_malloc (sz);
++      status = efi_call_4 (ip4_config->get_data, ip4_config,
++		    GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO,
++		    &sz, interface_info);
++    }
++
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      grub_free (interface_info);
++      return NULL;
++    }
++
++  return interface_info;
++}
++
++static grub_efi_ip4_config2_manual_address_t *
++efi_ip4_config_manual_address (grub_efi_ip4_config2_protocol_t *ip4_config)
++{
++  grub_efi_uintn_t sz;
++  grub_efi_status_t status;
++  grub_efi_ip4_config2_manual_address_t *manual_address;
++
++  sz = sizeof (*manual_address);
++  manual_address = grub_malloc (sz);
++  if (!manual_address)
++    return NULL;
++
++  status = efi_call_4 (ip4_config->get_data, ip4_config,
++		    GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
++		    &sz, manual_address);
++
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      grub_free (manual_address);
++      return NULL;
++    }
++
++  return manual_address;
++}
++
++char *
++grub_efi_ip4_interface_name (struct grub_efi_net_device *dev)
++{
++  grub_efi_ip4_config2_interface_info_t *interface_info;
++  char *name;
++
++  interface_info = efi_ip4_config_interface_info (dev->ip4_config);
++
++  if (!interface_info)
++    return NULL;
++
++  name = grub_malloc (GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE
++		      * GRUB_MAX_UTF8_PER_UTF16 + 1);
++  *grub_utf16_to_utf8 ((grub_uint8_t *)name, interface_info->name,
++		      GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE) = 0;
++  grub_free (interface_info);
++  return name;
++}
++
++static char *
++grub_efi_ip4_interface_hw_address (struct grub_efi_net_device *dev)
++{
++  grub_efi_ip4_config2_interface_info_t *interface_info;
++  char *hw_addr;
++
++  interface_info = efi_ip4_config_interface_info (dev->ip4_config);
++
++  if (!interface_info)
++    return NULL;
++
++  hw_addr = grub_efi_hw_address_to_string (interface_info->hw_address_size, interface_info->hw_address);
++  grub_free (interface_info);
++
++  return hw_addr;
++}
++
++static char *
++grub_efi_ip4_interface_address (struct grub_efi_net_device *dev)
++{
++  grub_efi_ip4_config2_manual_address_t *manual_address;
++  char *addr;
++
++  manual_address = efi_ip4_config_manual_address (dev->ip4_config);
++
++  if (!manual_address)
++    return NULL;
++
++  addr = grub_efi_ip4_address_to_string (&manual_address->address);
++  grub_free (manual_address);
++  return addr;
++}
++
++
++static int
++address_mask_size (grub_efi_ipv4_address_t *address)
++{
++  grub_uint8_t i;
++  grub_uint32_t u32_addr = grub_be_to_cpu32 (grub_get_unaligned32 (address));
++
++  if (u32_addr == 0)
++    return 0;
++
++  for (i = 0; i < 32 ; ++i)
++    {
++      if (u32_addr == ((0xffffffff >> i) << i))
++	return (32 - i);
++    }
++
++  return -1;
++}
++
++static char **
++grub_efi_ip4_interface_route_table (struct grub_efi_net_device *dev)
++{
++  grub_efi_ip4_config2_interface_info_t *interface_info;
++  char **ret;
++  int i, id;
++
++  interface_info = efi_ip4_config_interface_info (dev->ip4_config);
++  if (!interface_info)
++    return NULL;
++
++  ret = grub_malloc (sizeof (*ret) * (interface_info->route_table_size + 1));
++
++  if (!ret)
++    {
++      grub_free (interface_info);
++      return NULL;
++    }
++
++  id = 0;
++  for (i = 0; i < (int)interface_info->route_table_size; i++)
++    {
++      char *subnet, *gateway, *mask;
++      grub_uint32_t u32_subnet, u32_gateway;
++      int mask_size;
++      grub_efi_ip4_route_table_t *route_table = interface_info->route_table + i;
++      grub_efi_net_interface_t *inf;
++      char *interface_name = NULL;
++
++      for (inf = dev->net_interfaces; inf; inf = inf->next)
++	if (!inf->prefer_ip6)
++	  interface_name = inf->name;
++
++      u32_gateway = grub_get_unaligned32 (&route_table->gateway_address);
++      gateway = grub_efi_ip4_address_to_string (&route_table->gateway_address);
++      u32_subnet = grub_get_unaligned32 (&route_table->subnet_address);
++      subnet = grub_efi_ip4_address_to_string (&route_table->subnet_address);
++      mask_size = address_mask_size (&route_table->subnet_mask);
++      mask = grub_efi_ip4_address_to_string (&route_table->subnet_mask);
++      if (u32_subnet && !u32_gateway && interface_name)
++	ret[id++] = grub_xasprintf ("%s:local %s/%d %s", dev->card_name, subnet, mask_size, interface_name);
++      else if (u32_subnet && u32_gateway)
++	ret[id++] = grub_xasprintf ("%s:gw %s/%d gw %s", dev->card_name, subnet, mask_size, gateway);
++      else if (!u32_subnet && u32_gateway)
++	ret[id++] = grub_xasprintf ("%s:default %s/%d gw %s", dev->card_name, subnet, mask_size, gateway);
++      grub_free (subnet);
++      grub_free (gateway);
++      grub_free (mask);
++    }
++
++  ret[id] = NULL;
++  grub_free (interface_info);
++  return ret;
++}
++
++static grub_efi_net_interface_t *
++grub_efi_ip4_interface_match (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *ip_address)
++{
++  grub_efi_ip4_config2_interface_info_t *interface_info;
++  grub_efi_net_interface_t *inf;
++  int i;
++  grub_efi_ipv4_address_t *address = &ip_address->ip4;
++
++  interface_info = efi_ip4_config_interface_info (dev->ip4_config);
++  if (!interface_info)
++    return NULL;
++
++  for (i = 0; i < (int)interface_info->route_table_size; i++)
++    {
++      grub_efi_ip4_route_table_t *route_table = interface_info->route_table + i;
++      grub_uint32_t u32_address, u32_mask, u32_subnet;
++
++      u32_address = grub_get_unaligned32 (address);
++      u32_subnet = grub_get_unaligned32 (route_table->subnet_address);
++      u32_mask = grub_get_unaligned32 (route_table->subnet_mask);
++
++      /* SKIP Default GATEWAY */
++      if (!u32_subnet && !u32_mask)
++	continue;
++
++      if ((u32_address & u32_mask) == u32_subnet)
++	{
++	  for (inf = dev->net_interfaces; inf; inf = inf->next)
++	    if (!inf->prefer_ip6)
++	      {
++		grub_free (interface_info);
++		return inf;
++	      }
++	}
++    }
++
++  grub_free (interface_info);
++  return NULL;
++}
++
++static int
++grub_efi_ip4_interface_set_manual_address (struct grub_efi_net_device *dev,
++	    grub_efi_net_ip_manual_address_t *net_ip,
++	    int with_subnet)
++{
++  grub_efi_status_t status;
++  grub_efi_ip4_config2_manual_address_t *address = &net_ip->ip4;
++
++  if (!with_subnet)
++    {
++      grub_efi_ip4_config2_manual_address_t *manual_address =
++      efi_ip4_config_manual_address (dev->ip4_config);
++
++      if (manual_address)
++	{
++	  grub_memcpy (address->subnet_mask, manual_address->subnet_mask, sizeof(address->subnet_mask));
++	  grub_free (manual_address);
++	}
++      else
++	{
++	  /* XXX: */
++	  address->subnet_mask[0] = 0xff;
++	  address->subnet_mask[1] = 0xff;
++	  address->subnet_mask[2] = 0xff;
++	  address->subnet_mask[3] = 0;
++	}
++    }
++
++  status = efi_call_4 (dev->ip4_config->set_data, dev->ip4_config,
++		    GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
++		    sizeof(*address), address);
++
++  if (status != GRUB_EFI_SUCCESS)
++    return 0;
++
++  return 1;
++}
++
++static int
++grub_efi_ip4_interface_set_gateway (struct grub_efi_net_device *dev,
++	      grub_efi_net_ip_address_t *address)
++{
++  grub_efi_status_t status;
++
++  status = efi_call_4 (dev->ip4_config->set_data, dev->ip4_config,
++		GRUB_EFI_IP4_CONFIG2_DATA_TYPE_GATEWAY,
++		sizeof (address->ip4), &address->ip4);
++
++  if (status != GRUB_EFI_SUCCESS)
++    return 0;
++  return 1;
++}
++
++/* FIXME: Multiple DNS */
++static int
++grub_efi_ip4_interface_set_dns (struct grub_efi_net_device *dev,
++	      grub_efi_net_ip_address_t *address)
++{
++  grub_efi_status_t status;
++
++  status = efi_call_4 (dev->ip4_config->set_data, dev->ip4_config,
++		GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER,
++		sizeof (address->ip4), &address->ip4);
++
++  if (status != GRUB_EFI_SUCCESS)
++    return 0;
++  return 1;
++}
++
++grub_efi_net_ip_config_t *efi_net_ip4_config = &(grub_efi_net_ip_config_t)
++  {
++    .get_hw_address = grub_efi_ip4_interface_hw_address,
++    .get_address = grub_efi_ip4_interface_address,
++    .get_route_table = grub_efi_ip4_interface_route_table,
++    .best_interface = grub_efi_ip4_interface_match,
++    .set_address = grub_efi_ip4_interface_set_manual_address,
++    .set_gateway = grub_efi_ip4_interface_set_gateway,
++    .set_dns = grub_efi_ip4_interface_set_dns
++  };
+diff --git a/grub-core/net/efi/ip6_config.c b/grub-core/net/efi/ip6_config.c
+new file mode 100644
+index 00000000000..017c4d05bc7
+--- /dev/null
++++ b/grub-core/net/efi/ip6_config.c
+@@ -0,0 +1,422 @@
++#include <grub/efi/api.h>
++#include <grub/efi/efi.h>
++#include <grub/misc.h>
++#include <grub/net/efi.h>
++#include <grub/charset.h>
++
++char *
++grub_efi_ip6_address_to_string (grub_efi_pxe_ipv6_address_t *address)
++{
++  char *str = grub_malloc (sizeof ("XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX"));
++  char *p;
++  int i;
++  int squash;
++
++  if (!str)
++    return NULL;
++
++  p = str;
++  squash = 0;
++  for (i = 0; i < 8; ++i)
++    {
++      grub_uint16_t addr;
++
++      if (i == 7)
++	squash = 2;
++
++      addr = grub_get_unaligned16 (address->addr + i * 2);
++
++      if (grub_be_to_cpu16 (addr))
++	{
++	  char buf[sizeof ("XXXX")];
++	  if (i > 0)
++	    *p++ = ':';
++	  grub_snprintf (buf, sizeof (buf), "%x", grub_be_to_cpu16 (addr));
++	  grub_strcpy (p, buf);
++	  p += grub_strlen (buf);
++
++	  if (squash == 1)
++	    squash = 2;
++	}
++      else
++	{
++	  if (squash == 0)
++	    {
++	      *p++ = ':';
++	      squash = 1;
++	    }
++	  else if (squash == 2)
++	    {
++	      *p++ = ':';
++	      *p++ = '0';
++	    }
++	}
++    }
++  *p = '\0';
++  return str;
++}
++
++int
++grub_efi_string_to_ip6_address (const char *val, grub_efi_ipv6_address_t *address, const char **rest)
++{
++  grub_uint16_t newip[8];
++  const char *ptr = val;
++  int word, quaddot = -1;
++  int bracketed = 0;
++
++  if (ptr[0] == '[') {
++    bracketed = 1;
++    ptr++;
++  }
++
++  if (ptr[0] == ':' && ptr[1] != ':')
++    return 0;
++  if (ptr[0] == ':')
++    ptr++;
++
++  for (word = 0; word < 8; word++)
++    {
++      unsigned long t;
++      if (*ptr == ':')
++	{
++	  quaddot = word;
++	  word--;
++	  ptr++;
++	  continue;
++	}
++      t = grub_strtoul (ptr, (char **) &ptr, 16);
++      if (grub_errno)
++	{
++	  grub_errno = GRUB_ERR_NONE;
++	  break;
++	}
++      if (t & ~0xffff)
++	return 0;
++      newip[word] = grub_cpu_to_be16 (t);
++      if (*ptr != ':')
++	break;
++      ptr++;
++    }
++  if (quaddot == -1 && word < 7)
++    return 0;
++  if (quaddot != -1)
++    {
++      grub_memmove (&newip[quaddot + 7 - word], &newip[quaddot],
++		    (word - quaddot + 1) * sizeof (newip[0]));
++      grub_memset (&newip[quaddot], 0, (7 - word) * sizeof (newip[0]));
++    }
++  grub_memcpy (address, newip, 16);
++  if (bracketed && *ptr == ']') {
++    ptr++;
++  }
++  if (rest)
++    *rest = ptr;
++  return 1;
++}
++
++static grub_efi_ip6_config_interface_info_t *
++efi_ip6_config_interface_info (grub_efi_ip6_config_protocol_t *ip6_config)
++{
++  grub_efi_uintn_t sz;
++  grub_efi_status_t status;
++  grub_efi_ip6_config_interface_info_t *interface_info;
++
++  sz = sizeof (*interface_info) + sizeof (*interface_info->route_table);
++  interface_info = grub_malloc (sz);
++
++  status = efi_call_4 (ip6_config->get_data, ip6_config,
++		GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO,
++		&sz, interface_info);
++
++  if (status == GRUB_EFI_BUFFER_TOO_SMALL)
++    {
++      grub_free (interface_info);
++      interface_info = grub_malloc (sz);
++      status = efi_call_4 (ip6_config->get_data, ip6_config,
++		    GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO,
++		    &sz, interface_info);
++    }
++
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      grub_free (interface_info);
++      return NULL;
++    }
++
++  return interface_info;
++}
++
++static grub_efi_ip6_config_manual_address_t *
++efi_ip6_config_manual_address (grub_efi_ip6_config_protocol_t *ip6_config)
++{
++  grub_efi_uintn_t sz;
++  grub_efi_status_t status;
++  grub_efi_ip6_config_manual_address_t *manual_address;
++
++  sz = sizeof (*manual_address);
++  manual_address = grub_malloc (sz);
++  if (!manual_address)
++    return NULL;
++
++  status = efi_call_4 (ip6_config->get_data, ip6_config,
++		    GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS,
++		    &sz, manual_address);
++
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      grub_free (manual_address);
++      return NULL;
++    }
++
++  return manual_address;
++}
++
++char *
++grub_efi_ip6_interface_name (struct grub_efi_net_device *dev)
++{
++  grub_efi_ip6_config_interface_info_t *interface_info;
++  char *name;
++
++  interface_info = efi_ip6_config_interface_info (dev->ip6_config);
++
++  if (!interface_info)
++    return NULL;
++
++  name = grub_malloc (GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE
++		      * GRUB_MAX_UTF8_PER_UTF16 + 1);
++  *grub_utf16_to_utf8 ((grub_uint8_t *)name, interface_info->name,
++		      GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE) = 0;
++  grub_free (interface_info);
++  return name;
++}
++
++static char *
++grub_efi_ip6_interface_hw_address (struct grub_efi_net_device *dev)
++{
++  grub_efi_ip6_config_interface_info_t *interface_info;
++  char *hw_addr;
++
++  interface_info = efi_ip6_config_interface_info (dev->ip6_config);
++
++  if (!interface_info)
++    return NULL;
++
++  hw_addr = grub_efi_hw_address_to_string (interface_info->hw_address_size, interface_info->hw_address);
++  grub_free (interface_info);
++
++  return hw_addr;
++}
++
++static char *
++grub_efi_ip6_interface_address (struct grub_efi_net_device *dev)
++{
++  grub_efi_ip6_config_manual_address_t *manual_address;
++  char *addr;
++
++  manual_address = efi_ip6_config_manual_address (dev->ip6_config);
++
++  if (!manual_address)
++    return NULL;
++
++  addr = grub_efi_ip6_address_to_string ((grub_efi_pxe_ipv6_address_t *)&manual_address->address);
++  grub_free (manual_address);
++  return addr;
++}
++
++static char **
++grub_efi_ip6_interface_route_table (struct grub_efi_net_device *dev)
++{
++  grub_efi_ip6_config_interface_info_t *interface_info;
++  char **ret;
++  int i, id;
++
++  interface_info = efi_ip6_config_interface_info (dev->ip6_config);
++  if (!interface_info)
++    return NULL;
++
++  ret = grub_malloc (sizeof (*ret) * (interface_info->route_count + 1));
++
++  if (!ret)
++    {
++      grub_free (interface_info);
++      return NULL;
++    }
++
++  id = 0;
++  for (i = 0; i < (int)interface_info->route_count ; i++)
++    {
++      char *gateway, *destination;
++      grub_uint64_t u64_gateway[2];
++      grub_uint64_t u64_destination[2];
++      grub_efi_ip6_route_table_t *route_table = interface_info->route_table + i;
++      grub_efi_net_interface_t *inf;
++      char *interface_name = NULL;
++
++      gateway = grub_efi_ip6_address_to_string (&route_table->gateway);
++      destination = grub_efi_ip6_address_to_string (&route_table->destination);
++
++      u64_gateway[0] = grub_get_unaligned64 (route_table->gateway.addr);
++      u64_gateway[1] = grub_get_unaligned64 (route_table->gateway.addr + 8);
++      u64_destination[0] = grub_get_unaligned64 (route_table->destination.addr);
++      u64_destination[1] = grub_get_unaligned64 (route_table->destination.addr + 8);
++
++      for (inf = dev->net_interfaces; inf; inf = inf->next)
++	if (inf->prefer_ip6)
++	  interface_name = inf->name;
++
++      if ((!u64_gateway[0] && !u64_gateway[1])
++	  && (u64_destination[0] || u64_destination[1]))
++	{
++	  if (interface_name)
++	    {
++	      if ((grub_be_to_cpu64 (u64_destination[0]) == 0xfe80000000000000ULL)
++	      && (!u64_destination[1])
++	      && (route_table->prefix_length == 64))
++		ret[id++] = grub_xasprintf ("%s:link %s/%d %s", dev->card_name, destination, route_table->prefix_length, interface_name);
++	      else
++		ret[id++] = grub_xasprintf ("%s:local %s/%d %s", dev->card_name, destination, route_table->prefix_length, interface_name);
++	    }
++	}
++      else if ((u64_gateway[0] || u64_gateway[1])
++	  && (u64_destination[0] || u64_destination[1]))
++	ret[id++] = grub_xasprintf ("%s:gw %s/%d gw %s", dev->card_name, destination, route_table->prefix_length, gateway);
++      else if ((u64_gateway[0] || u64_gateway[1])
++	  && (!u64_destination[0] && !u64_destination[1]))
++	ret[id++] = grub_xasprintf ("%s:default %s/%d gw %s", dev->card_name, destination, route_table->prefix_length, gateway);
++
++      grub_free (gateway);
++      grub_free (destination);
++    }
++
++  ret[id] = NULL;
++  grub_free (interface_info);
++  return ret;
++}
++
++static grub_efi_net_interface_t *
++grub_efi_ip6_interface_match (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *ip_address)
++{
++  grub_efi_ip6_config_interface_info_t *interface_info;
++  grub_efi_net_interface_t *inf;
++  int i;
++  grub_efi_ipv6_address_t *address = &ip_address->ip6;
++
++  interface_info = efi_ip6_config_interface_info (dev->ip6_config);
++  if (!interface_info)
++    return NULL;
++
++  for (i = 0; i < (int)interface_info->route_count ; i++)
++    {
++      grub_uint64_t u64_addr[2];
++      grub_uint64_t u64_subnet[2];
++      grub_uint64_t u64_mask[2];
++
++      grub_efi_ip6_route_table_t *route_table = interface_info->route_table + i;
++
++      /* SKIP Default GATEWAY */
++      if (route_table->prefix_length == 0)
++	continue;
++
++      u64_addr[0] = grub_get_unaligned64 (address);
++      u64_addr[1] = grub_get_unaligned64 (address + 4);
++      u64_subnet[0] = grub_get_unaligned64 (route_table->destination.addr);
++      u64_subnet[1] = grub_get_unaligned64 (route_table->destination.addr + 8);
++      u64_mask[0] = (route_table->prefix_length <= 64) ?
++	    0xffffffffffffffffULL << (64 - route_table->prefix_length) :
++	    0xffffffffffffffffULL;
++      u64_mask[1] = (route_table->prefix_length <= 64) ?
++	    0 :
++	    0xffffffffffffffffULL << (128 - route_table->prefix_length);
++
++      if (((u64_addr[0] & u64_mask[0]) == u64_subnet[0])
++	  && ((u64_addr[1] & u64_mask[1]) == u64_subnet[1]))
++	{
++	  for (inf = dev->net_interfaces; inf; inf = inf->next)
++	    if (inf->prefer_ip6)
++	      {
++		grub_free (interface_info);
++		return inf;
++	      }
++	}
++    }
++
++  grub_free (interface_info);
++  return NULL;
++}
++
++static int
++grub_efi_ip6_interface_set_manual_address (struct grub_efi_net_device *dev,
++	    grub_efi_net_ip_manual_address_t *net_ip,
++	    int with_subnet)
++{
++  grub_efi_status_t status;
++  grub_efi_ip6_config_manual_address_t *address = &net_ip->ip6;
++
++  if (!with_subnet)
++    {
++      grub_efi_ip6_config_manual_address_t *manual_address =
++      efi_ip6_config_manual_address (dev->ip6_config);
++
++      if (manual_address)
++	{
++	  address->prefix_length = manual_address->prefix_length;
++	  grub_free (manual_address);
++	}
++      else
++	{
++	  /* XXX: */
++	  address->prefix_length = 64;
++	}
++    }
++
++  status = efi_call_4 (dev->ip6_config->set_data, dev->ip6_config,
++		    GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS,
++		    sizeof(*address), address);
++
++  if (status != GRUB_EFI_SUCCESS)
++    return 0;
++
++  return 1;
++}
++
++static int
++grub_efi_ip6_interface_set_gateway (struct grub_efi_net_device *dev,
++	      grub_efi_net_ip_address_t *address)
++{
++  grub_efi_status_t status;
++
++  status = efi_call_4 (dev->ip6_config->set_data, dev->ip6_config,
++		GRUB_EFI_IP6_CONFIG_DATA_TYPE_GATEWAY,
++		sizeof (address->ip6), &address->ip6);
++
++  if (status != GRUB_EFI_SUCCESS)
++    return 0;
++  return 1;
++}
++
++static int
++grub_efi_ip6_interface_set_dns (struct grub_efi_net_device *dev,
++	      grub_efi_net_ip_address_t *address)
++{
++
++  grub_efi_status_t status;
++
++  status = efi_call_4 (dev->ip6_config->set_data, dev->ip6_config,
++		GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER,
++		sizeof (address->ip6), &address->ip6);
++
++  if (status != GRUB_EFI_SUCCESS)
++    return 0;
++  return 1;
++}
++
++grub_efi_net_ip_config_t *efi_net_ip6_config = &(grub_efi_net_ip_config_t)
++  {
++    .get_hw_address = grub_efi_ip6_interface_hw_address,
++    .get_address = grub_efi_ip6_interface_address,
++    .get_route_table = grub_efi_ip6_interface_route_table,
++    .best_interface = grub_efi_ip6_interface_match,
++    .set_address = grub_efi_ip6_interface_set_manual_address,
++    .set_gateway = grub_efi_ip6_interface_set_gateway,
++    .set_dns = grub_efi_ip6_interface_set_dns
++  };
+diff --git a/grub-core/net/efi/net.c b/grub-core/net/efi/net.c
+new file mode 100644
+index 00000000000..86bce6535d3
+--- /dev/null
++++ b/grub-core/net/efi/net.c
+@@ -0,0 +1,1428 @@
++#include <grub/net.h>
++#include <grub/env.h>
++#include <grub/mm.h>
++#include <grub/misc.h>
++#include <grub/dl.h>
++#include <grub/command.h>
++#include <grub/efi/api.h>
++#include <grub/efi/efi.h>
++#include <grub/i18n.h>
++#include <grub/bufio.h>
++#include <grub/efi/http.h>
++#include <grub/efi/dhcp.h>
++#include <grub/net/efi.h>
++#include <grub/charset.h>
++
++GRUB_MOD_LICENSE ("GPLv3+");
++
++#define GRUB_EFI_IP6_PREFIX_LENGTH 64
++
++static grub_efi_guid_t ip4_config_guid = GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID;
++static grub_efi_guid_t ip6_config_guid = GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID;
++static grub_efi_guid_t http_service_binding_guid = GRUB_EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID;
++static grub_efi_guid_t http_guid = GRUB_EFI_HTTP_PROTOCOL_GUID;
++static grub_efi_guid_t pxe_io_guid = GRUB_EFI_PXE_GUID;
++static grub_efi_guid_t dhcp4_service_binding_guid = GRUB_EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID;
++static grub_efi_guid_t dhcp4_guid = GRUB_EFI_DHCP4_PROTOCOL_GUID;
++static grub_efi_guid_t dhcp6_service_binding_guid = GRUB_EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID;
++static grub_efi_guid_t dhcp6_guid = GRUB_EFI_DHCP6_PROTOCOL_GUID;
++
++struct grub_efi_net_device *net_devices;
++
++static char *default_server;
++static grub_efi_net_interface_t *net_interface;
++static grub_efi_net_interface_t *net_default_interface;
++
++#define efi_net_interface_configure(inf) inf->io->configure (inf->dev, inf->prefer_ip6)
++#define efi_net_interface_open(inf, file, name) inf->io->open (inf->dev, inf->prefer_ip6, file, name, inf->io_type)
++#define efi_net_interface_read(inf, file, buf, sz) inf->io->read (inf->dev, inf->prefer_ip6, file, buf, sz)
++#define efi_net_interface_close(inf, file) inf->io->close (inf->dev, inf->prefer_ip6, file)
++#define efi_net_interface(m,...) efi_net_interface_ ## m (net_interface, ## __VA_ARGS__)
++
++static grub_efi_handle_t
++grub_efi_locate_device_path (grub_efi_guid_t *protocol, grub_efi_device_path_t *device_path,
++                            grub_efi_device_path_t **r_device_path)
++{
++  grub_efi_handle_t handle;
++  grub_efi_status_t status;
++
++  status = efi_call_3 (grub_efi_system_table->boot_services->locate_device_path,
++                      protocol, &device_path, &handle);
++
++  if (status != GRUB_EFI_SUCCESS)
++    return 0;
++
++  if (r_device_path)
++    *r_device_path = device_path;
++
++  return handle;
++}
++
++static int
++url_parse_fields (const char *url, char **proto, char **host, char **path)
++{
++  const char *p, *ps;
++  grub_size_t l;
++
++  *proto = *host = *path = NULL;
++  ps = p = url;
++
++  while ((p = grub_strchr (p, ':')))
++    {
++      if (grub_strlen (p) < sizeof ("://") - 1)
++	break;
++      if (grub_memcmp (p, "://", sizeof ("://") - 1) == 0)
++	{
++	  l = p - ps;
++	  *proto = grub_malloc (l + 1);
++	  if (!*proto)
++	    {
++	      grub_print_error ();
++	      return 0;
++	    }
++
++	  grub_memcpy (*proto, ps, l);
++	  (*proto)[l] = '\0';
++	  p +=  sizeof ("://") - 1;
++	  break;
++	}
++      ++p;
++    }
++
++  if (!*proto)
++    {
++      grub_dprintf ("bootp", "url: %s is not valid, protocol not found\n", url);
++      return 0;
++    }
++
++  ps = p;
++  p = grub_strchr (p, '/');
++
++  if (!p)
++    {
++      grub_dprintf ("bootp", "url: %s is not valid, host/path not found\n", url);
++      grub_free (*proto);
++      *proto = NULL;
++      return 0;
++    }
++
++  l = p - ps;
++
++  if (l > 2 && ps[0] == '[' && ps[l - 1] == ']')
++    {
++      *host = grub_malloc (l - 1);
++      if (!*host)
++	{
++	  grub_print_error ();
++	  grub_free (*proto);
++	  *proto = NULL;
++	  return 0;
++	}
++      grub_memcpy (*host, ps + 1, l - 2);
++      (*host)[l - 2] = 0;
++    }
++  else
++    {
++      *host = grub_malloc (l + 1);
++      if (!*host)
++	{
++	  grub_print_error ();
++	  grub_free (*proto);
++	  *proto = NULL;
++	  return 0;
++	}
++      grub_memcpy (*host, ps, l);
++      (*host)[l] = 0;
++    }
++
++  *path = grub_strdup (p);
++  if (!*path)
++    {
++      grub_print_error ();
++      grub_free (*host);
++      grub_free (*proto);
++      *host = NULL;
++      *proto = NULL;
++      return 0;
++    }
++  return 1;
++}
++
++static void
++url_get_boot_location (const char *url, char **device, char **path, int is_default)
++{
++  char *protocol, *server, *file;
++  char *slash;
++
++  if (!url_parse_fields (url, &protocol, &server, &file))
++    return;
++
++  if ((slash = grub_strrchr (file, '/')))
++    *slash = 0;
++  else
++    *file = 0;
++
++  *device = grub_xasprintf ("%s,%s", protocol, server);
++  *path = grub_strdup(file);
++
++  if (is_default)
++    default_server = server;
++  else
++    grub_free (server);
++
++  grub_free (protocol);
++  grub_free (file);
++}
++
++static void
++pxe_get_boot_location (const struct grub_net_bootp_packet *bp,
++		  char **device,
++		  char **path,
++		  int is_default)
++{
++  char *server = grub_xasprintf ("%d.%d.%d.%d",
++	     ((grub_uint8_t *) &bp->server_ip)[0],
++	     ((grub_uint8_t *) &bp->server_ip)[1],
++	     ((grub_uint8_t *) &bp->server_ip)[2],
++	     ((grub_uint8_t *) &bp->server_ip)[3]);
++
++  *device = grub_xasprintf ("tftp,%s", server);
++
++  *path = grub_strndup (bp->boot_file, sizeof (bp->boot_file));
++
++  if (*path)
++    {
++      char *slash;
++      slash = grub_strrchr (*path, '/');
++      if (slash)
++	*slash = 0;
++      else
++	**path = 0;
++    }
++
++  if (is_default)
++    default_server = server;
++  else
++    grub_free (server);
++}
++
++static void
++pxe_get_boot_location_v6 (const struct grub_net_dhcp6_packet *dp,
++		  grub_size_t dhcp_size,
++		  char **device,
++		  char **path)
++{
++
++  struct grub_net_dhcp6_option *dhcp_opt;
++  grub_size_t dhcp_remain_size;
++  *device = *path = 0;
++
++  if (dhcp_size < sizeof (*dp))
++    {
++      grub_error (GRUB_ERR_OUT_OF_RANGE, N_("DHCPv6 packet size too small"));
++      return;
++    }
++
++  dhcp_remain_size = dhcp_size - sizeof (*dp);
++  dhcp_opt = (struct grub_net_dhcp6_option *)dp->dhcp_options;
++
++  while (dhcp_remain_size)
++    {
++      grub_uint16_t code = grub_be_to_cpu16 (dhcp_opt->code);
++      grub_uint16_t len = grub_be_to_cpu16 (dhcp_opt->len);
++      grub_uint16_t option_size = sizeof (*dhcp_opt) + len;
++
++      if (dhcp_remain_size < option_size || code == 0)
++	break;
++
++      if (code == GRUB_NET_DHCP6_OPTION_BOOTFILE_URL)
++	{
++	  char *url = grub_malloc (len + 1);
++
++	  grub_memcpy (url, dhcp_opt->data, len);
++	  url[len] = 0;
++
++	  url_get_boot_location ((const char *)url, device, path, 1);
++	  grub_free (url);
++	  break;
++	}
++
++      dhcp_remain_size -= option_size;
++      dhcp_opt = (struct grub_net_dhcp6_option *)((grub_uint8_t *)dhcp_opt + option_size);
++    }
++}
++
++static grub_efi_net_interface_t *
++grub_efi_net_config_from_device_path (grub_efi_device_path_t *dp,
++		  struct grub_efi_net_device *netdev,
++		  char **device,
++		  char **path)
++{
++  grub_efi_net_interface_t *inf = NULL;
++
++  while (1)
++    {
++      grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
++      grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
++      grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp);
++
++      if (type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE)
++	{
++	  if (subtype == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)
++	    {
++	      grub_efi_uri_device_path_t *uri_dp;
++	      uri_dp = (grub_efi_uri_device_path_t *) dp;
++	      /* Beware that uri_dp->uri may not be null terminated */
++	      url_get_boot_location ((const char *)uri_dp->uri, device, path, 1);
++	    }
++	  else if (subtype == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE)
++	    {
++	      grub_efi_net_ip_manual_address_t net_ip;
++	      grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) dp;
++
++	      if (inf)
++		continue;
++	      grub_memcpy (net_ip.ip4.address, ipv4->local_ip_address, sizeof (net_ip.ip4.address));
++	      grub_memcpy (net_ip.ip4.subnet_mask, ipv4->subnet_mask, sizeof (net_ip.ip4.subnet_mask));
++	      net_ip.is_ip6 = 0;
++	      inf = grub_efi_net_create_interface (netdev,
++			    netdev->card_name,
++			    &net_ip,
++			    1);
++	    }
++	  else if (subtype == GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)
++	    {
++	      grub_efi_net_ip_manual_address_t net_ip;
++	      grub_efi_ipv6_device_path_t *ipv6 = (grub_efi_ipv6_device_path_t *) dp;
++
++	      if (inf)
++		continue;
++	      grub_memcpy (net_ip.ip6.address, ipv6->local_ip_address, sizeof (net_ip.ip6.address));
++	      net_ip.ip6.prefix_length = GRUB_EFI_IP6_PREFIX_LENGTH;
++	      net_ip.ip6.is_anycast = 0;
++	      net_ip.is_ip6 = 1;
++	      inf = grub_efi_net_create_interface (netdev,
++			    netdev->card_name,
++			    &net_ip,
++			    1);
++	    }
++	}
++
++      if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp))
++        break;
++      dp = (grub_efi_device_path_t *) ((char *) dp + len);
++    }
++
++  return inf;
++}
++
++static grub_efi_net_interface_t *
++grub_efi_net_config_from_handle (grub_efi_handle_t *hnd,
++		  struct grub_efi_net_device *netdev,
++		  char **device,
++		  char **path)
++{
++  grub_efi_pxe_t *pxe = NULL;
++
++  if (hnd == netdev->ip4_pxe_handle)
++    pxe = netdev->ip4_pxe;
++  else if (hnd == netdev->ip6_pxe_handle)
++    pxe = netdev->ip6_pxe;
++
++  if (!pxe)
++    return (grub_efi_net_config_from_device_path (
++		grub_efi_get_device_path (hnd),
++		netdev,
++		device,
++		path));
++
++  if (pxe->mode->using_ipv6)
++    {
++      grub_efi_net_ip_manual_address_t net_ip;
++
++      pxe_get_boot_location_v6 (
++	    (const struct grub_net_dhcp6_packet *) &pxe->mode->dhcp_ack,
++	    sizeof (pxe->mode->dhcp_ack),
++	    device,
++	    path);
++
++      grub_memcpy (net_ip.ip6.address, pxe->mode->station_ip.v6, sizeof(net_ip.ip6.address));
++      net_ip.ip6.prefix_length = GRUB_EFI_IP6_PREFIX_LENGTH;
++      net_ip.ip6.is_anycast = 0;
++      net_ip.is_ip6 = 1;
++      return (grub_efi_net_create_interface (netdev,
++		    netdev->card_name,
++		    &net_ip,
++		    1));
++    }
++  else
++    {
++      grub_efi_net_ip_manual_address_t net_ip;
++
++      pxe_get_boot_location (
++		(const struct grub_net_bootp_packet *) &pxe->mode->dhcp_ack,
++		device,
++		path,
++		1);
++
++      grub_memcpy (net_ip.ip4.address, pxe->mode->station_ip.v4, sizeof (net_ip.ip4.address));
++      grub_memcpy (net_ip.ip4.subnet_mask, pxe->mode->subnet_mask.v4, sizeof (net_ip.ip4.subnet_mask));
++      net_ip.is_ip6 = 0;
++      return (grub_efi_net_create_interface (netdev,
++		    netdev->card_name,
++		    &net_ip,
++		    1));
++    }
++}
++
++static const char *
++grub_efi_net_var_get_address (struct grub_env_var *var,
++                   const char *val __attribute__ ((unused)))
++{
++  struct grub_efi_net_device *dev;
++
++  for (dev = net_devices; dev; dev = dev->next)
++    {
++      grub_efi_net_interface_t *inf;
++
++      for (inf = dev->net_interfaces; inf; inf = inf->next)
++	{
++	  char *var_name;
++
++	  var_name = grub_xasprintf ("net_%s_ip", inf->name);
++	  if (grub_strcmp (var_name, var->name) == 0)
++	    return efi_net_interface_get_address (inf);
++	  grub_free (var_name);
++	  var_name = grub_xasprintf ("net_%s_mac", inf->name);
++	  if (grub_strcmp (var_name, var->name) == 0)
++	    return efi_net_interface_get_hw_address (inf);
++	  grub_free (var_name);
++	}
++    }
++
++  return NULL;
++}
++
++static char *
++grub_efi_net_var_set_interface (struct grub_env_var *var __attribute__ ((unused)),
++		   const char *val)
++{
++  struct grub_efi_net_device *dev;
++  grub_efi_net_interface_t *inf;
++
++  for (dev = net_devices; dev; dev = dev->next)
++    for (inf = dev->net_interfaces; inf; inf = inf->next)
++      if (grub_strcmp (inf->name, val) == 0)
++	{
++	  net_default_interface = inf;
++	  return grub_strdup (val);
++	}
++
++  return NULL;
++}
++
++static char *
++grub_efi_net_var_set_server (struct grub_env_var *var __attribute__ ((unused)),
++		   const char *val)
++{
++  grub_free (default_server);
++  default_server = grub_strdup (val);
++  return grub_strdup (val);
++}
++
++static const char *
++grub_efi_net_var_get_server (struct grub_env_var *var __attribute__ ((unused)),
++		   const char *val __attribute__ ((unused)))
++{
++  return default_server ? : "";
++}
++
++static const char *
++grub_efi_net_var_get_ip (struct grub_env_var *var __attribute__ ((unused)),
++	       const char *val __attribute__ ((unused)))
++{
++  const char *intf = grub_env_get ("net_default_interface");
++  const char *ret = NULL;
++  if (intf)
++    {
++      char *buf = grub_xasprintf ("net_%s_ip", intf);
++      if (buf)
++	ret = grub_env_get (buf);
++      grub_free (buf);
++    }
++  return ret;
++}
++
++static const char *
++grub_efi_net_var_get_mac (struct grub_env_var *var __attribute__ ((unused)),
++	       const char *val __attribute__ ((unused)))
++{
++  const char *intf = grub_env_get ("net_default_interface");
++  const char *ret = NULL;
++  if (intf)
++    {
++      char *buf = grub_xasprintf ("net_%s_mac", intf);
++      if (buf)
++	ret = grub_env_get (buf);
++      grub_free (buf);
++    }
++  return ret;
++}
++
++static void
++grub_efi_net_export_interface_vars (void)
++{
++  struct grub_efi_net_device *dev;
++
++  for (dev = net_devices; dev; dev = dev->next)
++    {
++      grub_efi_net_interface_t *inf;
++
++      for (inf = dev->net_interfaces; inf; inf = inf->next)
++	{
++	  char *var;
++
++	  var = grub_xasprintf ("net_%s_ip", inf->name);
++	  grub_register_variable_hook (var, grub_efi_net_var_get_address, 0);
++	  grub_env_export (var);
++	  grub_free (var);
++	  var = grub_xasprintf ("net_%s_mac", inf->name);
++	  grub_register_variable_hook (var, grub_efi_net_var_get_address, 0);
++	  grub_env_export (var);
++	  grub_free (var);
++	}
++    }
++}
++
++static void
++grub_efi_net_unset_interface_vars (void)
++{
++  struct grub_efi_net_device *dev;
++
++  for (dev = net_devices; dev; dev = dev->next)
++    {
++      grub_efi_net_interface_t *inf;
++
++      for (inf = dev->net_interfaces; inf; inf = inf->next)
++	{
++	  char *var;
++
++	  var = grub_xasprintf ("net_%s_ip", inf->name);
++	  grub_register_variable_hook (var, 0, 0);
++	  grub_env_unset (var);
++	  grub_free (var);
++	  var = grub_xasprintf ("net_%s_mac", inf->name);
++	  grub_register_variable_hook (var, 0, 0);
++	  grub_env_unset (var);
++	  grub_free (var);
++	}
++    }
++}
++
++grub_efi_net_interface_t *
++grub_efi_net_create_interface (struct grub_efi_net_device *dev,
++		const char *interface_name,
++		grub_efi_net_ip_manual_address_t *net_ip,
++		int has_subnet)
++{
++  grub_efi_net_interface_t *inf;
++
++  for (inf = dev->net_interfaces; inf; inf = inf->next)
++    {
++      if (inf->prefer_ip6 == net_ip->is_ip6)
++	break;
++    }
++
++  if (!inf)
++    {
++      inf = grub_malloc (sizeof(*inf));
++      inf->name = grub_strdup (interface_name);
++      inf->prefer_ip6 = net_ip->is_ip6;
++      inf->dev = dev;
++      inf->next = dev->net_interfaces;
++      inf->ip_config = (net_ip->is_ip6) ? efi_net_ip6_config : efi_net_ip4_config ;
++      dev->net_interfaces = inf;
++    }
++  else
++    {
++      grub_free (inf->name);
++      inf->name = grub_strdup (interface_name);
++    }
++
++  if (!efi_net_interface_set_address (inf, net_ip, has_subnet))
++    {
++      grub_error (GRUB_ERR_BUG, N_("Set Address Failed"));
++      return NULL;
++    }
++
++  return inf;
++}
++
++static void
++grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
++			  char **path)
++{
++  grub_efi_handle_t config_hnd;
++
++  struct grub_efi_net_device *netdev;
++  grub_efi_net_interface_t *inf;
++
++  config_hnd = grub_efi_locate_device_path (&ip4_config_guid, grub_efi_get_device_path (hnd), NULL);
++
++  if (!config_hnd)
++    return;
++
++  for (netdev = net_devices; netdev; netdev = netdev->next)
++    if (netdev->handle == config_hnd)
++      break;
++
++  if (!netdev)
++    return;
++
++  if (!(inf = grub_efi_net_config_from_handle (hnd, netdev, device, path)))
++    return;
++
++  grub_env_set ("net_default_interface", inf->name);
++  grub_efi_net_export_interface_vars ();
++}
++
++static grub_err_t
++grub_efi_netfs_dir (grub_device_t device, const char *path __attribute__ ((unused)),
++		 grub_fs_dir_hook_t hook __attribute__ ((unused)),
++		 void *hook_data __attribute__ ((unused)))
++{
++  if (!device->net)
++    return grub_error (GRUB_ERR_BUG, "invalid net device");
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_efi_netfs_open (struct grub_file *file_out __attribute__ ((unused)),
++		  const char *name __attribute__ ((unused)))
++{
++  struct grub_file *file, *bufio;
++
++  file = grub_malloc (sizeof (*file));
++  if (!file)
++    return grub_errno;
++
++  grub_memcpy (file, file_out, sizeof (struct grub_file));
++  file->device->net->name = grub_strdup (name);
++
++  if (!file->device->net->name)
++    {
++      grub_free (file);
++      return grub_errno;
++    }
++
++  efi_net_interface(open, file, name);
++  grub_print_error ();
++
++  bufio = grub_bufio_open (file, 32768);
++  if (!bufio)
++    {
++      grub_free (file->device->net->name);
++      grub_free (file);
++      return grub_errno;
++    }
++  grub_memcpy (file_out, bufio, sizeof (struct grub_file));
++  grub_free (bufio);
++
++  return GRUB_ERR_NONE;
++}
++
++static grub_ssize_t
++grub_efihttp_chunk_read (grub_file_t file, char *buf,
++			grub_size_t len, grub_size_t chunk_size)
++{
++  char *chunk = grub_malloc (chunk_size);
++  grub_size_t sum = 0;
++
++  while (len)
++    {
++      grub_ssize_t rd;
++      grub_size_t sz = (len > chunk_size) ? chunk_size : len;
++
++      rd = efi_net_interface (read, file, chunk, sz);
++
++      if (rd <= 0)
++	return rd;
++
++      if (buf)
++	{
++	  grub_memcpy (buf, chunk, rd);
++	  buf += rd;
++	}
++      sum += rd;
++      len -= rd;
++    }
++
++  grub_free (chunk);
++  return sum;
++}
++
++static grub_ssize_t
++grub_efi_netfs_read (grub_file_t file __attribute__ ((unused)),
++		  char *buf __attribute__ ((unused)), grub_size_t len __attribute__ ((unused)))
++{
++  if (file->offset > file->device->net->offset)
++    {
++      grub_efihttp_chunk_read (file, NULL, file->offset - file->device->net->offset, 10240);
++    }
++  else if (file->offset < file->device->net->offset)
++    {
++      efi_net_interface (close, file);
++      efi_net_interface (open, file, file->device->net->name);
++      if (file->offset)
++	grub_efihttp_chunk_read (file, NULL, file->offset, 10240);
++    }
++
++  return efi_net_interface (read, file, buf, len);
++}
++
++static grub_err_t
++grub_efi_netfs_close (grub_file_t file)
++{
++  efi_net_interface (close, file);
++  return GRUB_ERR_NONE;
++}
++
++static grub_efi_handle_t
++grub_efi_service_binding (grub_efi_handle_t dev, grub_efi_guid_t *service_binding_guid)
++{
++  grub_efi_service_binding_t *service;
++  grub_efi_status_t status;
++  grub_efi_handle_t child_dev = NULL;
++
++  service = grub_efi_open_protocol (dev, service_binding_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
++  if (!service)
++    {
++      grub_error (GRUB_ERR_IO, N_("couldn't open efi service binding protocol"));
++      return NULL;
++    }
++
++  status = efi_call_2 (service->create_child, service, &child_dev);
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      grub_error (GRUB_ERR_IO, N_("Failed to create child device of http service %x"), status);
++      return NULL;
++    }
++
++  return child_dev;
++}
++
++static grub_err_t
++grub_efi_net_parse_address (const char *address,
++    grub_efi_ip4_config2_manual_address_t *ip4,
++    grub_efi_ip6_config_manual_address_t *ip6,
++    int *is_ip6,
++    int *has_cidr)
++{
++  const char *rest;
++
++  if (grub_efi_string_to_ip4_address (address, &ip4->address, &rest))
++    {
++      *is_ip6 = 0;
++      if (*rest == '/')
++	{
++	  grub_uint32_t subnet_mask_size;
++
++	  subnet_mask_size = grub_strtoul (rest + 1, (char **) &rest, 0);
++
++	  if (!grub_errno && subnet_mask_size <= 32 && *rest == 0)
++	    {
++	      grub_uint32_t subnet_mask;
++
++	      subnet_mask = grub_cpu_to_be32 ((0xffffffffU << (32 - subnet_mask_size)));
++	      grub_memcpy (ip4->subnet_mask, &subnet_mask, sizeof (ip4->subnet_mask));
++	      if (has_cidr)
++		*has_cidr = 1;
++	      return GRUB_ERR_NONE;
++	    }
++	}
++      else if (*rest == 0)
++	{
++	  grub_uint32_t subnet_mask = 0xffffffffU;
++	  grub_memcpy (ip4->subnet_mask, &subnet_mask, sizeof (ip4->subnet_mask));
++	  if (has_cidr)
++	    *has_cidr = 0;
++	  return GRUB_ERR_NONE;
++	}
++    }
++  else if (grub_efi_string_to_ip6_address (address, &ip6->address, &rest))
++    {
++      *is_ip6 = 1;
++      if (*rest == '/')
++	{
++	  grub_efi_uint8_t prefix_length;
++
++	  prefix_length = grub_strtoul (rest + 1, (char **) &rest, 0);
++	  if (!grub_errno && prefix_length <= 128 && *rest == 0)
++	    {
++	      ip6->prefix_length = prefix_length;
++	      ip6->is_anycast = 0;
++	      if (has_cidr)
++		*has_cidr = 1;
++	      return GRUB_ERR_NONE;
++	    }
++	}
++      else if (*rest == 0)
++	{
++	  ip6->prefix_length = 128;
++	  ip6->is_anycast = 0;
++	  if (has_cidr)
++	    *has_cidr = 0;
++	  return GRUB_ERR_NONE;
++	}
++    }
++
++  return grub_error (GRUB_ERR_NET_BAD_ADDRESS,
++		   N_("unrecognised network address `%s'"),
++		   address);
++}
++
++static grub_efi_net_interface_t *
++match_route (const char *server)
++{
++  grub_err_t err;
++  grub_efi_ip4_config2_manual_address_t ip4;
++  grub_efi_ip6_config_manual_address_t ip6;
++  grub_efi_net_interface_t *inf;
++  int is_ip6 = 0;
++
++  err = grub_efi_net_parse_address (server, &ip4, &ip6, &is_ip6, 0);
++
++  if (err)
++    {
++      grub_print_error ();
++      return NULL;
++    }
++
++  if (is_ip6)
++    {
++      struct grub_efi_net_device *dev;
++      grub_efi_net_ip_address_t addr;
++
++      grub_memcpy (addr.ip6, ip6.address, sizeof(ip6.address));
++
++      for (dev = net_devices; dev; dev = dev->next)
++	  if ((inf = efi_net_ip6_config->best_interface (dev, &addr)))
++	    return inf;
++    }
++  else
++    {
++      struct grub_efi_net_device *dev;
++      grub_efi_net_ip_address_t addr;
++
++      grub_memcpy (addr.ip4, ip4.address, sizeof(ip4.address));
++
++      for (dev = net_devices; dev; dev = dev->next)
++	  if ((inf = efi_net_ip4_config->best_interface (dev, &addr)))
++	    return inf;
++    }
++
++  return 0;
++}
++
++static void
++grub_efi_net_add_pxebc_to_cards (void)
++{
++  grub_efi_uintn_t num_handles;
++  grub_efi_handle_t *handles;
++  grub_efi_handle_t *handle;
++
++  handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &pxe_io_guid,
++				    0, &num_handles);
++  if (!handles)
++    return;
++
++  for (handle = handles; num_handles--; handle++)
++    {
++      grub_efi_device_path_t *dp, *ddp, *ldp;
++      grub_efi_pxe_t *pxe;
++      struct grub_efi_net_device *d;
++      int is_ip6 = 0;
++
++      dp = grub_efi_get_device_path (*handle);
++      if (!dp)
++	continue;
++
++      ddp = grub_efi_duplicate_device_path (dp);
++      ldp = grub_efi_find_last_device_path (ddp);
++
++      if (ldp->type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
++	  && ldp->subtype == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE)
++	{
++	  ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
++	  ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
++	  ldp->length = sizeof (*ldp);
++	}
++      else if (ldp->type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
++	  && ldp->subtype == GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)
++	{
++	  is_ip6 = 1;
++	  ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
++	  ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
++	  ldp->length = sizeof (*ldp);
++	}
++
++      for (d = net_devices; d; d = d->next)
++	if (grub_efi_compare_device_paths (ddp, grub_efi_get_device_path (d->handle)) == 0)
++	  break;
++
++      if (!d)
++	{
++	  grub_free (ddp);
++	  continue;
++	}
++
++      pxe = grub_efi_open_protocol (*handle, &pxe_io_guid,
++				GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
++
++      if (!pxe)
++	{
++	  grub_free (ddp);
++	  continue;
++	}
++
++      if (is_ip6)
++	{
++	  d->ip6_pxe_handle = *handle;
++	  d->ip6_pxe = pxe;
++	}
++      else
++	{
++	  d->ip4_pxe_handle = *handle;
++	  d->ip4_pxe = pxe;
++	}
++
++      grub_free (ddp);
++    }
++
++  grub_free (handles);
++}
++
++static void
++set_ip_policy_to_static (void)
++{
++  struct grub_efi_net_device *dev;
++
++  for (dev = net_devices; dev; dev = dev->next)
++    {
++      grub_efi_ip4_config2_policy_t ip4_policy = GRUB_EFI_IP4_CONFIG2_POLICY_STATIC;
++
++      if (efi_call_4 (dev->ip4_config->set_data, dev->ip4_config,
++		    GRUB_EFI_IP4_CONFIG2_DATA_TYPE_POLICY,
++		    sizeof (ip4_policy), &ip4_policy) != GRUB_EFI_SUCCESS)
++	grub_dprintf ("efinetfs", "could not set GRUB_EFI_IP4_CONFIG2_POLICY_STATIC on dev `%s'", dev->card_name);
++
++      if (dev->ip6_config)
++	{
++	  grub_efi_ip6_config_policy_t ip6_policy = GRUB_EFI_IP6_CONFIG_POLICY_MANUAL;
++
++	  if (efi_call_4 (dev->ip6_config->set_data, dev->ip6_config,
++		    GRUB_EFI_IP6_CONFIG_DATA_TYPE_POLICY,
++		    sizeof (ip6_policy), &ip6_policy) != GRUB_EFI_SUCCESS)
++	    grub_dprintf ("efinetfs", "could not set GRUB_EFI_IP6_CONFIG_POLICY_MANUAL on dev `%s'", dev->card_name);
++	}
++    }
++}
++
++/* FIXME: Do not fail if the card did not support any of the protocol (Eg http) */
++static void
++grub_efi_net_find_cards (void)
++{
++  grub_efi_uintn_t num_handles;
++  grub_efi_handle_t *handles;
++  grub_efi_handle_t *handle;
++  int id;
++
++  handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &ip4_config_guid,
++				    0, &num_handles);
++  if (!handles)
++    return;
++
++  for (id = 0, handle = handles; num_handles--; handle++, id++)
++    {
++      grub_efi_device_path_t *dp;
++      grub_efi_ip4_config2_protocol_t *ip4_config;
++      grub_efi_ip6_config_protocol_t *ip6_config;
++      grub_efi_handle_t http_handle;
++      grub_efi_http_t *http;
++      grub_efi_handle_t dhcp4_handle;
++      grub_efi_dhcp4_protocol_t *dhcp4;
++      grub_efi_handle_t dhcp6_handle;
++      grub_efi_dhcp6_protocol_t *dhcp6;
++
++      struct grub_efi_net_device *d;
++
++      dp = grub_efi_get_device_path (*handle);
++      if (!dp)
++	continue;
++
++      ip4_config = grub_efi_open_protocol (*handle, &ip4_config_guid,
++				    GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
++      if (!ip4_config)
++	continue;
++
++      ip6_config = grub_efi_open_protocol (*handle, &ip6_config_guid,
++				    GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
++
++      http_handle = grub_efi_service_binding (*handle, &http_service_binding_guid);
++      grub_errno = GRUB_ERR_NONE;
++      http = (http_handle)
++	? grub_efi_open_protocol (http_handle, &http_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL)
++	: NULL;
++
++      dhcp4_handle = grub_efi_service_binding (*handle, &dhcp4_service_binding_guid);
++      grub_errno = GRUB_ERR_NONE;
++      dhcp4 = (dhcp4_handle)
++	? grub_efi_open_protocol (dhcp4_handle, &dhcp4_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL)
++	: NULL;
++
++
++      dhcp6_handle = grub_efi_service_binding (*handle, &dhcp6_service_binding_guid);
++      grub_errno = GRUB_ERR_NONE;
++      dhcp6 = (dhcp6_handle)
++	? grub_efi_open_protocol (dhcp6_handle, &dhcp6_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL)
++	: NULL;
++
++      d = grub_malloc (sizeof (*d));
++      if (!d)
++	{
++	  grub_free (handles);
++	  while (net_devices)
++	    {
++	      d = net_devices->next;
++	      grub_free (net_devices);
++	      net_devices = d;
++	    }
++	  return;
++	}
++      d->handle = *handle;
++      d->ip4_config = ip4_config;
++      d->ip6_config = ip6_config;
++      d->http_handle = http_handle;
++      d->http = http;
++      d->dhcp4_handle = dhcp4_handle;
++      d->dhcp4 = dhcp4;
++      d->dhcp6_handle = dhcp6_handle;
++      d->dhcp6 = dhcp6;
++      d->next = net_devices;
++      d->card_name = grub_xasprintf ("efinet%d", id);
++      d->net_interfaces = NULL;
++      net_devices = d;
++    }
++
++  grub_efi_net_add_pxebc_to_cards ();
++  grub_free (handles);
++  set_ip_policy_to_static ();
++}
++
++static void
++listroutes_ip4 (struct grub_efi_net_device *netdev)
++{
++  char **routes;
++
++  routes = NULL;
++
++  if ((routes = efi_net_ip4_config->get_route_table (netdev)))
++    {
++      char **r;
++
++      for (r = routes; *r; ++r)
++	grub_printf ("%s\n", *r);
++    }
++
++  if (routes)
++    {
++      char **r;
++
++      for (r = routes; *r; ++r)
++	grub_free (*r);
++      grub_free (routes);
++    }
++}
++
++static void
++listroutes_ip6 (struct grub_efi_net_device *netdev)
++{
++  char **routes;
++
++  routes = NULL;
++
++  if ((routes = efi_net_ip6_config->get_route_table (netdev)))
++    {
++      char **r;
++
++      for (r = routes; *r; ++r)
++	grub_printf ("%s\n", *r);
++    }
++
++  if (routes)
++    {
++      char **r;
++
++      for (r = routes; *r; ++r)
++	grub_free (*r);
++      grub_free (routes);
++    }
++}
++
++static grub_err_t
++grub_cmd_efi_listroutes (struct grub_command *cmd __attribute__ ((unused)),
++		     int argc __attribute__ ((unused)),
++		     char **args __attribute__ ((unused)))
++{
++  struct grub_efi_net_device *netdev;
++
++  for (netdev = net_devices; netdev; netdev = netdev->next)
++    {
++      listroutes_ip4 (netdev);
++      listroutes_ip6 (netdev);
++    }
++
++  return GRUB_ERR_NONE;
++}
++static grub_err_t
++grub_cmd_efi_listcards (struct grub_command *cmd __attribute__ ((unused)),
++		    int argc __attribute__ ((unused)),
++		    char **args __attribute__ ((unused)))
++{
++  struct grub_efi_net_device *dev;
++
++  for (dev = net_devices; dev; dev = dev->next)
++    {
++      char *hw_addr;
++
++      hw_addr = efi_net_ip4_config->get_hw_address (dev);
++
++      if (hw_addr)
++	{
++	  grub_printf ("%s %s\n", dev->card_name, hw_addr);
++	  grub_free (hw_addr);
++	}
++    }
++
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_cmd_efi_listaddrs (struct grub_command *cmd __attribute__ ((unused)),
++		    int argc __attribute__ ((unused)),
++		    char **args __attribute__ ((unused)))
++{
++  struct grub_efi_net_device *dev;
++  grub_efi_net_interface_t *inf;
++
++  for (dev = net_devices; dev; dev = dev->next)
++    for (inf = dev->net_interfaces; inf; inf = inf->next)
++      {
++	char *hw_addr = NULL;
++	char *addr = NULL;
++
++	if ((hw_addr = efi_net_interface_get_hw_address (inf))
++	    && (addr = efi_net_interface_get_address (inf)))
++	  grub_printf ("%s %s %s\n", inf->name, hw_addr, addr);
++
++	if (hw_addr)
++	  grub_free (hw_addr);
++	if (addr)
++	  grub_free (addr);
++      }
++
++  return GRUB_ERR_NONE;
++}
++
++/* FIXME: support MAC specifying.  */
++static grub_err_t
++grub_cmd_efi_addaddr (struct grub_command *cmd __attribute__ ((unused)),
++                  int argc, char **args)
++{
++  struct grub_efi_net_device *dev;
++  grub_err_t err;
++  grub_efi_ip4_config2_manual_address_t ip4;
++  grub_efi_ip6_config_manual_address_t ip6;
++  grub_efi_net_ip_manual_address_t net_ip;
++  int is_ip6 = 0;
++  int cidr = 0;
++
++  if (argc != 3)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("three arguments expected"));
++
++  for (dev = net_devices; dev; dev = dev->next)
++    {
++      if (grub_strcmp (dev->card_name, args[1]) == 0)
++	break;
++    }
++
++  if (!dev)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("card not found"));
++
++  err = grub_efi_net_parse_address (args[2], &ip4, &ip6, &is_ip6, &cidr);
++
++  if (err)
++    return err;
++
++  net_ip.is_ip6 = is_ip6;
++  if (is_ip6)
++    grub_memcpy (&net_ip.ip6, &ip6, sizeof(net_ip.ip6));
++  else
++    grub_memcpy (&net_ip.ip4, &ip4, sizeof(net_ip.ip4));
++
++  if (!grub_efi_net_create_interface (dev,
++		args[0],
++		&net_ip,
++		cidr))
++    return grub_errno;
++
++  return GRUB_ERR_NONE;
++}
++
++static struct grub_fs grub_efi_netfs;
++
++static grub_net_t
++grub_net_open_real (const char *name __attribute__ ((unused)))
++{
++  grub_size_t protnamelen;
++  const char *protname, *server;
++  grub_net_t ret;
++
++  net_interface = NULL;
++
++  if (grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) == 0)
++    {
++      protname = "tftp";
++      protnamelen = sizeof ("tftp") - 1;
++      server = name + sizeof ("pxe:") - 1;
++    }
++  else if (grub_strcmp (name, "pxe") == 0)
++    {
++      protname = "tftp";
++      protnamelen = sizeof ("tftp") - 1;
++      server = default_server;
++    }
++  else
++    {
++      const char *comma;
++
++      comma = grub_strchr (name, ',');
++      if (comma)
++	{
++	  protnamelen = comma - name;
++	  server = comma + 1;
++	  protname = name;
++	}
++      else
++	{
++	  protnamelen = grub_strlen (name);
++	  server = default_server;
++	  protname = name;
++	}
++    }
++
++  if (!server)
++    {
++      grub_error (GRUB_ERR_NET_BAD_ADDRESS,
++		  N_("no server is specified"));
++      return NULL;
++    }
++
++  /*FIXME: Use DNS translate name to address */
++  net_interface = match_route (server);
++
++  /*XXX: should we check device with default gateway ? */
++  if (!net_interface && !(net_interface = net_default_interface))
++    {
++      grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' no route found"),
++		  name);
++      return NULL;
++    }
++
++  if ((protnamelen == (sizeof ("https") - 1)
++	&& grub_memcmp ("https", protname, protnamelen) == 0))
++    {
++      net_interface->io = &io_http;
++      net_interface->io_type = 1;
++    }
++  else if ((protnamelen == (sizeof ("http") - 1)
++	&& grub_memcmp ("http", protname, protnamelen) == 0))
++    {
++      net_interface->io = &io_http;
++      net_interface->io_type = 0;
++    }
++  else if (protnamelen == (sizeof ("tftp") - 1)
++	&& grub_memcmp ("tftp", protname, protnamelen) == 0)
++    {
++      net_interface->io = &io_pxe;
++      net_interface->io_type = 0;
++    }
++  else
++    {
++      grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' not found"),
++		  name);
++      return NULL;
++    }
++
++  /*XXX: Should we try to avoid doing excess "reconfigure" here ??? */
++  efi_net_interface (configure);
++
++  ret = grub_zalloc (sizeof (*ret));
++  if (!ret)
++    return NULL;
++
++  ret->server = grub_strdup (server);
++  if (!ret->server)
++    {
++      grub_free (ret);
++      return NULL;
++    }
++
++  ret->fs = &grub_efi_netfs;
++  return ret;
++}
++#if 0
++static grub_command_t cmd_efi_lsaddr;
++static grub_command_t cmd_efi_lscards;
++static grub_command_t cmd_efi_lsroutes;
++static grub_command_t cmd_efi_addaddr;
++#endif
++
++static struct grub_fs grub_efi_netfs =
++  {
++    .name = "efi netfs",
++    .fs_dir = grub_efi_netfs_dir,
++    .fs_open = grub_efi_netfs_open,
++    .fs_read = grub_efi_netfs_read,
++    .fs_close = grub_efi_netfs_close,
++    .fs_label = NULL,
++    .fs_uuid = NULL,
++    .fs_mtime = NULL,
++  };
++
++int
++grub_efi_net_boot_from_https (void)
++{
++  grub_efi_loaded_image_t *image = NULL;
++  grub_efi_device_path_t *dp;
++
++  image = grub_efi_get_loaded_image (grub_efi_image_handle);
++  if (!image)
++    return 0;
++
++  dp = grub_efi_get_device_path (image->device_handle);
++
++  while (1)
++    {
++      grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
++      grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
++      grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp);
++
++      if ((type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE)
++	  && (subtype == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE))
++	{
++	  grub_efi_uri_device_path_t *uri_dp = (grub_efi_uri_device_path_t *) dp;
++	  return (grub_strncmp ((const char*)uri_dp->uri, "https://", sizeof ("https://") - 1) == 0) ? 1 : 0;
++	}
++
++      if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp))
++        break;
++      dp = (grub_efi_device_path_t *) ((char *) dp + len);
++    }
++
++  return 0;
++}
++
++int
++grub_efi_net_boot_from_opa (void)
++{
++  grub_efi_loaded_image_t *image = NULL;
++  grub_efi_device_path_t *dp;
++
++  image = grub_efi_get_loaded_image (grub_efi_image_handle);
++  if (!image)
++    return 0;
++
++  dp = grub_efi_get_device_path (image->device_handle);
++
++  while (1)
++    {
++      grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
++      grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
++      grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp);
++
++      if ((type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE)
++	  && (subtype == GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE))
++	{
++	  grub_efi_mac_address_device_path_t *mac_dp  = (grub_efi_mac_address_device_path_t *)dp;
++	  return (mac_dp->if_type == 0xC7) ? 1 : 0;
++	}
++
++      if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp))
++        break;
++      dp = (grub_efi_device_path_t *) ((char *) dp + len);
++    }
++
++  return 0;
++}
++
++static char *
++grub_env_write_readonly (struct grub_env_var *var __attribute__ ((unused)),
++			 const char *val __attribute__ ((unused)))
++{
++  return NULL;
++}
++
++grub_command_func_t grub_efi_net_list_routes = grub_cmd_efi_listroutes;
++grub_command_func_t grub_efi_net_list_cards = grub_cmd_efi_listcards;
++grub_command_func_t grub_efi_net_list_addrs = grub_cmd_efi_listaddrs;
++grub_command_func_t grub_efi_net_add_addr = grub_cmd_efi_addaddr;
++
++int
++grub_efi_net_fs_init ()
++{
++  grub_efi_net_find_cards ();
++  grub_efi_net_config = grub_efi_net_config_real;
++  grub_net_open = grub_net_open_real;
++  grub_register_variable_hook ("net_default_server", grub_efi_net_var_get_server,
++			       grub_efi_net_var_set_server);
++  grub_env_export ("net_default_server");
++  grub_register_variable_hook ("pxe_default_server", grub_efi_net_var_get_server,
++			       grub_efi_net_var_set_server);
++  grub_env_export ("pxe_default_server");
++  grub_register_variable_hook ("net_default_interface", 0,
++			       grub_efi_net_var_set_interface);
++  grub_env_export ("net_default_interface");
++  grub_register_variable_hook ("net_default_ip", grub_efi_net_var_get_ip,
++			       0);
++  grub_env_export ("net_default_ip");
++  grub_register_variable_hook ("net_default_mac", grub_efi_net_var_get_mac,
++			       0);
++  grub_env_export ("net_default_mac");
++
++  grub_env_set ("grub_netfs_type", "efi");
++  grub_register_variable_hook ("grub_netfs_type", 0, grub_env_write_readonly);
++  grub_env_export ("grub_netfs_type");
++
++  return 1;
++}
++
++void
++grub_efi_net_fs_fini (void)
++{
++  grub_env_unset ("grub_netfs_type");
++  grub_efi_net_unset_interface_vars ();
++  grub_register_variable_hook ("net_default_server", 0, 0);
++  grub_env_unset ("net_default_server");
++  grub_register_variable_hook ("net_default_interface", 0, 0);
++  grub_env_unset ("net_default_interface");
++  grub_register_variable_hook ("pxe_default_server", 0, 0);
++  grub_env_unset ("pxe_default_server");
++  grub_register_variable_hook ("net_default_ip", 0, 0);
++  grub_env_unset ("net_default_ip");
++  grub_register_variable_hook ("net_default_mac", 0, 0);
++  grub_env_unset ("net_default_mac");
++  grub_efi_net_config = NULL;
++  grub_net_open = NULL;
++  grub_fs_unregister (&grub_efi_netfs);
++}
+diff --git a/grub-core/net/efi/pxe.c b/grub-core/net/efi/pxe.c
+new file mode 100644
+index 00000000000..531949cba5c
+--- /dev/null
++++ b/grub-core/net/efi/pxe.c
+@@ -0,0 +1,424 @@
++
++#include <grub/efi/api.h>
++#include <grub/efi/efi.h>
++#include <grub/misc.h>
++#include <grub/net/efi.h>
++#include <grub/charset.h>
++
++static grub_efi_ip6_config_manual_address_t *
++efi_ip6_config_manual_address (grub_efi_ip6_config_protocol_t *ip6_config)
++{
++  grub_efi_uintn_t sz;
++  grub_efi_status_t status;
++  grub_efi_ip6_config_manual_address_t *manual_address;
++
++  sz = sizeof (*manual_address);
++  manual_address = grub_malloc (sz);
++  if (!manual_address)
++    return NULL;
++
++  status = efi_call_4 (ip6_config->get_data, ip6_config,
++		    GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS,
++		    &sz, manual_address);
++
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      grub_free (manual_address);
++      return NULL;
++    }
++
++  return manual_address;
++}
++
++static grub_efi_ip4_config2_manual_address_t *
++efi_ip4_config_manual_address (grub_efi_ip4_config2_protocol_t *ip4_config)
++{
++  grub_efi_uintn_t sz;
++  grub_efi_status_t status;
++  grub_efi_ip4_config2_manual_address_t *manual_address;
++
++  sz = sizeof (*manual_address);
++  manual_address = grub_malloc (sz);
++  if (!manual_address)
++    return NULL;
++
++  status = efi_call_4 (ip4_config->get_data, ip4_config,
++		    GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
++		    &sz, manual_address);
++
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      grub_free (manual_address);
++      return NULL;
++    }
++
++  return manual_address;
++}
++
++static void
++pxe_configure (struct grub_efi_net_device *dev, int prefer_ip6)
++{
++  grub_efi_pxe_t *pxe = (prefer_ip6) ? dev->ip6_pxe : dev->ip4_pxe;
++
++  grub_efi_pxe_mode_t *mode = pxe->mode;
++
++  if (!mode->started)
++    {
++      grub_efi_status_t status;
++      status = efi_call_2 (pxe->start, pxe, prefer_ip6);
++
++      if (status != GRUB_EFI_SUCCESS)
++	  grub_printf ("Couldn't start PXE\n");
++    }
++
++#if 0
++  grub_printf ("PXE STARTED: %u\n", mode->started);
++  grub_printf ("PXE USING IPV6: %u\n", mode->using_ipv6);
++#endif
++
++  if (mode->using_ipv6)
++    {
++      grub_efi_ip6_config_manual_address_t *manual_address;
++      manual_address = efi_ip6_config_manual_address (dev->ip6_config);
++
++      if (manual_address &&
++	  grub_memcmp (manual_address->address, mode->station_ip.v6, sizeof (manual_address->address)) != 0)
++	{
++	  grub_efi_status_t status;
++	  grub_efi_pxe_ip_address_t station_ip;
++
++	  grub_memcpy (station_ip.v6.addr, manual_address->address, sizeof (station_ip.v6.addr));
++	  status = efi_call_3 (pxe->set_station_ip, pxe, &station_ip, NULL);
++
++	  if (status != GRUB_EFI_SUCCESS)
++	      grub_printf ("Couldn't set station ip\n");
++
++	  grub_free (manual_address);
++	}
++    }
++  else
++    {
++      grub_efi_ip4_config2_manual_address_t *manual_address;
++      manual_address = efi_ip4_config_manual_address (dev->ip4_config);
++
++      if (manual_address &&
++	  grub_memcmp (manual_address->address, mode->station_ip.v4, sizeof (manual_address->address)) != 0)
++	{
++	  grub_efi_status_t status;
++	  grub_efi_pxe_ip_address_t station_ip;
++	  grub_efi_pxe_ip_address_t subnet_mask;
++
++	  grub_memcpy (station_ip.v4.addr, manual_address->address, sizeof (station_ip.v4.addr));
++	  grub_memcpy (subnet_mask.v4.addr, manual_address->subnet_mask, sizeof (subnet_mask.v4.addr));
++
++	  status = efi_call_3 (pxe->set_station_ip, pxe, &station_ip, &subnet_mask);
++
++	  if (status != GRUB_EFI_SUCCESS)
++	      grub_printf ("Couldn't set station ip\n");
++
++	  grub_free (manual_address);
++	}
++    }
++
++#if 0
++  if (mode->using_ipv6)
++    {
++      grub_printf ("PXE STATION IP: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
++	mode->station_ip.v6.addr[0],
++	mode->station_ip.v6.addr[1],
++	mode->station_ip.v6.addr[2],
++	mode->station_ip.v6.addr[3],
++	mode->station_ip.v6.addr[4],
++	mode->station_ip.v6.addr[5],
++	mode->station_ip.v6.addr[6],
++	mode->station_ip.v6.addr[7],
++	mode->station_ip.v6.addr[8],
++	mode->station_ip.v6.addr[9],
++	mode->station_ip.v6.addr[10],
++	mode->station_ip.v6.addr[11],
++	mode->station_ip.v6.addr[12],
++	mode->station_ip.v6.addr[13],
++	mode->station_ip.v6.addr[14],
++	mode->station_ip.v6.addr[15]);
++    }
++  else
++    {
++      grub_printf ("PXE STATION IP: %d.%d.%d.%d\n",
++	mode->station_ip.v4.addr[0],
++	mode->station_ip.v4.addr[1],
++	mode->station_ip.v4.addr[2],
++	mode->station_ip.v4.addr[3]);
++      grub_printf ("PXE SUBNET MASK: %d.%d.%d.%d\n",
++	mode->subnet_mask.v4.addr[0],
++	mode->subnet_mask.v4.addr[1],
++	mode->subnet_mask.v4.addr[2],
++	mode->subnet_mask.v4.addr[3]);
++    }
++#endif
++
++  /* TODO: Set The Station IP to the IP2 Config */
++}
++
++static int
++parse_ip6 (const char *val, grub_uint64_t *ip, const char **rest)
++{
++  grub_uint16_t newip[8];
++  const char *ptr = val;
++  int word, quaddot = -1;
++  int bracketed = 0;
++
++  if (ptr[0] == '[') {
++    bracketed = 1;
++    ptr++;
++  }
++
++  if (ptr[0] == ':' && ptr[1] != ':')
++    return 0;
++  if (ptr[0] == ':')
++    ptr++;
++
++  for (word = 0; word < 8; word++)
++    {
++      unsigned long t;
++      if (*ptr == ':')
++	{
++	  quaddot = word;
++	  word--;
++	  ptr++;
++	  continue;
++	}
++      t = grub_strtoul (ptr, (char **) &ptr, 16);
++      if (grub_errno)
++	{
++	  grub_errno = GRUB_ERR_NONE;
++	  break;
++	}
++      if (t & ~0xffff)
++	return 0;
++      newip[word] = grub_cpu_to_be16 (t);
++      if (*ptr != ':')
++	break;
++      ptr++;
++    }
++  if (quaddot == -1 && word < 7)
++    return 0;
++  if (quaddot != -1)
++    {
++      grub_memmove (&newip[quaddot + 7 - word], &newip[quaddot],
++		    (word - quaddot + 1) * sizeof (newip[0]));
++      grub_memset (&newip[quaddot], 0, (7 - word) * sizeof (newip[0]));
++    }
++  grub_memcpy (ip, newip, 16);
++  if (bracketed && *ptr == ']') {
++    ptr++;
++  }
++  if (rest)
++    *rest = ptr;
++  return 1;
++}
++
++static grub_err_t
++pxe_open (struct grub_efi_net_device *dev,
++	  int prefer_ip6,
++	  grub_file_t file,
++	  const char *filename,
++	  int type __attribute__((unused)))
++{
++  int i;
++  char *p;
++  grub_efi_status_t status;
++  grub_efi_pxe_ip_address_t server_ip;
++  grub_efi_uint64_t file_size = 0;
++  grub_efi_pxe_t *pxe = (prefer_ip6) ? dev->ip6_pxe : dev->ip4_pxe;
++
++  if (pxe->mode->using_ipv6)
++    {
++      const char *rest;
++      grub_uint64_t ip6[2];
++      if (parse_ip6 (file->device->net->server, ip6, &rest) && *rest == 0)
++	grub_memcpy (server_ip.v6.addr, ip6, sizeof (server_ip.v6.addr));
++      /* TODO: ERROR Handling Here */
++#if 0
++      grub_printf ("PXE SERVER IP: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
++	server_ip.v6.addr[0],
++	server_ip.v6.addr[1],
++	server_ip.v6.addr[2],
++	server_ip.v6.addr[3],
++	server_ip.v6.addr[4],
++	server_ip.v6.addr[5],
++	server_ip.v6.addr[6],
++	server_ip.v6.addr[7],
++	server_ip.v6.addr[8],
++	server_ip.v6.addr[9],
++	server_ip.v6.addr[10],
++	server_ip.v6.addr[11],
++	server_ip.v6.addr[12],
++	server_ip.v6.addr[13],
++	server_ip.v6.addr[14],
++	server_ip.v6.addr[15]);
++#endif
++    }
++  else
++    {
++      for (i = 0, p = file->device->net->server; i < 4; ++i, ++p)
++	server_ip.v4.addr[i] = grub_strtoul (p, &p, 10);
++    }
++
++  status = efi_call_10 (pxe->mtftp,
++	    pxe,
++	    GRUB_EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
++	    NULL,
++	    0,
++	    &file_size,
++	    NULL,
++	    &server_ip,
++	    (grub_efi_char8_t *)filename,
++	    NULL,
++	    0);
++
++  if (status != GRUB_EFI_SUCCESS)
++    return grub_error (GRUB_ERR_IO, "Couldn't get file size");
++
++  file->size = (grub_off_t)file_size;
++  file->not_easily_seekable = 0;
++  file->data = 0;
++  file->device->net->offset = 0;
++
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++pxe_close (struct grub_efi_net_device *dev __attribute__((unused)),
++	  int prefer_ip6 __attribute__((unused)),
++	  grub_file_t file __attribute__((unused)))
++{
++  file->offset = 0;
++  file->size = 0;
++  file->device->net->offset = 0;
++
++  if (file->data)
++    {
++      grub_free (file->data);
++      file->data = NULL;
++    }
++
++  return GRUB_ERR_NONE;
++}
++
++static grub_ssize_t
++pxe_read (struct grub_efi_net_device *dev,
++	  int prefer_ip6,
++	  grub_file_t file,
++	  char *buf,
++	  grub_size_t len)
++{
++  int i;
++  char *p;
++  grub_efi_status_t status;
++  grub_efi_pxe_t *pxe = (prefer_ip6) ? dev->ip6_pxe : dev->ip4_pxe;
++  grub_efi_uint64_t bufsz = len;
++  grub_efi_pxe_ip_address_t server_ip;
++  char *buf2 = NULL;
++
++  if (file->data)
++    {
++      /* TODO: RANGE Check for offset and file size */
++      grub_memcpy (buf, (char*)file->data + file->device->net->offset, len);
++      file->device->net->offset += len;
++      return len;
++    }
++
++  if (file->device->net->offset)
++    {
++      grub_error (GRUB_ERR_BUG, "No Offet Read Possible");
++      grub_print_error ();
++      return 0;
++    }
++
++  if (pxe->mode->using_ipv6)
++    {
++      const char *rest;
++      grub_uint64_t ip6[2];
++      if (parse_ip6 (file->device->net->server, ip6, &rest) && *rest == 0)
++	grub_memcpy (server_ip.v6.addr, ip6, sizeof (server_ip.v6.addr));
++      /* TODO: ERROR Handling Here */
++    }
++  else
++    {
++      for (i = 0, p = file->device->net->server; i < 4; ++i, ++p)
++	server_ip.v4.addr[i] = grub_strtoul (p, &p, 10);
++    }
++
++  status = efi_call_10 (pxe->mtftp,
++	    pxe,
++	    GRUB_EFI_PXE_BASE_CODE_TFTP_READ_FILE,
++	    buf,
++	    0,
++	    &bufsz,
++	    NULL,
++	    &server_ip,
++	    (grub_efi_char8_t *)file->device->net->name,
++	    NULL,
++	    0);
++
++  if (bufsz != file->size)
++    {
++      grub_error (GRUB_ERR_BUG, "Short read should not happen here");
++      grub_print_error ();
++      return 0;
++    }
++
++  if (status == GRUB_EFI_BUFFER_TOO_SMALL)
++    {
++
++      buf2 = grub_malloc (bufsz);
++
++      if (!buf2)
++	{
++	  grub_error (GRUB_ERR_OUT_OF_MEMORY, "ERROR OUT OF MEMORY");
++	  grub_print_error ();
++	  return 0;
++	}
++
++      status = efi_call_10 (pxe->mtftp,
++		pxe,
++		GRUB_EFI_PXE_BASE_CODE_TFTP_READ_FILE,
++		buf2,
++		0,
++		&bufsz,
++		NULL,
++		&server_ip,
++		(grub_efi_char8_t *)file->device->net->name,
++		NULL,
++		0);
++    }
++
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      if (buf2)
++	grub_free (buf2);
++
++      grub_error (GRUB_ERR_IO, "Failed to Read File");
++      grub_print_error ();
++      return 0;
++    }
++
++  if (buf2)
++    grub_memcpy (buf, buf2, len);
++
++  file->device->net->offset = len;
++
++  if (buf2)
++    file->data = buf2;
++
++  return len;
++}
++
++struct grub_efi_net_io io_pxe =
++  {
++    .configure = pxe_configure,
++    .open = pxe_open,
++    .read = pxe_read,
++    .close = pxe_close
++  };
++
+diff --git a/grub-core/net/net.c b/grub-core/net/net.c
+index 0ce5e675ed7..55aed92722c 100644
+--- a/grub-core/net/net.c
++++ b/grub-core/net/net.c
+@@ -32,6 +32,9 @@
+ #include <grub/loader.h>
+ #include <grub/bufio.h>
+ #include <grub/kernel.h>
++#ifdef GRUB_MACHINE_EFI
++#include <grub/net/efi.h>
++#endif
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -2033,8 +2036,49 @@ static grub_command_t cmd_addaddr, cmd_deladdr, cmd_addroute, cmd_delroute;
+ static grub_command_t cmd_lsroutes, cmd_lscards;
+ static grub_command_t cmd_lsaddr, cmd_slaac;
+ 
++#ifdef GRUB_MACHINE_EFI
++
++static enum {
++  INIT_MODE_NONE,
++  INIT_MODE_GRUB,
++  INIT_MODE_EFI
++} init_mode;
++
++static grub_command_t cmd_bootp, cmd_bootp6;
++
++#endif
++
+ GRUB_MOD_INIT(net)
+ {
++#ifdef GRUB_MACHINE_EFI
++  if (grub_net_open)
++    return;
++
++  if ((grub_efi_net_boot_from_https () || grub_efi_net_boot_from_opa ())
++      && grub_efi_net_fs_init ())
++    {
++      cmd_lsroutes = grub_register_command ("net_ls_routes", grub_efi_net_list_routes,
++					    "", N_("list network routes"));
++      cmd_lscards = grub_register_command ("net_ls_cards", grub_efi_net_list_cards,
++					   "", N_("list network cards"));
++      cmd_lsaddr = grub_register_command ("net_ls_addr", grub_efi_net_list_addrs,
++					  "", N_("list network addresses"));
++      cmd_addaddr = grub_register_command ("net_add_addr", grub_efi_net_add_addr,
++					    /* TRANSLATORS: HWADDRESS stands for
++					       "hardware address".  */
++					  N_("SHORTNAME CARD ADDRESS [HWADDRESS]"),
++					  N_("Add a network address."));
++      cmd_bootp = grub_register_command ("net_bootp", grub_efi_net_bootp,
++					 N_("[CARD]"),
++					 N_("perform a bootp autoconfiguration"));
++      cmd_bootp6 = grub_register_command ("net_bootp6", grub_efi_net_bootp6,
++					 N_("[CARD]"),
++					 N_("perform a bootp autoconfiguration"));
++      init_mode = INIT_MODE_EFI;
++      return;
++    }
++#endif
++
+   grub_register_variable_hook ("net_default_server", defserver_get_env,
+ 			       defserver_set_env);
+   grub_env_export ("net_default_server");
+@@ -2082,10 +2126,37 @@ GRUB_MOD_INIT(net)
+ 						grub_net_restore_hw,
+ 						GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK);
+   grub_net_poll_cards_idle = grub_net_poll_cards_idle_real;
++
++#ifdef GRUB_MACHINE_EFI
++  grub_env_set ("grub_netfs_type", "grub");
++  grub_register_variable_hook ("grub_netfs_type", 0, grub_env_write_readonly);
++  grub_env_export ("grub_netfs_type");
++  init_mode = INIT_MODE_GRUB;
++#endif
++
+ }
+ 
+ GRUB_MOD_FINI(net)
+ {
++
++#ifdef GRUB_MACHINE_EFI
++  if (init_mode == INIT_MODE_NONE)
++    return;
++
++  if (init_mode == INIT_MODE_EFI)
++    {
++      grub_unregister_command (cmd_lsroutes);
++      grub_unregister_command (cmd_lscards);
++      grub_unregister_command (cmd_lsaddr);
++      grub_unregister_command (cmd_addaddr);
++      grub_unregister_command (cmd_bootp);
++      grub_unregister_command (cmd_bootp6);
++      grub_efi_net_fs_fini ();
++      init_mode = INIT_MODE_NONE;
++      return;
++    }
++#endif
++
+   grub_register_variable_hook ("net_default_server", 0, 0);
+   grub_register_variable_hook ("pxe_default_server", 0, 0);
+ 
+@@ -2104,4 +2175,7 @@ GRUB_MOD_FINI(net)
+   grub_net_fini_hw (0);
+   grub_loader_unregister_preboot_hook (fini_hnd);
+   grub_net_poll_cards_idle = grub_net_poll_cards_idle_real;
++#ifdef GRUB_MACHINE_EFI
++  init_mode = INIT_MODE_NONE;
++#endif
+ }
+diff --git a/util/grub-mknetdir.c b/util/grub-mknetdir.c
+index a2461cda1c4..77958dd9dd5 100644
+--- a/util/grub-mknetdir.c
++++ b/util/grub-mknetdir.c
+@@ -32,13 +32,15 @@
+ 
+ static char *rootdir = NULL, *subdir = NULL;
+ static char *debug_image = NULL;
++static char efi_netfs = 0;
+ 
+ enum
+   {
+     OPTION_NET_DIRECTORY = 0x301,
+     OPTION_SUBDIR,
+     OPTION_DEBUG,
+-    OPTION_DEBUG_IMAGE
++    OPTION_DEBUG_IMAGE,
++    OPTION_DEBUG_EFI_NETFS
+   };
+ 
+ static struct argp_option options[] = {
+@@ -49,6 +51,7 @@ static struct argp_option options[] = {
+    0, N_("relative subdirectory on network server"), 2},
+   {"debug", OPTION_DEBUG, 0, OPTION_HIDDEN, 0, 2},
+   {"debug-image", OPTION_DEBUG_IMAGE, N_("STRING"), OPTION_HIDDEN, 0, 2},
++  {"debug-efi-netfs", OPTION_DEBUG_EFI_NETFS, 0, OPTION_HIDDEN, 0, 2},
+   {0, 0, 0, 0, 0, 0}
+ };
+ 
+@@ -67,6 +70,9 @@ argp_parser (int key, char *arg, struct argp_state *state)
+       free (subdir);
+       subdir = xstrdup (arg);
+       return 0;
++    case OPTION_DEBUG_EFI_NETFS:
++      efi_netfs = 1;
++      return 0;
+       /* This is an undocumented feature...  */
+     case OPTION_DEBUG:
+       verbosity++;
+@@ -82,7 +88,6 @@ argp_parser (int key, char *arg, struct argp_state *state)
+     }
+ }
+ 
+-
+ struct argp argp = {
+   options, argp_parser, NULL,
+   "\v"N_("Prepares GRUB network boot images at net_directory/subdir "
+@@ -92,7 +97,7 @@ struct argp argp = {
+ 
+ static char *base;
+ 
+-static const struct
++static struct
+ {
+   const char *mkimage_target;
+   const char *netmodule;
+@@ -156,6 +161,7 @@ process_input_dir (const char *input_dir, enum grub_install_plat platform)
+   grub_install_push_module (targets[platform].netmodule);
+ 
+   output = grub_util_path_concat_ext (2, grubdir, "core", targets[platform].ext);
++
+   grub_install_make_image_wrap (input_dir, prefix, output,
+ 				0, load_cfg,
+ 				targets[platform].mkimage_target, 0);
+@@ -195,7 +201,16 @@ main (int argc, char *argv[])
+ 
+   grub_install_mkdir_p (base);
+ 
+-  grub_install_push_module ("tftp");
++  if (!efi_netfs)
++    {
++      grub_install_push_module ("tftp");
++      grub_install_push_module ("http");
++    }
++  else
++    {
++      targets[GRUB_INSTALL_PLATFORM_I386_EFI].netmodule = "efi_netfs";
++      targets[GRUB_INSTALL_PLATFORM_X86_64_EFI].netmodule = "efi_netfs";
++    }
+ 
+   if (!grub_install_source_directory)
+     {
+diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
+index 0b490195ad9..f431f49973e 100644
+--- a/include/grub/efi/api.h
++++ b/include/grub/efi/api.h
+@@ -622,6 +622,23 @@ typedef union
+ 
+ typedef grub_efi_uint64_t grub_efi_physical_address_t;
+ typedef grub_efi_uint64_t grub_efi_virtual_address_t;
++typedef struct {
++  grub_uint8_t addr[4];
++} grub_efi_pxe_ipv4_address_t;
++
++typedef struct {
++  grub_uint8_t addr[16];
++} grub_efi_pxe_ipv6_address_t;
++
++typedef struct {
++  grub_uint8_t addr[32];
++} grub_efi_pxe_mac_address_t;
++
++typedef union {
++    grub_uint32_t addr[4];
++    grub_efi_pxe_ipv4_address_t v4;
++    grub_efi_pxe_ipv6_address_t v6;
++} grub_efi_pxe_ip_address_t;
+ 
+ struct grub_efi_guid
+ {
+@@ -889,6 +906,8 @@ struct grub_efi_ipv6_device_path
+   grub_efi_uint16_t remote_port;
+   grub_efi_uint16_t protocol;
+   grub_efi_uint8_t static_ip_address;
++  grub_efi_uint8_t prefix_length;
++  grub_efi_ipv6_address_t gateway_ip_address;
+ } GRUB_PACKED;
+ typedef struct grub_efi_ipv6_device_path grub_efi_ipv6_device_path_t;
+ 
+@@ -938,6 +957,15 @@ struct grub_efi_uri_device_path
+ } GRUB_PACKED;
+ typedef struct grub_efi_uri_device_path grub_efi_uri_device_path_t;
+ 
++#define GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE                31
++struct grub_efi_dns_device_path
++{
++  grub_efi_device_path_t header;
++  grub_efi_uint8_t is_ipv6;
++  grub_efi_pxe_ip_address_t dns_server_ip[0];
++} GRUB_PACKED;
++typedef struct grub_efi_dns_device_path grub_efi_dns_device_path_t;
++
+ #define GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE	10
+ 
+ /* Media Device Path.  */
+@@ -1020,6 +1048,23 @@ struct grub_efi_bios_device_path
+ } GRUB_PACKED;
+ typedef struct grub_efi_bios_device_path grub_efi_bios_device_path_t;
+ 
++/* Service Binding definitions */
++struct grub_efi_service_binding;
++
++typedef grub_efi_status_t
++(*grub_efi_service_binding_create_child) (struct grub_efi_service_binding *this,
++                                          grub_efi_handle_t *child_handle);
++
++typedef grub_efi_status_t
++(*grub_efi_service_binding_destroy_child) (struct grub_efi_service_binding *this,
++                                           grub_efi_handle_t *child_handle);
++
++typedef struct grub_efi_service_binding
++{
++  grub_efi_service_binding_create_child create_child;
++  grub_efi_service_binding_destroy_child destroy_child;
++} grub_efi_service_binding_t;
++
+ struct grub_efi_open_protocol_information_entry
+ {
+   grub_efi_handle_t agent_handle;
+@@ -1569,23 +1614,27 @@ typedef struct grub_efi_pxe_tftp_error
+   grub_efi_char8_t error_string[127];
+ } grub_efi_pxe_tftp_error_t;
+ 
+-typedef struct {
+-  grub_uint8_t addr[4];
+-} grub_efi_pxe_ipv4_address_t;
++typedef grub_efi_uint16_t grub_efi_pxe_base_code_udp_port_t;
+ 
+-typedef struct {
+-  grub_uint8_t addr[16];
+-} grub_efi_pxe_ipv6_address_t;
++typedef enum {
++  GRUB_EFI_PXE_BASE_CODE_TFTP_FIRST,
++  GRUB_EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
++  GRUB_EFI_PXE_BASE_CODE_TFTP_READ_FILE,
++  GRUB_EFI_PXE_BASE_CODE_TFTP_WRITE_FILE,
++  GRUB_EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY,
++  GRUB_EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE,
++  GRUB_EFI_PXE_BASE_CODE_MTFTP_READ_FILE,
++  GRUB_EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY,
++  GRUB_EFI_PXE_BASE_CODE_MTFTP_LAST
++} grub_efi_pxe_base_code_tftp_opcode_t;
+ 
+ typedef struct {
+-  grub_uint8_t addr[32];
+-} grub_efi_pxe_mac_address_t;
+-
+-typedef union {
+-  grub_uint32_t addr[4];
+-  grub_efi_pxe_ipv4_address_t v4;
+-  grub_efi_pxe_ipv6_address_t v6;
+-} grub_efi_pxe_ip_address_t;
++  grub_efi_ip_address_t mcast_ip;
++  grub_efi_pxe_base_code_udp_port_t c_port;
++  grub_efi_pxe_base_code_udp_port_t s_port;
++  grub_efi_uint16_t listen_timeout;
++  grub_efi_uint16_t transmit_timeout;
++} grub_efi_pxe_base_code_mtftp_info_t;
+ 
+ #define GRUB_EFI_PXE_BASE_CODE_MAX_IPCNT 8
+ typedef struct grub_efi_pxe_ip_filter
+@@ -1652,17 +1701,31 @@ typedef struct grub_efi_pxe_mode
+ typedef struct grub_efi_pxe
+ {
+   grub_uint64_t rev;
+-  void (*start) (void);
++  grub_efi_status_t (*start) (struct grub_efi_pxe *this, grub_efi_boolean_t use_ipv6);
+   void (*stop) (void);
+-  void (*dhcp) (void);
++  grub_efi_status_t (*dhcp) (struct grub_efi_pxe *this,
++			    grub_efi_boolean_t sort_offers);
+   void (*discover) (void);
+-  void (*mftp) (void);
++  grub_efi_status_t (*mtftp) (struct grub_efi_pxe *this,
++			    grub_efi_pxe_base_code_tftp_opcode_t operation,
++			    void *buffer_ptr,
++			    grub_efi_boolean_t overwrite,
++			    grub_efi_uint64_t *buffer_size,
++			    grub_efi_uintn_t *block_size,
++			    grub_efi_pxe_ip_address_t *server_ip,
++			    //grub_efi_ip_address_t *server_ip,
++			    grub_efi_char8_t *filename,
++			    grub_efi_pxe_base_code_mtftp_info_t *info,
++			    grub_efi_boolean_t dont_use_buffer);
+   void (*udpwrite) (void);
+   void (*udpread) (void);
+   void (*setipfilter) (void);
+   void (*arp) (void);
+   void (*setparams) (void);
+-  void (*setstationip) (void);
++  grub_efi_status_t (*set_station_ip) (struct grub_efi_pxe *this,
++			    grub_efi_pxe_ip_address_t *new_station_ip,
++			    grub_efi_pxe_ip_address_t *new_subnet_mask);
++  //void (*setstationip) (void);
+   void (*setpackets) (void);
+   struct grub_efi_pxe_mode *mode;
+ } grub_efi_pxe_t;
+@@ -1924,6 +1987,44 @@ struct grub_efi_ip4_config2_protocol
+ };
+ typedef struct grub_efi_ip4_config2_protocol grub_efi_ip4_config2_protocol_t;
+ 
++struct grub_efi_ip4_route_table {
++  grub_efi_ipv4_address_t subnet_address;
++  grub_efi_ipv4_address_t subnet_mask;
++  grub_efi_ipv4_address_t gateway_address;
++};
++
++typedef struct grub_efi_ip4_route_table grub_efi_ip4_route_table_t;
++
++#define GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE 32
++
++struct grub_efi_ip4_config2_interface_info {
++  grub_efi_char16_t name[GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE];
++  grub_efi_uint8_t if_type;
++  grub_efi_uint32_t hw_address_size;
++  grub_efi_mac_address_t hw_address;
++  grub_efi_ipv4_address_t station_address;
++  grub_efi_ipv4_address_t subnet_mask;
++  grub_efi_uint32_t route_table_size;
++  grub_efi_ip4_route_table_t *route_table;
++};
++
++typedef struct grub_efi_ip4_config2_interface_info grub_efi_ip4_config2_interface_info_t;
++
++enum grub_efi_ip4_config2_policy {
++  GRUB_EFI_IP4_CONFIG2_POLICY_STATIC,
++  GRUB_EFI_IP4_CONFIG2_POLICY_DHCP,
++  GRUB_EFI_IP4_CONFIG2_POLICY_MAX
++};
++
++typedef enum grub_efi_ip4_config2_policy grub_efi_ip4_config2_policy_t;
++
++struct grub_efi_ip4_config2_manual_address {
++  grub_efi_ipv4_address_t address;
++  grub_efi_ipv4_address_t subnet_mask;
++};
++
++typedef struct grub_efi_ip4_config2_manual_address grub_efi_ip4_config2_manual_address_t;
++
+ enum grub_efi_ip6_config_data_type {
+   GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO,
+   GRUB_EFI_IP6_CONFIG_DATA_TYPE_ALT_INTERFACEID,
+@@ -1958,6 +2059,49 @@ struct grub_efi_ip6_config_protocol
+ };
+ typedef struct grub_efi_ip6_config_protocol grub_efi_ip6_config_protocol_t;
+ 
++enum grub_efi_ip6_config_policy {
++  GRUB_EFI_IP6_CONFIG_POLICY_MANUAL,
++  GRUB_EFI_IP6_CONFIG_POLICY_AUTOMATIC
++};
++typedef enum grub_efi_ip6_config_policy grub_efi_ip6_config_policy_t;
++
++struct grub_efi_ip6_address_info {
++  grub_efi_ipv6_address_t address;
++  grub_efi_uint8_t prefix_length;
++};
++typedef struct grub_efi_ip6_address_info grub_efi_ip6_address_info_t;
++
++struct grub_efi_ip6_route_table {
++  grub_efi_pxe_ipv6_address_t gateway;
++  grub_efi_pxe_ipv6_address_t destination;
++  grub_efi_uint8_t prefix_length;
++};
++typedef struct grub_efi_ip6_route_table grub_efi_ip6_route_table_t;
++
++struct grub_efi_ip6_config_interface_info {
++  grub_efi_char16_t name[32];
++  grub_efi_uint8_t if_type;
++  grub_efi_uint32_t hw_address_size;
++  grub_efi_mac_address_t hw_address;
++  grub_efi_uint32_t address_info_count;
++  grub_efi_ip6_address_info_t *address_info;
++  grub_efi_uint32_t route_count;
++  grub_efi_ip6_route_table_t *route_table;
++};
++typedef struct grub_efi_ip6_config_interface_info grub_efi_ip6_config_interface_info_t;
++
++struct grub_efi_ip6_config_dup_addr_detect_transmits {
++  grub_efi_uint32_t dup_addr_detect_transmits;
++};
++typedef struct grub_efi_ip6_config_dup_addr_detect_transmits grub_efi_ip6_config_dup_addr_detect_transmits_t;
++
++struct grub_efi_ip6_config_manual_address {
++  grub_efi_ipv6_address_t address;
++  grub_efi_boolean_t is_anycast;
++  grub_efi_uint8_t prefix_length;
++};
++typedef struct grub_efi_ip6_config_manual_address grub_efi_ip6_config_manual_address_t;
++
+ #if (GRUB_TARGET_SIZEOF_VOID_P == 4) || defined (__ia64__) \
+   || defined (__aarch64__) || defined (__MINGW64__) || defined (__CYGWIN__) \
+   || defined(__riscv)
+diff --git a/include/grub/efi/dhcp.h b/include/grub/efi/dhcp.h
+new file mode 100644
+index 00000000000..fdb88eb810e
+--- /dev/null
++++ b/include/grub/efi/dhcp.h
+@@ -0,0 +1,343 @@
++#ifndef GRUB_EFI_DHCP_HEADER
++#define GRUB_EFI_DHCP_HEADER	1
++
++#define GRUB_EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID \
++  { 0x9d9a39d8, 0xbd42, 0x4a73, \
++    { 0xa4, 0xd5, 0x8e, 0xe9, 0x4b, 0xe1, 0x13, 0x80 } \
++  }
++
++#define GRUB_EFI_DHCP4_PROTOCOL_GUID \
++  { 0x8a219718, 0x4ef5, 0x4761, \
++    { 0x91, 0xc8, 0xc0, 0xf0, 0x4b, 0xda, 0x9e, 0x56 } \
++  }
++
++#define GRUB_EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID \
++  { 0x9fb9a8a1, 0x2f4a, 0x43a6, \
++    { 0x88, 0x9c, 0xd0, 0xf7, 0xb6, 0xc4 ,0x7a, 0xd5 } \
++  }
++
++#define GRUB_EFI_DHCP6_PROTOCOL_GUID \
++  { 0x87c8bad7, 0x595, 0x4053, \
++    { 0x82, 0x97, 0xde, 0xde, 0x39, 0x5f, 0x5d, 0x5b } \
++  }
++
++typedef struct grub_efi_dhcp4_protocol grub_efi_dhcp4_protocol_t;
++
++enum grub_efi_dhcp4_state {
++  GRUB_EFI_DHCP4_STOPPED,
++  GRUB_EFI_DHCP4_INIT,
++  GRUB_EFI_DHCP4_SELECTING,
++  GRUB_EFI_DHCP4_REQUESTING,
++  GRUB_EFI_DHCP4_BOUND,
++  GRUB_EFI_DHCP4_RENEWING,
++  GRUB_EFI_DHCP4_REBINDING,
++  GRUB_EFI_DHCP4_INIT_REBOOT,
++  GRUB_EFI_DHCP4_REBOOTING
++};
++
++typedef enum grub_efi_dhcp4_state grub_efi_dhcp4_state_t;
++
++struct grub_efi_dhcp4_header {
++  grub_efi_uint8_t op_code;
++  grub_efi_uint8_t hw_type;
++  grub_efi_uint8_t hw_addr_len;
++  grub_efi_uint8_t hops;
++  grub_efi_uint32_t xid;
++  grub_efi_uint16_t seconds;
++  grub_efi_uint16_t reserved;
++  grub_efi_ipv4_address_t client_addr;
++  grub_efi_ipv4_address_t your_addr;
++  grub_efi_ipv4_address_t server_addr;
++  grub_efi_ipv4_address_t gateway_addr;
++  grub_efi_uint8_t client_hw_addr[16];
++  grub_efi_char8_t server_name[64];
++  grub_efi_char8_t boot_file_name[128];
++} GRUB_PACKED;
++
++typedef struct grub_efi_dhcp4_header grub_efi_dhcp4_header_t;
++
++struct grub_efi_dhcp4_packet {
++  grub_efi_uint32_t size;
++  grub_efi_uint32_t length;
++  struct {
++    grub_efi_dhcp4_header_t header;
++    grub_efi_uint32_t magik;
++    grub_efi_uint8_t option[1];
++  } dhcp4;
++} GRUB_PACKED;
++
++typedef struct grub_efi_dhcp4_packet grub_efi_dhcp4_packet_t;
++
++struct grub_efi_dhcp4_listen_point {
++  grub_efi_ipv4_address_t listen_address;
++  grub_efi_ipv4_address_t subnet_mask;
++  grub_efi_uint16_t listen_port;
++};
++
++typedef struct grub_efi_dhcp4_listen_point grub_efi_dhcp4_listen_point_t;
++
++struct grub_efi_dhcp4_transmit_receive_token {
++  grub_efi_status_t status;
++  grub_efi_event_t completion_event;
++  grub_efi_ipv4_address_t remote_address;
++  grub_efi_uint16_t remote_port;
++  grub_efi_ipv4_address_t gateway_address;
++  grub_efi_uint32_t listen_point_count;
++  grub_efi_dhcp4_listen_point_t *listen_points;
++  grub_efi_uint32_t timeout_value;
++  grub_efi_dhcp4_packet_t *packet;
++  grub_efi_uint32_t response_count;
++  grub_efi_dhcp4_packet_t *response_list;
++};
++
++typedef struct grub_efi_dhcp4_transmit_receive_token grub_efi_dhcp4_transmit_receive_token_t;
++
++enum grub_efi_dhcp4_event {
++  GRUB_EFI_DHCP4_SEND_DISCOVER = 0X01,
++  GRUB_EFI_DHCP4_RCVD_OFFER,
++  GRUB_EFI_DHCP4_SELECT_OFFER,
++  GRUB_EFI_DHCP4_SEND_REQUEST,
++  GRUB_EFI_DHCP4_RCVD_ACK,
++  GRUB_EFI_DHCP4_RCVD_NAK,
++  GRUB_EFI_DHCP4_SEND_DECLINE,
++  GRUB_EFI_DHCP4_BOUND_COMPLETED,
++  GRUB_EFI_DHCP4_ENTER_RENEWING,
++  GRUB_EFI_DHCP4_ENTER_REBINDING,
++  GRUB_EFI_DHCP4_ADDRESS_LOST,
++  GRUB_EFI_DHCP4_FAIL
++};
++
++typedef enum grub_efi_dhcp4_event grub_efi_dhcp4_event_t;
++
++struct grub_efi_dhcp4_packet_option {
++  grub_efi_uint8_t op_code;
++  grub_efi_uint8_t length;
++  grub_efi_uint8_t data[1];
++} GRUB_PACKED;
++
++typedef struct grub_efi_dhcp4_packet_option grub_efi_dhcp4_packet_option_t;
++
++struct grub_efi_dhcp4_config_data {
++  grub_efi_uint32_t discover_try_count;
++  grub_efi_uint32_t *discover_timeout;
++  grub_efi_uint32_t request_try_count;
++  grub_efi_uint32_t *request_timeout;
++  grub_efi_ipv4_address_t client_address;
++  grub_efi_status_t (*dhcp4_callback) (
++    grub_efi_dhcp4_protocol_t *this,
++    void *context,
++    grub_efi_dhcp4_state_t current_state,
++    grub_efi_dhcp4_event_t dhcp4_event,
++    grub_efi_dhcp4_packet_t *packet,
++    grub_efi_dhcp4_packet_t **new_packet
++  );
++  void *callback_context;
++  grub_efi_uint32_t option_count;
++  grub_efi_dhcp4_packet_option_t **option_list;
++};
++
++typedef struct grub_efi_dhcp4_config_data grub_efi_dhcp4_config_data_t;
++
++struct grub_efi_dhcp4_mode_data {
++  grub_efi_dhcp4_state_t state;
++  grub_efi_dhcp4_config_data_t config_data;
++  grub_efi_ipv4_address_t client_address;
++  grub_efi_mac_address_t client_mac_address;
++  grub_efi_ipv4_address_t server_address;
++  grub_efi_ipv4_address_t router_address;
++  grub_efi_ipv4_address_t subnet_mask;
++  grub_efi_uint32_t lease_time;
++  grub_efi_dhcp4_packet_t *reply_packet;
++};
++
++typedef struct grub_efi_dhcp4_mode_data grub_efi_dhcp4_mode_data_t;
++
++struct grub_efi_dhcp4_protocol {
++  grub_efi_status_t (*get_mode_data) (grub_efi_dhcp4_protocol_t *this,
++	      grub_efi_dhcp4_mode_data_t *dhcp4_mode_data);
++  grub_efi_status_t (*configure) (grub_efi_dhcp4_protocol_t *this,
++	      grub_efi_dhcp4_config_data_t *dhcp4_cfg_data);
++  grub_efi_status_t (*start) (grub_efi_dhcp4_protocol_t *this,
++	      grub_efi_event_t completion_event);
++  grub_efi_status_t (*renew_rebind) (grub_efi_dhcp4_protocol_t *this,
++	      grub_efi_boolean_t rebind_request,
++	      grub_efi_event_t completion_event);
++  grub_efi_status_t (*release) (grub_efi_dhcp4_protocol_t *this);
++  grub_efi_status_t (*stop) (grub_efi_dhcp4_protocol_t *this);
++  grub_efi_status_t (*build) (grub_efi_dhcp4_protocol_t *this,
++	      grub_efi_dhcp4_packet_t *seed_packet,
++	      grub_efi_uint32_t delete_count,
++	      grub_efi_uint8_t *delete_list,
++	      grub_efi_uint32_t append_count,
++	      grub_efi_dhcp4_packet_option_t *append_list[],
++	      grub_efi_dhcp4_packet_t **new_packet);
++  grub_efi_status_t (*transmit_receive) (grub_efi_dhcp4_protocol_t *this,
++	      grub_efi_dhcp4_transmit_receive_token_t *token);
++  grub_efi_status_t (*parse) (grub_efi_dhcp4_protocol_t *this,
++	      grub_efi_dhcp4_packet_t *packet,
++	      grub_efi_uint32_t *option_count,
++	      grub_efi_dhcp4_packet_option_t *packet_option_list[]);
++};
++
++typedef struct grub_efi_dhcp6_protocol grub_efi_dhcp6_protocol_t;
++
++struct grub_efi_dhcp6_retransmission {
++  grub_efi_uint32_t irt;
++  grub_efi_uint32_t mrc;
++  grub_efi_uint32_t mrt;
++  grub_efi_uint32_t mrd;
++};
++
++typedef struct grub_efi_dhcp6_retransmission grub_efi_dhcp6_retransmission_t;
++
++enum grub_efi_dhcp6_event {
++  GRUB_EFI_DHCP6_SEND_SOLICIT,
++  GRUB_EFI_DHCP6_RCVD_ADVERTISE,
++  GRUB_EFI_DHCP6_SELECT_ADVERTISE,
++  GRUB_EFI_DHCP6_SEND_REQUEST,
++  GRUB_EFI_DHCP6_RCVD_REPLY,
++  GRUB_EFI_DHCP6_RCVD_RECONFIGURE,
++  GRUB_EFI_DHCP6_SEND_DECLINE,
++  GRUB_EFI_DHCP6_SEND_CONFIRM,
++  GRUB_EFI_DHCP6_SEND_RELEASE,
++  GRUB_EFI_DHCP6_SEND_RENEW,
++  GRUB_EFI_DHCP6_SEND_REBIND
++};
++
++typedef enum grub_efi_dhcp6_event grub_efi_dhcp6_event_t;
++
++struct grub_efi_dhcp6_packet_option {
++  grub_efi_uint16_t op_code;
++  grub_efi_uint16_t op_len;
++  grub_efi_uint8_t data[1];
++} GRUB_PACKED;
++
++typedef struct grub_efi_dhcp6_packet_option grub_efi_dhcp6_packet_option_t;
++
++struct grub_efi_dhcp6_header {
++  grub_efi_uint32_t transaction_id:24;
++  grub_efi_uint32_t message_type:8;
++} GRUB_PACKED;
++
++typedef struct grub_efi_dhcp6_header grub_efi_dhcp6_header_t;
++
++struct grub_efi_dhcp6_packet {
++  grub_efi_uint32_t size;
++  grub_efi_uint32_t length;
++  struct {
++    grub_efi_dhcp6_header_t header;
++    grub_efi_uint8_t option[1];
++  } dhcp6;
++} GRUB_PACKED;
++
++typedef struct grub_efi_dhcp6_packet grub_efi_dhcp6_packet_t;
++
++struct grub_efi_dhcp6_ia_address {
++  grub_efi_ipv6_address_t ip_address;
++  grub_efi_uint32_t preferred_lifetime;
++  grub_efi_uint32_t valid_lifetime;
++};
++
++typedef struct grub_efi_dhcp6_ia_address grub_efi_dhcp6_ia_address_t;
++
++enum grub_efi_dhcp6_state {
++  GRUB_EFI_DHCP6_INIT,
++  GRUB_EFI_DHCP6_SELECTING,
++  GRUB_EFI_DHCP6_REQUESTING,
++  GRUB_EFI_DHCP6_DECLINING,
++  GRUB_EFI_DHCP6_CONFIRMING,
++  GRUB_EFI_DHCP6_RELEASING,
++  GRUB_EFI_DHCP6_BOUND,
++  GRUB_EFI_DHCP6_RENEWING,
++  GRUB_EFI_DHCP6_REBINDING
++};
++
++typedef enum grub_efi_dhcp6_state grub_efi_dhcp6_state_t;
++
++#define GRUB_EFI_DHCP6_IA_TYPE_NA 3
++#define GRUB_EFI_DHCP6_IA_TYPE_TA 4
++
++struct grub_efi_dhcp6_ia_descriptor {
++  grub_efi_uint16_t type;
++  grub_efi_uint32_t ia_id;
++};
++
++typedef struct grub_efi_dhcp6_ia_descriptor grub_efi_dhcp6_ia_descriptor_t;
++
++struct grub_efi_dhcp6_ia {
++  grub_efi_dhcp6_ia_descriptor_t descriptor;
++  grub_efi_dhcp6_state_t state;
++  grub_efi_dhcp6_packet_t *reply_packet;
++  grub_efi_uint32_t ia_address_count;
++  grub_efi_dhcp6_ia_address_t ia_address[1];
++};
++
++typedef struct grub_efi_dhcp6_ia grub_efi_dhcp6_ia_t;
++
++struct grub_efi_dhcp6_duid {
++  grub_efi_uint16_t length;
++  grub_efi_uint8_t duid[1];
++};
++
++typedef struct grub_efi_dhcp6_duid grub_efi_dhcp6_duid_t;
++
++struct grub_efi_dhcp6_mode_data {
++  grub_efi_dhcp6_duid_t *client_id;
++  grub_efi_dhcp6_ia_t *ia;
++};
++
++typedef struct grub_efi_dhcp6_mode_data grub_efi_dhcp6_mode_data_t;
++
++struct grub_efi_dhcp6_config_data {
++  grub_efi_status_t (*dhcp6_callback) (grub_efi_dhcp6_protocol_t this,
++		void *context,
++		grub_efi_dhcp6_state_t current_state,
++		grub_efi_dhcp6_event_t dhcp6_event,
++		grub_efi_dhcp6_packet_t *packet,
++		grub_efi_dhcp6_packet_t **new_packet);
++  void *callback_context;
++  grub_efi_uint32_t option_count;
++  grub_efi_dhcp6_packet_option_t **option_list;
++  grub_efi_dhcp6_ia_descriptor_t ia_descriptor;
++  grub_efi_event_t ia_info_event;
++  grub_efi_boolean_t reconfigure_accept;
++  grub_efi_boolean_t rapid_commit;
++  grub_efi_dhcp6_retransmission_t *solicit_retransmission;
++};
++
++typedef struct grub_efi_dhcp6_config_data grub_efi_dhcp6_config_data_t;
++
++struct grub_efi_dhcp6_protocol {
++  grub_efi_status_t (*get_mode_data) (grub_efi_dhcp6_protocol_t *this,
++	    grub_efi_dhcp6_mode_data_t *dhcp6_mode_data,
++	    grub_efi_dhcp6_config_data_t *dhcp6_config_data);
++  grub_efi_status_t (*configure) (grub_efi_dhcp6_protocol_t *this,
++	    grub_efi_dhcp6_config_data_t *dhcp6_cfg_data);
++  grub_efi_status_t (*start) (grub_efi_dhcp6_protocol_t *this);
++  grub_efi_status_t (*info_request) (grub_efi_dhcp6_protocol_t *this,
++	    grub_efi_boolean_t send_client_id,
++	    grub_efi_dhcp6_packet_option_t *option_request,
++	    grub_efi_uint32_t option_count,
++	    grub_efi_dhcp6_packet_option_t *option_list[],
++	    grub_efi_dhcp6_retransmission_t *retransmission,
++	    grub_efi_event_t timeout_event,
++	    grub_efi_status_t (*reply_callback) (grub_efi_dhcp6_protocol_t *this,
++		    void *context,
++		    grub_efi_dhcp6_packet_t *packet),
++	    void *callback_context);
++  grub_efi_status_t (*renew_rebind) (grub_efi_dhcp6_protocol_t *this,
++	    grub_efi_boolean_t rebind_request);
++  grub_efi_status_t (*decline) (grub_efi_dhcp6_protocol_t *this,
++	    grub_efi_uint32_t address_count,
++	    grub_efi_ipv6_address_t *addresses);
++  grub_efi_status_t (*release) (grub_efi_dhcp6_protocol_t *this,
++	    grub_efi_uint32_t address_count,
++	    grub_efi_ipv6_address_t *addresses);
++  grub_efi_status_t (*stop) (grub_efi_dhcp6_protocol_t *this);
++  grub_efi_status_t (*parse) (grub_efi_dhcp6_protocol_t *this,
++	    grub_efi_dhcp6_packet_t *packet,
++	    grub_efi_uint32_t *option_count,
++	    grub_efi_dhcp6_packet_option_t *packet_option_list[]);
++};
++
++#endif /* ! GRUB_EFI_DHCP_HEADER */
+diff --git a/include/grub/efi/http.h b/include/grub/efi/http.h
+new file mode 100644
+index 00000000000..c5e9a89f505
+--- /dev/null
++++ b/include/grub/efi/http.h
+@@ -0,0 +1,215 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2006,2007,2008  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef GRUB_EFI_HTTP_HEADER
++#define GRUB_EFI_HTTP_HEADER	1
++
++#include <grub/symbol.h>
++#include <grub/net.h>
++#include <grub/efi/api.h>
++
++#define GRUB_EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID \
++  { 0xbdc8e6af, 0xd9bc, 0x4379, \
++      { 0xa7, 0x2a, 0xe0, 0xc4, 0xe7, 0x5d, 0xae, 0x1c } \
++  }
++
++#define GRUB_EFI_HTTP_PROTOCOL_GUID \
++  { 0x7A59B29B, 0x910B, 0x4171, \
++      { 0x82, 0x42, 0xA8, 0x5A, 0x0D, 0xF2, 0x5B, 0x5B } \
++  }
++
++#define EFIHTTP_WAIT_TIME 10000 // 10000ms = 10s
++#define EFIHTTP_RX_BUF_LEN 10240
++
++//******************************************
++// Protocol Interface Structure
++//******************************************
++struct grub_efi_http;
++
++//******************************************
++// EFI_HTTP_VERSION
++//******************************************
++typedef enum {
++  GRUB_EFI_HTTPVERSION10,
++  GRUB_EFI_HTTPVERSION11,
++  GRUB_EFI_HTTPVERSIONUNSUPPORTED
++} grub_efi_http_version_t;
++
++//******************************************
++// EFI_HTTPv4_ACCESS_POINT
++//******************************************
++typedef struct {
++  grub_efi_boolean_t use_default_address;
++  grub_efi_ipv4_address_t local_address;
++  grub_efi_ipv4_address_t local_subnet;
++  grub_efi_uint16_t local_port;
++} grub_efi_httpv4_access_point_t;
++
++//******************************************
++// EFI_HTTPv6_ACCESS_POINT
++//******************************************
++typedef struct {
++  grub_efi_ipv6_address_t local_address;
++  grub_efi_uint16_t local_port;
++} grub_efi_httpv6_access_point_t;
++
++//******************************************
++// EFI_HTTP_CONFIG_DATA
++//******************************************
++typedef struct {
++  grub_efi_http_version_t http_version;
++  grub_efi_uint32_t timeout_millisec;
++  grub_efi_boolean_t local_address_is_ipv6;
++  union {
++    grub_efi_httpv4_access_point_t *ipv4_node;
++    grub_efi_httpv6_access_point_t *ipv6_node;
++  } access_point;
++} grub_efi_http_config_data_t;
++
++//******************************************
++// EFI_HTTP_METHOD
++//******************************************
++typedef enum {
++  GRUB_EFI_HTTPMETHODGET,
++  GRUB_EFI_HTTPMETHODPOST,
++  GRUB_EFI_HTTPMETHODPATCH,
++  GRUB_EFI_HTTPMETHODOPTIONS,
++  GRUB_EFI_HTTPMETHODCONNECT,
++  GRUB_EFI_HTTPMETHODHEAD,
++  GRUB_EFI_HTTPMETHODPUT,
++  GRUB_EFI_HTTPMETHODDELETE,
++  GRUB_EFI_HTTPMETHODTRACE,
++} grub_efi_http_method_t;
++
++//******************************************
++// EFI_HTTP_REQUEST_DATA
++//******************************************
++typedef struct {
++  grub_efi_http_method_t method;
++  grub_efi_char16_t *url;
++} grub_efi_http_request_data_t;
++
++typedef enum {
++  GRUB_EFI_HTTP_STATUS_UNSUPPORTED_STATUS = 0,
++  GRUB_EFI_HTTP_STATUS_100_CONTINUE,
++  GRUB_EFI_HTTP_STATUS_101_SWITCHING_PROTOCOLS,
++  GRUB_EFI_HTTP_STATUS_200_OK,
++  GRUB_EFI_HTTP_STATUS_201_CREATED,
++  GRUB_EFI_HTTP_STATUS_202_ACCEPTED,
++  GRUB_EFI_HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION,
++  GRUB_EFI_HTTP_STATUS_204_NO_CONTENT,
++  GRUB_EFI_HTTP_STATUS_205_RESET_CONTENT,
++  GRUB_EFI_HTTP_STATUS_206_PARTIAL_CONTENT,
++  GRUB_EFI_HTTP_STATUS_300_MULTIPLE_CHIOCES,
++  GRUB_EFI_HTTP_STATUS_301_MOVED_PERMANENTLY,
++  GRUB_EFI_HTTP_STATUS_302_FOUND,
++  GRUB_EFI_HTTP_STATUS_303_SEE_OTHER,
++  GRUB_EFI_HTTP_STATUS_304_NOT_MODIFIED,
++  GRUB_EFI_HTTP_STATUS_305_USE_PROXY,
++  GRUB_EFI_HTTP_STATUS_307_TEMPORARY_REDIRECT,
++  GRUB_EFI_HTTP_STATUS_400_BAD_REQUEST,
++  GRUB_EFI_HTTP_STATUS_401_UNAUTHORIZED,
++  GRUB_EFI_HTTP_STATUS_402_PAYMENT_REQUIRED,
++  GRUB_EFI_HTTP_STATUS_403_FORBIDDEN,
++  GRUB_EFI_HTTP_STATUS_404_NOT_FOUND,
++  GRUB_EFI_HTTP_STATUS_405_METHOD_NOT_ALLOWED,
++  GRUB_EFI_HTTP_STATUS_406_NOT_ACCEPTABLE,
++  GRUB_EFI_HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED,
++  GRUB_EFI_HTTP_STATUS_408_REQUEST_TIME_OUT,
++  GRUB_EFI_HTTP_STATUS_409_CONFLICT,
++  GRUB_EFI_HTTP_STATUS_410_GONE,
++  GRUB_EFI_HTTP_STATUS_411_LENGTH_REQUIRED,
++  GRUB_EFI_HTTP_STATUS_412_PRECONDITION_FAILED,
++  GRUB_EFI_HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE,
++  GRUB_EFI_HTTP_STATUS_414_REQUEST_URI_TOO_LARGE,
++  GRUB_EFI_HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE,
++  GRUB_EFI_HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED,
++  GRUB_EFI_HTTP_STATUS_417_EXPECTATION_FAILED,
++  GRUB_EFI_HTTP_STATUS_500_INTERNAL_SERVER_ERROR,
++  GRUB_EFI_HTTP_STATUS_501_NOT_IMPLEMENTED,
++  GRUB_EFI_HTTP_STATUS_502_BAD_GATEWAY,
++  GRUB_EFI_HTTP_STATUS_503_SERVICE_UNAVAILABLE,
++  GRUB_EFI_HTTP_STATUS_504_GATEWAY_TIME_OUT,
++  GRUB_EFI_HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED
++} grub_efi_http_status_code_t;
++
++//******************************************
++// EFI_HTTP_RESPONSE_DATA
++//******************************************
++typedef struct {
++  grub_efi_http_status_code_t status_code;
++} grub_efi_http_response_data_t;
++
++//******************************************
++// EFI_HTTP_HEADER
++//******************************************
++typedef struct {
++  grub_efi_char8_t *field_name;
++  grub_efi_char8_t *field_value;
++} grub_efi_http_header_t;
++
++//******************************************
++// EFI_HTTP_MESSAGE
++//******************************************
++typedef struct {
++  union {
++    grub_efi_http_request_data_t *request;
++    grub_efi_http_response_data_t *response;
++  } data;
++  grub_efi_uint32_t header_count;
++  grub_efi_http_header_t *headers;
++  grub_efi_uint32_t body_length;
++  void *body;
++} grub_efi_http_message_t;
++
++//******************************************
++// EFI_HTTP_TOKEN
++//******************************************
++typedef struct {
++  grub_efi_event_t event;
++  grub_efi_status_t status;
++  grub_efi_http_message_t *message;
++} grub_efi_http_token_t;
++
++struct grub_efi_http {
++  grub_efi_status_t
++  (*get_mode_data) (struct grub_efi_http *this,
++                    grub_efi_http_config_data_t *http_config_data);
++
++  grub_efi_status_t
++  (*configure) (struct grub_efi_http *this,
++                grub_efi_http_config_data_t *http_config_data);
++
++  grub_efi_status_t
++  (*request) (struct grub_efi_http *this,
++              grub_efi_http_token_t *token);
++
++  grub_efi_status_t
++  (*cancel) (struct grub_efi_http *this,
++             grub_efi_http_token_t *token);
++
++  grub_efi_status_t
++  (*response) (struct grub_efi_http *this,
++               grub_efi_http_token_t *token);
++
++  grub_efi_status_t
++  (*poll) (struct grub_efi_http *this);
++};
++typedef struct grub_efi_http grub_efi_http_t;
++
++#endif /* !GRUB_EFI_HTTP_HEADER */
+diff --git a/include/grub/net/efi.h b/include/grub/net/efi.h
+new file mode 100644
+index 00000000000..de90d223e8e
+--- /dev/null
++++ b/include/grub/net/efi.h
+@@ -0,0 +1,144 @@
++#ifndef GRUB_NET_EFI_HEADER
++#define GRUB_NET_EFI_HEADER	1
++
++#include <grub/efi/api.h>
++#include <grub/efi/http.h>
++#include <grub/efi/dhcp.h>
++#include <grub/command.h>
++
++typedef struct grub_efi_net_interface grub_efi_net_interface_t;
++typedef struct grub_efi_net_ip_config grub_efi_net_ip_config_t;
++typedef union grub_efi_net_ip_address grub_efi_net_ip_address_t;
++typedef struct grub_efi_net_ip_manual_address grub_efi_net_ip_manual_address_t;
++
++struct grub_efi_net_interface
++{
++  char *name;
++  int prefer_ip6;
++  struct grub_efi_net_device *dev;
++  struct grub_efi_net_io *io;
++  grub_efi_net_ip_config_t *ip_config;
++  int io_type;
++  struct grub_efi_net_interface *next;
++};
++
++#define efi_net_interface_get_hw_address(inf) inf->ip_config->get_hw_address (inf->dev)
++#define efi_net_interface_get_address(inf) inf->ip_config->get_address (inf->dev)
++#define efi_net_interface_get_route_table(inf) inf->ip_config->get_route_table (inf->dev)
++#define efi_net_interface_set_address(inf, addr, with_subnet) inf->ip_config->set_address (inf->dev, addr, with_subnet)
++#define efi_net_interface_set_gateway(inf, addr) inf->ip_config->set_gateway (inf->dev, addr)
++#define efi_net_interface_set_dns(inf, addr) inf->ip_config->set_dns (inf->dev, addr)
++
++struct grub_efi_net_ip_config
++{
++  char * (*get_hw_address) (struct grub_efi_net_device *dev);
++  char * (*get_address) (struct grub_efi_net_device *dev);
++  char ** (*get_route_table) (struct grub_efi_net_device *dev);
++  grub_efi_net_interface_t * (*best_interface) (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *address);
++  int (*set_address) (struct grub_efi_net_device *dev, grub_efi_net_ip_manual_address_t *net_ip, int with_subnet);
++  int (*set_gateway) (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *address);
++  int (*set_dns) (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *dns);
++};
++
++union grub_efi_net_ip_address
++{
++  grub_efi_ipv4_address_t ip4;
++  grub_efi_ipv6_address_t ip6;
++};
++
++struct grub_efi_net_ip_manual_address
++{
++  int is_ip6;
++  union
++  {
++    grub_efi_ip4_config2_manual_address_t ip4;
++    grub_efi_ip6_config_manual_address_t ip6;
++  };
++};
++
++struct grub_efi_net_device
++{
++  grub_efi_handle_t handle;
++  grub_efi_ip4_config2_protocol_t *ip4_config;
++  grub_efi_ip6_config_protocol_t *ip6_config;
++  grub_efi_handle_t http_handle;
++  grub_efi_http_t *http;
++  grub_efi_handle_t ip4_pxe_handle;
++  grub_efi_pxe_t *ip4_pxe;
++  grub_efi_handle_t ip6_pxe_handle;
++  grub_efi_pxe_t *ip6_pxe;
++  grub_efi_handle_t dhcp4_handle;
++  grub_efi_dhcp4_protocol_t *dhcp4;
++  grub_efi_handle_t dhcp6_handle;
++  grub_efi_dhcp6_protocol_t *dhcp6;
++  char *card_name;
++  grub_efi_net_interface_t *net_interfaces;
++  struct grub_efi_net_device *next;
++};
++
++struct grub_efi_net_io
++{
++  void (*configure) (struct grub_efi_net_device *dev, int prefer_ip6);
++  grub_err_t (*open) (struct grub_efi_net_device *dev,
++		    int prefer_ip6,
++		    grub_file_t file,
++		    const char *filename,
++		    int type);
++  grub_ssize_t (*read) (struct grub_efi_net_device *dev,
++			int prefer_ip6,
++			grub_file_t file,
++			char *buf,
++			grub_size_t len);
++  grub_err_t (*close) (struct grub_efi_net_device *dev,
++		      int prefer_ip6,
++		      grub_file_t file);
++};
++
++extern struct grub_efi_net_device *net_devices;
++
++extern struct grub_efi_net_io io_http;
++extern struct grub_efi_net_io io_pxe;
++
++extern grub_efi_net_ip_config_t *efi_net_ip4_config;
++extern grub_efi_net_ip_config_t *efi_net_ip6_config;
++
++char *
++grub_efi_ip4_address_to_string (grub_efi_ipv4_address_t *address);
++
++char *
++grub_efi_ip6_address_to_string (grub_efi_pxe_ipv6_address_t *address);
++
++char *
++grub_efi_hw_address_to_string (grub_efi_uint32_t hw_address_size, grub_efi_mac_address_t hw_address);
++
++int
++grub_efi_string_to_ip4_address (const char *val, grub_efi_ipv4_address_t *address, const char **rest);
++
++int
++grub_efi_string_to_ip6_address (const char *val, grub_efi_ipv6_address_t *address, const char **rest);
++
++char *
++grub_efi_ip6_interface_name (struct grub_efi_net_device *dev);
++
++char *
++grub_efi_ip4_interface_name (struct grub_efi_net_device *dev);
++
++grub_efi_net_interface_t *
++grub_efi_net_create_interface (struct grub_efi_net_device *dev,
++		const char *interface_name,
++		grub_efi_net_ip_manual_address_t *net_ip,
++		int has_subnet);
++
++int grub_efi_net_fs_init (void);
++void grub_efi_net_fs_fini (void);
++int grub_efi_net_boot_from_https (void);
++int grub_efi_net_boot_from_opa (void);
++
++extern grub_command_func_t grub_efi_net_list_routes;
++extern grub_command_func_t grub_efi_net_list_cards;
++extern grub_command_func_t grub_efi_net_list_addrs;
++extern grub_command_func_t grub_efi_net_add_addr;
++extern grub_command_func_t grub_efi_net_bootp;
++extern grub_command_func_t grub_efi_net_bootp6;
++
++#endif /* ! GRUB_NET_EFI_HEADER */
diff --git a/SOURCES/0096-AUDIT-0-http-boot-tracker-bug.patch b/SOURCES/0096-AUDIT-0-http-boot-tracker-bug.patch
new file mode 100644
index 0000000..ef3e67b
--- /dev/null
+++ b/SOURCES/0096-AUDIT-0-http-boot-tracker-bug.patch
@@ -0,0 +1,62 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Sebastian Krahmer <krahmer@suse.com>
+Date: Tue, 28 Nov 2017 17:24:38 +0800
+Subject: [PATCH] AUDIT-0: http boot tracker bug
+
+Fixing a memory leak in case of error, and a integer overflow, leading to a
+heap overflow due to overly large chunk sizes.
+
+We need to check against some maximum value, otherwise values like 0xffffffff
+will eventually lead in the allocation functions to small sized buffers, since
+the len is rounded up to the next reasonable alignment. The following memcpy
+will then smash the heap, leading to RCE.
+
+This is no big issue for pure http boot, since its going to execute an
+untrusted kernel anyway, but it will break trusted boot scenarios, where only
+signed code is allowed to be executed.
+
+Signed-off-by: Michael Chang <mchang@suse.com>
+---
+ grub-core/net/efi/net.c | 4 +++-
+ grub-core/net/http.c    | 5 ++++-
+ 2 files changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/net/efi/net.c b/grub-core/net/efi/net.c
+index 86bce6535d3..4bb308026ce 100644
+--- a/grub-core/net/efi/net.c
++++ b/grub-core/net/efi/net.c
+@@ -645,8 +645,10 @@ grub_efihttp_chunk_read (grub_file_t file, char *buf,
+ 
+       rd = efi_net_interface (read, file, chunk, sz);
+ 
+-      if (rd <= 0)
++      if (rd <= 0) {
++	grub_free (chunk);
+ 	return rd;
++      }
+ 
+       if (buf)
+ 	{
+diff --git a/grub-core/net/http.c b/grub-core/net/http.c
+index 12a2632ea55..b52b558d631 100644
+--- a/grub-core/net/http.c
++++ b/grub-core/net/http.c
+@@ -31,7 +31,8 @@ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+ enum
+   {
+-    HTTP_PORT = 80
++    HTTP_PORT = 80,
++    HTTP_MAX_CHUNK_SIZE = 0x80000000
+   };
+ 
+ 
+@@ -78,6 +79,8 @@ parse_line (grub_file_t file, http_data_t data, char *ptr, grub_size_t len)
+   if (data->in_chunk_len == 2)
+     {
+       data->chunk_rem = grub_strtoul (ptr, 0, 16);
++      if (data->chunk_rem > HTTP_MAX_CHUNK_SIZE)
++	  return GRUB_ERR_NET_PACKET_TOO_BIG;
+       grub_errno = GRUB_ERR_NONE;
+       if (data->chunk_rem == 0)
+ 	{
diff --git a/SOURCES/0097-grub-editenv-Add-incr-command-to-increment-integer-v.patch b/SOURCES/0097-grub-editenv-Add-incr-command-to-increment-integer-v.patch
new file mode 100644
index 0000000..f8de42a
--- /dev/null
+++ b/SOURCES/0097-grub-editenv-Add-incr-command-to-increment-integer-v.patch
@@ -0,0 +1,93 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 4 Jun 2018 19:49:47 +0200
+Subject: [PATCH] grub-editenv: Add "incr" command to increment integer value
+ env. variables
+
+To be able to automatically detect if the last boot was successful,
+We want to keep count of succesful / failed boots in some integer
+environment variable.
+
+This commit adds a grub-editenvt "incr" command to increment such
+integer value env. variables by 1 for use from various boot scripts.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ util/grub-editenv.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 50 insertions(+)
+
+diff --git a/util/grub-editenv.c b/util/grub-editenv.c
+index db6f187cc63..948eec8a114 100644
+--- a/util/grub-editenv.c
++++ b/util/grub-editenv.c
+@@ -53,6 +53,9 @@ static struct argp_option options[] = {
+   /* TRANSLATORS: "unset" is a keyword. It's a summary of "unset" subcommand.  */
+   {N_("unset [NAME ...]"),    0, 0, OPTION_DOC|OPTION_NO_USAGE,
+    N_("Delete variables."), 0},
++  /* TRANSLATORS: "incr" is a keyword. It's a summary of "incr" subcommand.  */
++  {N_("incr [NAME ...]"),     0, 0, OPTION_DOC|OPTION_NO_USAGE,
++   N_("Increase value of integer variables."), 0},
+ 
+   {0,         0, 0, OPTION_DOC, N_("Options:"), -1},
+   {"verbose", 'v', 0, 0, N_("print verbose messages."), 0},
+@@ -253,6 +256,51 @@ unset_variables (const char *name, int argc, char *argv[])
+   grub_envblk_close (envblk);
+ }
+ 
++struct get_int_value_params {
++  char *varname;
++  int value;
++};
++
++static int
++get_int_value (const char *varname, const char *value, void *hook_data)
++{
++  struct get_int_value_params *params = hook_data;
++
++  if (strcmp (varname, params->varname) == 0) {
++    params->value = strtol (value, NULL, 10);
++    return 1;
++  }
++  return 0;
++}
++
++static void
++incr_variables (const char *name, int argc, char *argv[])
++{
++  grub_envblk_t envblk;
++  char buf[16];
++
++  envblk = open_envblk_file (name);
++  while (argc)
++    {
++      struct get_int_value_params params = {
++        .varname = argv[0],
++        .value = 0, /* Consider unset variables 0 */
++      };
++
++      grub_envblk_iterate (envblk, &params, get_int_value);
++      snprintf(buf, sizeof(buf), "%d", params.value + 1);
++
++      if (! grub_envblk_set (envblk, argv[0], buf))
++        grub_util_error ("%s", _("environment block too small"));
++
++      argc--;
++      argv++;
++    }
++
++  write_envblk (name, envblk);
++  grub_envblk_close (envblk);
++}
++
+ int
+ main (int argc, char *argv[])
+ {
+@@ -292,6 +340,8 @@ main (int argc, char *argv[])
+     set_variables (filename, argc - curindex, argv + curindex);
+   else if (strcmp (command, "unset") == 0)
+     unset_variables (filename, argc - curindex, argv + curindex);
++  else if (strcmp (command, "incr") == 0)
++    incr_variables (filename, argc - curindex, argv + curindex);
+   else
+     {
+       char *program = xstrdup(program_name);
diff --git a/SOURCES/0098-Add-auto-hide-menu-support.patch b/SOURCES/0098-Add-auto-hide-menu-support.patch
new file mode 100644
index 0000000..efb76c4
--- /dev/null
+++ b/SOURCES/0098-Add-auto-hide-menu-support.patch
@@ -0,0 +1,189 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Wed, 6 Jun 2018 08:44:11 +0200
+Subject: [PATCH] Add auto-hide menu support
+
+On single-os systems we do not want to show the menu, unless something
+went wrong with the previous boot, in which case the user may need the
+menu to debug/fix the problem.
+
+This commit adds a new grub.d/00_menu_auto_hide file which emits a
+config snippet implementing this. I've chosen to do this in a separate
+grub.d file because chances of this going upstream are small and this way
+it will be easier to rebase.
+
+Since auto-hiding the menu requires detecting the previous boot was ok,
+we get fastboot support (where we don't check for a key at all) for free
+so this commit also adds support for this.
+
+The new config-file code uses the following variables:
+
+menu_auto_hide     Set this to "1" to activate the new auto-hide feature
+                   Set this to "2" to auto-hide the menu even when multiple
+                   operating systems are installed. Note the menu will still
+                   auto show after booting an other os as that won't set
+                   boot_success.
+menu_show_once     Set this to "1" to force showing the menu once.
+boot_success       The OS sets this to "1" to indicate a successful boot.
+boot_indeterminate The OS increments this integer when rebooting after e.g.
+                   installing updates or a selinux relabel.
+fastboot           If set to "1" and the conditions for auto-hiding the menu
+                   are met, the menu is not shown and all checks for keypresses
+                   are skipped, booting the default immediately.
+
+30_os-prober.in changes somewhat inspired by:
+https://git.launchpad.net/~ubuntu-core-dev/grub/+git/ubuntu/tree/debian/patches/quick_boot.patch
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+Changes in v2:
+-Drop shutdown_success tests, there is no meaningful way for systemd to set
+ this flag (by the time it knows all filesystems are unmounted or read-only
+-Drop fwsetup_once support, systemd already supports booting directly into
+ the fwsetup by doing "systemctl reboot --firmware"
+---
+ Makefile.util.def                |  6 +++++
+ util/grub.d/01_menu_auto_hide.in | 48 ++++++++++++++++++++++++++++++++++++++++
+ util/grub.d/30_os-prober.in      | 18 +++++++++++++++
+ 3 files changed, 72 insertions(+)
+ create mode 100644 util/grub.d/01_menu_auto_hide.in
+
+diff --git a/Makefile.util.def b/Makefile.util.def
+index 41906486a71..04551e095bd 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -458,6 +458,12 @@ script = {
+   installdir = grubconf;
+ };
+ 
++script = {
++  name = '01_menu_auto_hide';
++  common = util/grub.d/01_menu_auto_hide.in;
++  installdir = grubconf;
++};
++
+ script = {
+   name = '01_users';
+   common = util/grub.d/01_users.in;
+diff --git a/util/grub.d/01_menu_auto_hide.in b/util/grub.d/01_menu_auto_hide.in
+new file mode 100644
+index 00000000000..ad175870a54
+--- /dev/null
++++ b/util/grub.d/01_menu_auto_hide.in
+@@ -0,0 +1,48 @@
++#! /bin/sh
++
++# Disable / skip generating menu-auto-hide config parts on serial terminals
++for x in ${GRUB_TERMINAL_INPUT} ${GRUB_TERMINAL_OUTPUT}; do
++  case "$x" in
++    serial*)
++      exit 0
++      ;;
++  esac
++done
++
++cat << EOF
++if [ "\${boot_success}" = "1" -o "\${boot_indeterminate}" = "1" ]; then
++  set last_boot_ok=1
++else
++  set last_boot_ok=0
++fi
++
++# Reset boot_indeterminate after a successful boot
++if [ "\${boot_success}" = "1" ] ; then
++  set boot_indeterminate=0
++# Avoid boot_indeterminate causing the menu to be hidden more then once
++elif [ "\${boot_indeterminate}" = "1" ]; then
++  set boot_indeterminate=2
++fi
++set boot_success=0
++save_env boot_success boot_indeterminate
++
++if [ x\$feature_timeout_style = xy ] ; then
++  if [ "\${menu_show_once}" ]; then
++    unset menu_show_once
++    save_env menu_show_once
++    set timeout_style=menu
++    set timeout=60
++  elif [ "\${menu_auto_hide}" -a "\${last_boot_ok}" = "1" ]; then
++    set orig_timeout_style=\${timeout_style}
++    set orig_timeout=\${timeout}
++    if [ "\${fastboot}" = "1" ]; then
++      # timeout_style=menu + timeout=0 avoids the countdown code keypress check
++      set timeout_style=menu
++      set timeout=0
++    else
++      set timeout_style=hidden
++      set timeout=1
++    fi
++  fi
++fi
++EOF
+diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in
+index 4b27bd20153..3c9431cfcfb 100644
+--- a/util/grub.d/30_os-prober.in
++++ b/util/grub.d/30_os-prober.in
+@@ -42,6 +42,7 @@ if [ -z "${OSPROBED}" ] ; then
+ fi
+ 
+ osx_entry() {
++    found_other_os=1
+     # TRANSLATORS: it refers on the OS residing on device %s
+     onstr="$(gettext_printf "(on %s)" "${DEVICE}")"
+     hints=""
+@@ -102,6 +103,7 @@ for OS in ${OSPROBED} ; do
+ 
+   case ${BOOT} in
+     chain)
++      found_other_os=1
+ 
+ 	  onstr="$(gettext_printf "(on %s)" "${DEVICE}")"
+       cat << EOF
+@@ -132,6 +134,7 @@ EOF
+ EOF
+     ;;
+     efi)
++      found_other_os=1
+ 
+ 	EFIPATH=${DEVICE#*@}
+ 	DEVICE=${DEVICE%@*}
+@@ -176,6 +179,7 @@ EOF
+ 	  LINITRD="${LINITRD#/boot}"
+ 	fi
+ 
++        found_other_os=1
+ 	onstr="$(gettext_printf "(on %s)" "${DEVICE}")"
+ 	recovery_params="$(echo "${LPARAMS}" | grep single)" || true
+ 	counter=1
+@@ -257,6 +261,7 @@ EOF
+       done
+     ;;
+     hurd)
++      found_other_os=1
+       onstr="$(gettext_printf "(on %s)" "${DEVICE}")"
+       cat << EOF
+ menuentry '$(echo "${LONGNAME} $onstr" | grub_quote)' --class hurd --class gnu --class os \$menuentry_id_option 'osprober-gnuhurd-/boot/gnumach.gz-false-$(grub_get_device_id "${DEVICE}")' {
+@@ -283,6 +288,7 @@ EOF
+ EOF
+     ;;
+     minix)
++      found_other_os=1
+ 	  cat << EOF
+ menuentry "${LONGNAME} (on ${DEVICE}, Multiboot)" {
+ EOF
+@@ -299,3 +305,15 @@ EOF
+     ;;
+   esac
+ done
++
++# We override the results of the menu_auto_hide code here, this is a bit ugly,
++# but grub-mkconfig writes out the file linearly, so this is the only way
++if [ "${found_other_os}" = "1" ]; then
++  cat << EOF
++# Other OS found, undo autohiding of menu unless menu_auto_hide=2
++if [ "\${orig_timeout_style}" -a "\${menu_auto_hide}" != "2" ]; then
++  set timeout_style=\${orig_timeout_style}
++  set timeout=\${orig_timeout}
++fi
++EOF
++fi
diff --git a/SOURCES/0099-Add-grub-set-bootflag-utility.patch b/SOURCES/0099-Add-grub-set-bootflag-utility.patch
new file mode 100644
index 0000000..f5ff7d3
--- /dev/null
+++ b/SOURCES/0099-Add-grub-set-bootflag-utility.patch
@@ -0,0 +1,291 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 12 Jun 2018 13:25:16 +0200
+Subject: [PATCH] Add grub-set-bootflag utility
+
+This commit adds a new grub-set-bootflag utility, which can be used
+to set known bootflags in the grubenv: boot_success or menu_show_once.
+
+grub-set-bootflag is different from grub-editenv in 2 ways:
+
+1) It is intended to be executed by regular users so must be installed
+as suid root. As such it is written to not use any existing grubenv
+related code for easy auditing.
+
+It can't be executed through pkexec because we want to call it under gdm
+and pkexec does not work under gdm due the gdm user having /sbin/nologin
+as shell.
+
+2) Since it can be executed by regular users it only allows setting
+(assigning a value of 1 to) bootflags which it knows about. Currently
+those are just boot_success and menu_show_once.
+
+This commit also adds a couple of example systemd and files which show
+how this can be used to set boot_success from a user-session:
+
+docs/grub-boot-success.service
+docs/grub-boot-success.timer
+
+The 2 grub-boot-success.systemd files should be placed in /lib/systemd/user
+and a symlink to grub-boot-success.timer should be added to
+/lib/systemd/user/timers.target.wants.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ Makefile.util.def              |   7 ++
+ util/grub-set-bootflag.c       | 160 +++++++++++++++++++++++++++++++++++++++++
+ conf/Makefile.extra-dist       |   3 +
+ docs/grub-boot-success.service |   6 ++
+ docs/grub-boot-success.timer   |   6 ++
+ util/grub-set-bootflag.1       |  20 ++++++
+ 6 files changed, 202 insertions(+)
+ create mode 100644 util/grub-set-bootflag.c
+ create mode 100644 docs/grub-boot-success.service
+ create mode 100644 docs/grub-boot-success.timer
+ create mode 100644 util/grub-set-bootflag.1
+
+diff --git a/Makefile.util.def b/Makefile.util.def
+index 04551e095bd..c6375933faa 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -1445,3 +1445,10 @@ program = {
+   ldadd = grub-core/lib/gnulib/libgnu.a;
+   ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
+ };
++
++program = {
++  name = grub-set-bootflag;
++  installdir = sbin;
++  mansection = 1;
++  common = util/grub-set-bootflag.c;
++};
+diff --git a/util/grub-set-bootflag.c b/util/grub-set-bootflag.c
+new file mode 100644
+index 00000000000..bb198f02351
+--- /dev/null
++++ b/util/grub-set-bootflag.c
+@@ -0,0 +1,160 @@
++/* grub-set-bootflag.c - tool to set boot-flags in the grubenv. */
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2018 Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++/*
++ * NOTE this gets run by users as root (through pkexec), so this does not
++ * use any grub library / util functions to allow for easy auditing.
++ * The grub headers are only included to get certain defines.
++ */
++
++#include <config-util.h>     /* For *_DIR_NAME defines */
++#include <grub/types.h>
++#include <grub/lib/envblk.h> /* For GRUB_ENVBLK_DEFCFG define */
++#include <errno.h>
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++
++#define GRUBENV "/" GRUB_BOOT_DIR_NAME "/" GRUB_DIR_NAME "/" GRUB_ENVBLK_DEFCFG
++#define GRUBENV_SIZE 1024
++
++const char *bootflags[] = {
++  "boot_success",
++  "menu_show_once",
++  NULL
++};
++
++static void usage(void)
++{
++  int i;
++
++  fprintf (stderr, "Usage: 'grub-set-bootflag <bootflag>', where <bootflag> is one of:\n");
++  for (i = 0; bootflags[i]; i++)
++    fprintf (stderr, "  %s\n", bootflags[i]);
++}
++
++int main(int argc, char *argv[])
++{
++  /* NOTE buf must be at least the longest bootflag length + 4 bytes */
++  char env[GRUBENV_SIZE + 1], buf[64], *s;
++  const char *bootflag;
++  int i, len, ret;
++  FILE *f;
++
++  if (argc != 2)
++    {
++      usage();
++      return 1;
++    }
++
++  for (i = 0; bootflags[i]; i++)
++    if (!strcmp (argv[1], bootflags[i]))
++      break;
++  if (!bootflags[i])
++    {
++      fprintf (stderr, "Invalid bootflag: '%s'\n", argv[1]);
++      usage();
++      return 1;
++    }
++
++  bootflag = bootflags[i];
++  len = strlen (bootflag);
++
++  f = fopen (GRUBENV, "r");
++  if (!f)
++    {
++      perror ("Error opening " GRUBENV " for reading");
++      return 1;     
++    }
++
++  ret = fread (env, 1, GRUBENV_SIZE, f);
++  fclose (f);
++  if (ret != GRUBENV_SIZE)
++    {
++      errno = EINVAL;
++      perror ("Error reading from " GRUBENV);
++      return 1;     
++    }
++
++  /* 0 terminate env */
++  env[GRUBENV_SIZE] = 0;
++
++  if (strncmp (env, GRUB_ENVBLK_SIGNATURE, strlen (GRUB_ENVBLK_SIGNATURE)))
++    {
++      fprintf (stderr, "Error invalid environment block\n");
++      return 1;
++    }
++
++  /* Find a pre-existing definition of the bootflag */
++  s = strstr (env, bootflag);
++  while (s && s[len] != '=')
++    s = strstr (s + len, bootflag);
++
++  if (s && ((s[len + 1] != '0' && s[len + 1] != '1') || s[len + 2] != '\n'))
++    {
++      fprintf (stderr, "Pre-existing bootflag '%s' has unexpected value\n", bootflag);
++      return 1;     
++    }
++
++  /* No pre-existing bootflag? -> find free space */
++  if (!s)
++    {
++      for (i = 0; i < (len + 3); i++)
++        buf[i] = '#';
++      buf[i] = 0;
++      s = strstr (env, buf);
++    }
++
++  if (!s)
++    {
++      fprintf (stderr, "No space in grubenv to store bootflag '%s'\n", bootflag);
++      return 1;     
++    }
++
++  /* The grubenv is not 0 terminated, so memcpy the name + '=' , '1', '\n' */
++  snprintf(buf, sizeof(buf), "%s=1\n", bootflag);
++  memcpy(s, buf, len + 3);
++
++  /* "r+", don't truncate so that the diskspace stays reserved */
++  f = fopen (GRUBENV, "r+");
++  if (!f)
++    {
++      perror ("Error opening " GRUBENV " for writing");
++      return 1;     
++    }
++
++  ret = fwrite (env, 1, GRUBENV_SIZE, f);
++  if (ret != GRUBENV_SIZE)
++    {
++      perror ("Error writing to " GRUBENV);
++      return 1;     
++    }
++
++  ret = fflush (f);
++  if (ret)
++    {
++      perror ("Error flushing " GRUBENV);
++      return 1;     
++    }
++
++  fsync (fileno (f));
++  fclose (f);
++
++  return 0;
++}
+diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist
+index b909f2c073a..ea58362b555 100644
+--- a/conf/Makefile.extra-dist
++++ b/conf/Makefile.extra-dist
+@@ -14,6 +14,9 @@ EXTRA_DIST += util/import_unicode.py
+ EXTRA_DIST += docs/autoiso.cfg
+ EXTRA_DIST += docs/grub.cfg
+ EXTRA_DIST += docs/osdetect.cfg
++EXTRA_DIST += docs/org.gnu.grub.policy
++EXTRA_DIST += docs/grub-boot-success.service
++EXTRA_DIST += docs/grub-boot-success.timer
+ 
+ EXTRA_DIST += conf/i386-cygwin-img-ld.sc
+ 
+diff --git a/docs/grub-boot-success.service b/docs/grub-boot-success.service
+new file mode 100644
+index 00000000000..80e79584c91
+--- /dev/null
++++ b/docs/grub-boot-success.service
+@@ -0,0 +1,6 @@
++[Unit]
++Description=Mark boot as successful
++
++[Service]
++Type=oneshot
++ExecStart=/usr/sbin/grub2-set-bootflag boot_success
+diff --git a/docs/grub-boot-success.timer b/docs/grub-boot-success.timer
+new file mode 100644
+index 00000000000..5d8fcba21aa
+--- /dev/null
++++ b/docs/grub-boot-success.timer
+@@ -0,0 +1,6 @@
++[Unit]
++Description=Mark boot as successful after the user session has run 2 minutes
++ConditionUser=!@system
++
++[Timer]
++OnActiveSec=2min
+diff --git a/util/grub-set-bootflag.1 b/util/grub-set-bootflag.1
+new file mode 100644
+index 00000000000..57801da22a0
+--- /dev/null
++++ b/util/grub-set-bootflag.1
+@@ -0,0 +1,20 @@
++.TH GRUB-SET-BOOTFLAG 1 "Tue Jun 12 2018"
++.SH NAME
++\fBgrub-set-bootflag\fR \(em Set a bootflag in the GRUB environment block.
++
++.SH SYNOPSIS
++\fBgrub-set-bootflag\fR <\fIBOOTFLAG\fR>
++
++.SH DESCRIPTION
++\fBgrub-set-bootflag\fR is a command line to set bootflags in GRUB's
++stored environment.
++
++.SH COMMANDS
++.TP
++\fBBOOTFLAG\fR
++.RS 7
++Bootflag to set, one of \fIboot_success\fR or \fIshow_menu_once\fR.
++.RE
++
++.SH SEE ALSO
++.BR "info grub"
diff --git a/SOURCES/0100-docs-Add-grub-boot-indeterminate.service-example.patch b/SOURCES/0100-docs-Add-grub-boot-indeterminate.service-example.patch
new file mode 100644
index 0000000..44f6ad3
--- /dev/null
+++ b/SOURCES/0100-docs-Add-grub-boot-indeterminate.service-example.patch
@@ -0,0 +1,33 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 19 Jun 2018 15:20:54 +0200
+Subject: [PATCH] docs: Add grub-boot-indeterminate.service example
+
+This is an example service file, for use from
+/lib/systemd/system/system-update.target.wants
+to increment the boot_indeterminate variable when
+doing offline updates.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ docs/grub-boot-indeterminate.service | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+ create mode 100644 docs/grub-boot-indeterminate.service
+
+diff --git a/docs/grub-boot-indeterminate.service b/docs/grub-boot-indeterminate.service
+new file mode 100644
+index 00000000000..6c8dcb186b6
+--- /dev/null
++++ b/docs/grub-boot-indeterminate.service
+@@ -0,0 +1,11 @@
++[Unit]
++Description=Mark boot as indeterminate
++DefaultDependencies=false
++Requires=sysinit.target
++After=sysinit.target
++Wants=system-update-pre.target
++Before=system-update-pre.target
++
++[Service]
++Type=oneshot
++ExecStart=/usr/bin/grub2-editenv - incr boot_indeterminate
diff --git a/SOURCES/0101-gentpl-add-disable-support.patch b/SOURCES/0101-gentpl-add-disable-support.patch
new file mode 100644
index 0000000..2c3c998
--- /dev/null
+++ b/SOURCES/0101-gentpl-add-disable-support.patch
@@ -0,0 +1,46 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Wed, 11 Jul 2018 13:43:15 -0400
+Subject: [PATCH] gentpl: add 'disable = ' support
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ gentpl.py | 14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/gentpl.py b/gentpl.py
+index 2cba0bbbd6f..628e8bec1d7 100644
+--- a/gentpl.py
++++ b/gentpl.py
+@@ -592,11 +592,21 @@ def platform_conditional(platform, closure):
+ #  };
+ #
+ def foreach_enabled_platform(defn, closure):
++    enabled = False
++    disabled = False
+     if 'enable' in defn:
++        enabled = True
+         for platform in GRUB_PLATFORMS:
+             if platform_tagged(defn, platform, "enable"):
+                platform_conditional(platform, closure)
+-    else:
++
++    if 'disable' in defn:
++        disabled = True
++        for platform in GRUB_PLATFORMS:
++            if not platform_tagged(defn, platform, "disable"):
++                platform_conditional(platform, closure)
++
++    if not enabled and not disabled:
+         for platform in GRUB_PLATFORMS:
+             platform_conditional(platform, closure)
+ 
+@@ -655,6 +665,8 @@ def first_time(defn, snippet):
+ def is_platform_independent(defn):
+     if 'enable' in defn:
+         return False
++    if 'disable' in defn:
++        return False
+     for suffix in [ "", "_nodist" ]:
+         template = platform_values(defn, GRUB_PLATFORMS[0], suffix)
+         for platform in GRUB_PLATFORMS[1:]:
diff --git a/SOURCES/0102-gentpl-add-pc-firmware-type.patch b/SOURCES/0102-gentpl-add-pc-firmware-type.patch
new file mode 100644
index 0000000..96dd2b8
--- /dev/null
+++ b/SOURCES/0102-gentpl-add-pc-firmware-type.patch
@@ -0,0 +1,22 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 11 Jul 2019 11:04:24 +0200
+Subject: [PATCH] gentpl: add 'pc' firmware type
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ gentpl.py | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/gentpl.py b/gentpl.py
+index 628e8bec1d7..34a4eba2b42 100644
+--- a/gentpl.py
++++ b/gentpl.py
+@@ -51,6 +51,7 @@ GROUPS["riscv32"]  = [ "riscv32_efi" ]
+ GROUPS["riscv64"]  = [ "riscv64_efi" ]
+ 
+ # Groups based on firmware
++GROUPS["pc"] = [ "i386_pc" ]
+ GROUPS["efi"]  = [ "i386_efi", "x86_64_efi", "ia64_efi", "arm_efi", "arm64_efi",
+ 		   "riscv32_efi", "riscv64_efi" ]
+ GROUPS["ieee1275"]   = [ "i386_ieee1275", "sparc64_ieee1275", "powerpc_ieee1275" ]
diff --git a/SOURCES/0103-efinet-also-use-the-firmware-acceleration-for-http.patch b/SOURCES/0103-efinet-also-use-the-firmware-acceleration-for-http.patch
new file mode 100644
index 0000000..915b5aa
--- /dev/null
+++ b/SOURCES/0103-efinet-also-use-the-firmware-acceleration-for-http.patch
@@ -0,0 +1,25 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 30 Jul 2018 14:06:42 -0400
+Subject: [PATCH] efinet: also use the firmware acceleration for http
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/net/efi/net.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/net/efi/net.c b/grub-core/net/efi/net.c
+index 4bb308026ce..6603cd83edc 100644
+--- a/grub-core/net/efi/net.c
++++ b/grub-core/net/efi/net.c
+@@ -1324,7 +1324,9 @@ grub_efi_net_boot_from_https (void)
+ 	  && (subtype == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE))
+ 	{
+ 	  grub_efi_uri_device_path_t *uri_dp = (grub_efi_uri_device_path_t *) dp;
+-	  return (grub_strncmp ((const char*)uri_dp->uri, "https://", sizeof ("https://") - 1) == 0) ? 1 : 0;
++	  grub_dprintf ("efinet", "url:%s\n", (const char *)uri_dp->uri);
++	  return (grub_strncmp ((const char *)uri_dp->uri, "https://", sizeof ("https://") - 1) == 0 ||
++	          grub_strncmp ((const char *)uri_dp->uri, "http://", sizeof ("http://") - 1) == 0);
+ 	}
+ 
+       if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp))
diff --git a/SOURCES/0104-efi-http-Make-root_url-reflect-the-protocol-hostname.patch b/SOURCES/0104-efi-http-Make-root_url-reflect-the-protocol-hostname.patch
new file mode 100644
index 0000000..985a037
--- /dev/null
+++ b/SOURCES/0104-efi-http-Make-root_url-reflect-the-protocol-hostname.patch
@@ -0,0 +1,50 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 30 Jul 2018 16:39:57 -0400
+Subject: [PATCH] efi/http: Make root_url reflect the protocol+hostname of our
+ boot url.
+
+This lets you write config files that don't know urls.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/net/efi/http.c | 19 +++++++++++++++++++
+ 1 file changed, 19 insertions(+)
+
+diff --git a/grub-core/net/efi/http.c b/grub-core/net/efi/http.c
+index 3f61fd2fa5b..243acbaa35b 100644
+--- a/grub-core/net/efi/http.c
++++ b/grub-core/net/efi/http.c
+@@ -4,6 +4,7 @@
+ #include <grub/misc.h>
+ #include <grub/net/efi.h>
+ #include <grub/charset.h>
++#include <grub/env.h>
+ 
+ static void
+ http_configure (struct grub_efi_net_device *dev, int prefer_ip6)
+@@ -351,6 +352,24 @@ grub_efihttp_open (struct grub_efi_net_device *dev,
+   grub_err_t err;
+   grub_off_t size;
+   char *buf;
++  char *root_url;
++  grub_efi_ipv6_address_t address;
++  const char *rest;
++
++  if (grub_efi_string_to_ip6_address (file->device->net->server, &address, &rest) && *rest == 0)
++    root_url = grub_xasprintf ("%s://[%s]", type ? "https" : "http", file->device->net->server);
++  else
++    root_url = grub_xasprintf ("%s://%s", type ? "https" : "http", file->device->net->server);
++  if (root_url)
++    {
++      grub_env_unset ("root_url");
++      grub_env_set ("root_url", root_url);
++      grub_free (root_url);
++    }
++  else
++    {
++      return grub_errno;
++    }
+ 
+   err = efihttp_request (dev->http, file->device->net->server, file->device->net->name, type, 1, 0);
+   if (err != GRUB_ERR_NONE)
diff --git a/SOURCES/0105-Make-it-so-we-can-tell-configure-which-cflags-utils-.patch b/SOURCES/0105-Make-it-so-we-can-tell-configure-which-cflags-utils-.patch
new file mode 100644
index 0000000..b65eafc
--- /dev/null
+++ b/SOURCES/0105-Make-it-so-we-can-tell-configure-which-cflags-utils-.patch
@@ -0,0 +1,149 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 26 Jun 2018 17:16:06 -0400
+Subject: [PATCH] Make it so we can tell configure which cflags utils are built
+ with
+
+This lets us have kernel.img be built with TARGET_CFLAGS but grub-mkimage and
+friends built with HOST_CFLAGS.  That in turn lets us build with an ARM compiler
+that only has hard-float ABI versions of crt*.o and libgcc*, but still use soft
+float for grub.efi.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ configure.ac         | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-
+ conf/Makefile.common | 23 ++++++++++++-----------
+ gentpl.py            |  8 ++++----
+ 3 files changed, 64 insertions(+), 16 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index b4455e4732d..3405348178a 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -877,11 +877,23 @@ if ( test "x$target_cpu" = xi386 || test "x$target_cpu" = xx86_64 ) && test "x$p
+   TARGET_CFLAGS="$TARGET_CFLAGS -mno-mmx -mno-sse -mno-sse2 -mno-sse3 -mno-3dnow"
+ fi
+ 
++# Should grub utils get the host CFLAGS, or the target CFLAGS?
++AC_ARG_WITH([utils],
++            AS_HELP_STRING([--with-utils=host|target|build],
++                           [choose which flags to build utilities with. (default=target)]),
++	    [have_with_utils=y],
++	    [have_with_utils=n])
++if test x"$have_with_utils" = xy ; then
++  with_utils="$withval"
++else
++  with_utils=target
++fi
++
+ # GRUB doesn't use float or doubles at all. Yet some toolchains may decide
+ # that floats are a good fit to run instead of what's written in the code.
+ # Given that floating point unit is disabled (if present to begin with)
+ # when GRUB is running which may result in various hard crashes.
+-if test x"$platform" != xemu ; then
++if test x"$platform" != xemu -a x"$with_utils" == xtarget ; then
+   AC_CACHE_CHECK([for options to get soft-float], grub_cv_target_cc_soft_float, [
+     grub_cv_target_cc_soft_float=no
+     if test "x$target_cpu" = xarm64; then
+@@ -2018,6 +2030,41 @@ HOST_CPPFLAGS="$HOST_CPPFLAGS -I\$(top_builddir)/include"
+ TARGET_CPPFLAGS="$TARGET_CPPFLAGS -I\$(top_srcdir)/include"
+ TARGET_CPPFLAGS="$TARGET_CPPFLAGS -I\$(top_builddir)/include"
+ 
++case "$with_utils" in
++  host)
++    UTILS_CFLAGS=$HOST_CFLAGS
++    UTILS_CPPFLAGS=$HOST_CPPFLAGS
++    UTILS_CCASFLAGS=$HOST_CCASFLAGS
++    UTILS_LDFLAGS=$HOST_LDFLAGS
++    ;;
++  target)
++    UTILS_CFLAGS=$TARGET_CFLAGS
++    UTILS_CPPFLAGS=$TARGET_CPPFLAGS
++    UTILS_CCASFLAGS=$TARGET_CCASFLAGS
++    UTILS_LDFLAGS=$TARGET_LDFLAGS
++    ;;
++  build)
++    UTILS_CFLAGS=$BUILD_CFLAGS
++    UTILS_CPPFLAGS=$BUILD_CPPFLAGS
++    UTILS_CCASFLAGS=$BUILD_CCASFLAGS
++    UTILS_LDFLAGS=$BUILD_LDFLAGS
++    ;;
++  *)
++    AC_MSG_ERROR([--with-utils must be either host, target, or build])
++    ;;
++esac
++AC_MSG_NOTICE([Using $with_utils flags for utilities.])
++
++unset CFLAGS
++unset CPPFLAGS
++unset CCASFLAGS
++unset LDFLAGS
++
++AC_SUBST(UTILS_CFLAGS)
++AC_SUBST(UTILS_CPPFLAGS)
++AC_SUBST(UTILS_CCASFLAGS)
++AC_SUBST(UTILS_LDFLAGS)
++
+ GRUB_TARGET_CPU="${target_cpu}"
+ GRUB_PLATFORM="${platform}"
+ 
+diff --git a/conf/Makefile.common b/conf/Makefile.common
+index 5f0ef969857..2ff9b39357c 100644
+--- a/conf/Makefile.common
++++ b/conf/Makefile.common
+@@ -40,24 +40,25 @@ CPPFLAGS_KERNEL = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) -DGRUB_KERNEL=1
+ CCASFLAGS_KERNEL = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM)
+ STRIPFLAGS_KERNEL = -R .eh_frame -R .rel.dyn -R .reginfo -R .note -R .comment -R .drectve -R .note.gnu.gold-version -R .MIPS.abiflags -R .ARM.exidx -R .note.gnu.property -R .gnu.build.attributes
+ 
+-CFLAGS_MODULE = $(CFLAGS_PLATFORM) -ffreestanding
+-LDFLAGS_MODULE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-r,-d
+-CPPFLAGS_MODULE = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM)
+-CCASFLAGS_MODULE = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM)
++CFLAGS_MODULE = $(TARGET_CFLAGS) $(CFLAGS_PLATFORM) -ffreestanding
++LDFLAGS_MODULE = $(TARGET_LDFLAGS) $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-r,-d
++CPPFLAGS_MODULE = $(TARGET_CPPFLAGS) $(CPPFLAGS_DEFAULT) $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM)
++CCASFLAGS_MODULE = $(TARGET_CCASFLAGS) $(CCASFLAGS_DEFAULT) $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM)
+ 
+ CFLAGS_IMAGE = $(CFLAGS_PLATFORM) -fno-builtin
+ LDFLAGS_IMAGE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-S
+ CPPFLAGS_IMAGE = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM)
+ CCASFLAGS_IMAGE = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM)
+ 
+-CFLAGS_PROGRAM =
+-LDFLAGS_PROGRAM =
+-CPPFLAGS_PROGRAM =
+-CCASFLAGS_PROGRAM =
++CFLAGS_PROGRAM = $(UTILS_CFLAGS)
++LDFLAGS_PROGRAM = $(UTILS_LDFLAGS)
++CPPFLAGS_PROGRAM = $(UTILS_CPPFLAGS)
++CCASFLAGS_PROGRAM = $(UTILS_CCASFLAGS)
+ 
+-CFLAGS_LIBRARY =
+-CPPFLAGS_LIBRARY =
+-CCASFLAGS_LIBRARY =
++CFLAGS_LIBRARY = $(UTILS_CFLAGS)
++LDFLAGS_LIBRARY = $(UTILS_LDFLAGS)
++CPPFLAGS_LIBRARY = $(UTILS_CPPFLAGS)
++CCASFLAGS_LIBRARY = $(UTILS_CCASFLAGS)
+ 
+ # Other variables
+ 
+diff --git a/gentpl.py b/gentpl.py
+index 34a4eba2b42..59f62ef9522 100644
+--- a/gentpl.py
++++ b/gentpl.py
+@@ -697,10 +697,10 @@ def module(defn, platform):
+     var_set(cname(defn) + "_SOURCES", platform_sources(defn, platform) + " ## platform sources")
+     var_set("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform) + " ## platform nodist sources")
+     var_set(cname(defn) + "_LDADD", platform_ldadd(defn, platform))
+-    var_set(cname(defn) + "_CFLAGS", "$(AM_CFLAGS) $(CFLAGS_MODULE) " + platform_cflags(defn, platform))
+-    var_set(cname(defn) + "_LDFLAGS", "$(AM_LDFLAGS) $(LDFLAGS_MODULE) " + platform_ldflags(defn, platform))
+-    var_set(cname(defn) + "_CPPFLAGS", "$(AM_CPPFLAGS) $(CPPFLAGS_MODULE) " + platform_cppflags(defn, platform))
+-    var_set(cname(defn) + "_CCASFLAGS", "$(AM_CCASFLAGS) $(CCASFLAGS_MODULE) " + platform_ccasflags(defn, platform))
++    var_set(cname(defn) + "_CFLAGS", "$(CFLAGS_MODULE) " + platform_cflags(defn, platform))
++    var_set(cname(defn) + "_LDFLAGS", "$(LDFLAGS_MODULE) " + platform_ldflags(defn, platform))
++    var_set(cname(defn) + "_CPPFLAGS", "$(CPPFLAGS_MODULE) " + platform_cppflags(defn, platform))
++    var_set(cname(defn) + "_CCASFLAGS", "$(CCASFLAGS_MODULE) " + platform_ccasflags(defn, platform))
+     var_set(cname(defn) + "_DEPENDENCIES", "$(TARGET_OBJ2ELF) " + platform_dependencies(defn, platform))
+ 
+     gvar_add("dist_noinst_DATA", extra_dist(defn))
diff --git a/SOURCES/0106-module-verifier-make-it-possible-to-run-checkers-on-.patch b/SOURCES/0106-module-verifier-make-it-possible-to-run-checkers-on-.patch
new file mode 100644
index 0000000..e31b38f
--- /dev/null
+++ b/SOURCES/0106-module-verifier-make-it-possible-to-run-checkers-on-.patch
@@ -0,0 +1,58 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Wed, 1 Aug 2018 10:24:52 -0400
+Subject: [PATCH] module-verifier: make it possible to run checkers on
+ grub-module-verifierxx.c
+
+This makes it so you can treat grub-module-verifierxx.c as a file you can
+build directly, so syntax checkers like vim's "syntastic" plugin, which uses
+"gcc -x c -fsyntax-only" to build it, will work.
+
+One still has to do whatever setup is required to make it pick the right
+include dirs, which -W options we use, etc., but this makes it so you can do
+the checking on the file you're editing, rather than on a different file.
+
+v2: fix the typo in the #else clause in util/grub-module-verifierXX.c
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ util/grub-module-verifier32.c | 2 ++
+ util/grub-module-verifier64.c | 2 ++
+ util/grub-module-verifierXX.c | 9 +++++++++
+ 3 files changed, 13 insertions(+)
+
+diff --git a/util/grub-module-verifier32.c b/util/grub-module-verifier32.c
+index 257229f8f08..ba7d41aafea 100644
+--- a/util/grub-module-verifier32.c
++++ b/util/grub-module-verifier32.c
+@@ -1,2 +1,4 @@
+ #define MODULEVERIFIER_ELF32 1
++#ifndef GRUB_MODULE_VERIFIERXX
+ #include "grub-module-verifierXX.c"
++#endif
+diff --git a/util/grub-module-verifier64.c b/util/grub-module-verifier64.c
+index 4db6b4bedd1..fc23ef800b3 100644
+--- a/util/grub-module-verifier64.c
++++ b/util/grub-module-verifier64.c
+@@ -1,2 +1,4 @@
+ #define MODULEVERIFIER_ELF64 1
++#ifndef GRUB_MODULE_VERIFIERXX
+ #include "grub-module-verifierXX.c"
++#endif
+diff --git a/util/grub-module-verifierXX.c b/util/grub-module-verifierXX.c
+index ceb24309aec..a98e2f9b1ac 100644
+--- a/util/grub-module-verifierXX.c
++++ b/util/grub-module-verifierXX.c
+@@ -1,3 +1,12 @@
++#define GRUB_MODULE_VERIFIERXX
++#if !defined(MODULEVERIFIER_ELF32) && !defined(MODULEVERIFIER_ELF64)
++#if __SIZEOF_POINTER__ == 8
++#include "grub-module-verifier64.c"
++#else
++#include "grub-module-verifier32.c"
++#endif
++#endif
++
+ #include <string.h>
+ 
+ #include <grub/elf.h>
diff --git a/SOURCES/0107-Rework-how-the-fdt-command-builds.patch b/SOURCES/0107-Rework-how-the-fdt-command-builds.patch
new file mode 100644
index 0000000..71328da
--- /dev/null
+++ b/SOURCES/0107-Rework-how-the-fdt-command-builds.patch
@@ -0,0 +1,118 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 11 Jul 2019 13:01:41 +0200
+Subject: [PATCH] Rework how the fdt command builds.
+
+Trying to avoid all variants of:
+cat syminfo.lst | sort | gawk -f ../../grub-core/genmoddep.awk > moddep.lst || (rm -f moddep.lst; exit 1)
+grub_fdt_install in linux is not defined
+grub_fdt_load in linux is not defined
+grub_fdt_unload in linux is not defined
+grub_fdt_install in xen_boot is not defined
+grub_fdt_load in xen_boot is not defined
+grub_fdt_unload in xen_boot is not defined
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/Makefile.core.def | 5 ++---
+ grub-core/lib/fdt.c         | 2 --
+ grub-core/loader/efi/fdt.c  | 2 ++
+ include/grub/fdt.h          | 4 ++++
+ grub-core/Makefile.am       | 1 +
+ 5 files changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index c40170f2dd2..84a3d89de9a 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -177,7 +177,6 @@ kernel = {
+   arm_coreboot = kern/arm/coreboot/init.c;
+   arm_coreboot = kern/arm/coreboot/timer.c;
+   arm_coreboot = kern/arm/coreboot/coreboot.S;
+-  arm_coreboot = lib/fdt.c;
+   arm_coreboot = bus/fdt.c;
+   arm_coreboot = term/ps2.c;
+   arm_coreboot = term/arm/pl050.c;
+@@ -351,6 +350,8 @@ kernel = {
+   riscv64 = kern/riscv/cache_flush.S;
+   riscv64 = kern/riscv/dl.c;
+ 
++  fdt = lib/fdt.c;
++
+   emu = disk/host.c;
+   emu = kern/emu/cache_s.S;
+   emu = kern/emu/hostdisk.c;
+@@ -1825,7 +1826,6 @@ module = {
+   riscv32 = loader/riscv/linux.c;
+   riscv64 = loader/riscv/linux.c;
+   emu = loader/emu/linux.c;
+-  fdt = lib/fdt.c;
+ 
+   common = loader/linux.c;
+   common = lib/cmdline.c;
+@@ -1836,7 +1836,6 @@ module = {
+ module = {
+   name = fdt;
+   efi = loader/efi/fdt.c;
+-  common = lib/fdt.c;
+   enable = fdt;
+ };
+ 
+diff --git a/grub-core/lib/fdt.c b/grub-core/lib/fdt.c
+index 0d371c5633e..37e04bd69e7 100644
+--- a/grub-core/lib/fdt.c
++++ b/grub-core/lib/fdt.c
+@@ -21,8 +21,6 @@
+ #include <grub/mm.h>
+ #include <grub/dl.h>
+ 
+-GRUB_MOD_LICENSE ("GPLv3+");
+-
+ #define FDT_SUPPORTED_VERSION	17
+ 
+ #define FDT_BEGIN_NODE	0x00000001
+diff --git a/grub-core/loader/efi/fdt.c b/grub-core/loader/efi/fdt.c
+index c86f283d756..c572415d38a 100644
+--- a/grub-core/loader/efi/fdt.c
++++ b/grub-core/loader/efi/fdt.c
+@@ -27,6 +27,8 @@
+ #include <grub/efi/memory.h>
+ #include <grub/cpu/efi/memory.h>
+ 
++GRUB_MOD_LICENSE ("GPLv3+");
++
+ static void *loaded_fdt;
+ static void *fdt;
+ 
+diff --git a/include/grub/fdt.h b/include/grub/fdt.h
+index e609c7e4111..22b7c5463fc 100644
+--- a/include/grub/fdt.h
++++ b/include/grub/fdt.h
+@@ -19,6 +19,8 @@
+ #ifndef GRUB_FDT_HEADER
+ #define GRUB_FDT_HEADER	1
+ 
++#if defined(__arm__) || defined(__aarch64__)
++
+ #include <grub/types.h>
+ #include <grub/symbol.h>
+ 
+@@ -144,4 +146,6 @@ int EXPORT_FUNC(grub_fdt_set_prop) (void *fdt, unsigned int nodeoffset, const ch
+   grub_fdt_set_prop ((fdt), (nodeoffset), "reg", reg_64, 16);  \
+ })
+ 
++#endif /* defined(__arm__) || defined(__aarch64__) */
++
+ #endif	/* ! GRUB_FDT_HEADER */
+diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
+index f512573c0da..dd49939aaa9 100644
+--- a/grub-core/Makefile.am
++++ b/grub-core/Makefile.am
+@@ -76,6 +76,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/sb.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/env.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/env_private.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/err.h
++KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/fdt.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/file.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/fs.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i18n.h
diff --git a/SOURCES/0108-Disable-non-wordsize-allocations-on-arm.patch b/SOURCES/0108-Disable-non-wordsize-allocations-on-arm.patch
new file mode 100644
index 0000000..f614c9e
--- /dev/null
+++ b/SOURCES/0108-Disable-non-wordsize-allocations-on-arm.patch
@@ -0,0 +1,41 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 2 Aug 2018 10:56:38 -0400
+Subject: [PATCH] Disable non-wordsize allocations on arm
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ configure.ac | 20 ++++++++++++++++++++
+ 1 file changed, 20 insertions(+)
+
+diff --git a/configure.ac b/configure.ac
+index 3405348178a..152e7dba652 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1288,6 +1288,26 @@ if test "x$target_cpu" = xarm; then
+     done
+   ])
+ 
++  AC_CACHE_CHECK([for options to disable movt and movw relocations],
++		 grub_cv_target_cc_mword_relocations,
++		 [grub_cv_target_cc_mword_relocations=no
++		  for cand in "-mword-relocations" ; do
++		    if test x"$grub_cv_target_cc_mword_relocations" != xno ; then
++		      break
++		    fi
++		    CFLAGS="$TARGET_CFLAGS $cand -Werror"
++		    CPPFLAGS="$TARGET_CPPFLAGS"
++		    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
++				      [grub_cv_target_cc_mword_relocations="$cand"],
++				      [])
++		  done
++		 ])
++  if test x"$grub_cv_target_cc_mword_relocations" = xno ; then
++    AC_MSG_ERROR(["your compiler doesn't support disabling movw/movt relocations"])
++  else
++    TARGET_CFLAGS="$TARGET_CFLAGS $grub_cv_target_cc_mword_relocations"
++  fi
++
+   if test x"$grub_cv_target_cc_mno_movt" != xno ; then
+     # A trick so that clang doesn't see it on link stage
+     TARGET_CPPFLAGS="$TARGET_CPPFLAGS $grub_cv_target_cc_mno_movt"
diff --git a/SOURCES/0109-Prepend-prefix-when-HTTP-path-is-relative.patch b/SOURCES/0109-Prepend-prefix-when-HTTP-path-is-relative.patch
new file mode 100644
index 0000000..fc861d9
--- /dev/null
+++ b/SOURCES/0109-Prepend-prefix-when-HTTP-path-is-relative.patch
@@ -0,0 +1,150 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Stephen Benjamin <stephen@redhat.com>
+Date: Thu, 16 Aug 2018 16:58:51 -0400
+Subject: [PATCH] Prepend prefix when HTTP path is relative
+
+This sets a couple of variables.  With the url http://www.example.com/foo/bar :
+http_path: /foo/bar
+http_url: http://www.example.com/foo/bar
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/kern/main.c    | 10 +++++-
+ grub-core/net/efi/http.c | 82 ++++++++++++++++++++++++++++++++++++------------
+ 2 files changed, 71 insertions(+), 21 deletions(-)
+
+diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c
+index 48058d983ce..4ec3f5e4d33 100644
+--- a/grub-core/kern/main.c
++++ b/grub-core/kern/main.c
+@@ -131,11 +131,19 @@ grub_set_prefix_and_root (void)
+   if (fwdevice && fwpath)
+     {
+       char *fw_path;
++      char separator[3] = ")";
+ 
+-      fw_path = grub_xasprintf ("(%s)/%s", fwdevice, fwpath);
++      grub_dprintf ("fw_path", "\n");
++      grub_dprintf ("fw_path", "fwdevice:\"%s\" fwpath:\"%s\"\n", fwdevice, fwpath);
++
++      if (!grub_strncmp(fwdevice, "http", 4) && fwpath[0] != '/')
++	grub_strcpy(separator, ")/");
++
++      fw_path = grub_xasprintf ("(%s%s%s", fwdevice, separator, fwpath);
+       if (fw_path)
+ 	{
+ 	  grub_env_set ("fw_path", fw_path);
++	  grub_dprintf ("fw_path", "fw_path:\"%s\"\n", fw_path);
+ 	  grub_free (fw_path);
+ 	}
+     }
+diff --git a/grub-core/net/efi/http.c b/grub-core/net/efi/http.c
+index 243acbaa35b..de351b2cd03 100644
+--- a/grub-core/net/efi/http.c
++++ b/grub-core/net/efi/http.c
+@@ -9,10 +9,52 @@
+ static void
+ http_configure (struct grub_efi_net_device *dev, int prefer_ip6)
+ {
++  grub_efi_ipv6_address_t address;
+   grub_efi_http_config_data_t http_config;
+   grub_efi_httpv4_access_point_t httpv4_node;
+   grub_efi_httpv6_access_point_t httpv6_node;
+   grub_efi_status_t status;
++  int https;
++  char *http_url;
++  const char *rest, *http_server, *http_path = NULL;
++
++  http_server = grub_env_get ("root");
++  https = (grub_strncmp (http_server, "https", 5) == 0) ? 1 : 0;
++
++  /* extract http server + port */
++  if (http_server)
++    {
++      http_server = grub_strchr (http_server, ',');
++      if (http_server)
++	http_server++;
++    }
++
++  /* fw_path is like (http,192.168.1.1:8000)/httpboot, extract path part */
++  http_path = grub_env_get ("fw_path");
++  if (http_path)
++    {
++      http_path = grub_strchr (http_path, ')');
++      if (http_path)
++	{
++	  http_path++;
++	  grub_env_unset ("http_path");
++	  grub_env_set ("http_path", http_path);
++	}
++    }
++
++  if (http_server && http_path)
++    {
++      if (grub_efi_string_to_ip6_address (http_server, &address, &rest) && *rest == 0)
++	http_url = grub_xasprintf ("%s://[%s]%s", https ? "https" : "http", http_server, http_path);
++      else
++	http_url = grub_xasprintf ("%s://%s%s", https ? "https" : "http", http_server, http_path);
++      if (http_url)
++	{
++	  grub_env_unset ("http_url");
++	  grub_env_set ("http_url", http_url);
++	  grub_free (http_url);
++	}
++    }
+ 
+   grub_efi_http_t *http = dev->http;
+ 
+@@ -352,32 +394,32 @@ grub_efihttp_open (struct grub_efi_net_device *dev,
+   grub_err_t err;
+   grub_off_t size;
+   char *buf;
+-  char *root_url;
+-  grub_efi_ipv6_address_t address;
+-  const char *rest;
++  char *file_name = NULL;
++  const char *http_path;
+ 
+-  if (grub_efi_string_to_ip6_address (file->device->net->server, &address, &rest) && *rest == 0)
+-    root_url = grub_xasprintf ("%s://[%s]", type ? "https" : "http", file->device->net->server);
+-  else
+-    root_url = grub_xasprintf ("%s://%s", type ? "https" : "http", file->device->net->server);
+-  if (root_url)
+-    {
+-      grub_env_unset ("root_url");
+-      grub_env_set ("root_url", root_url);
+-      grub_free (root_url);
+-    }
+-  else
+-    {
++  /* If path is relative, prepend http_path */
++  http_path = grub_env_get ("http_path");
++  if (http_path && file->device->net->name[0] != '/') {
++    file_name = grub_xasprintf ("%s/%s", http_path, file->device->net->name);
++    if (!file_name)
+       return grub_errno;
+-    }
++  }
+ 
+-  err = efihttp_request (dev->http, file->device->net->server, file->device->net->name, type, 1, 0);
++  err = efihttp_request (dev->http, file->device->net->server,
++			 file_name ? file_name : file->device->net->name, type, 1, 0);
+   if (err != GRUB_ERR_NONE)
+-    return err;
++    {
++      grub_free (file_name);
++      return err;
++    }
+ 
+-  err = efihttp_request (dev->http, file->device->net->server, file->device->net->name, type, 0, &size);
++  err = efihttp_request (dev->http, file->device->net->server,
++			 file_name ? file_name : file->device->net->name, type, 0, &size);
++  grub_free (file_name);
+   if (err != GRUB_ERR_NONE)
+-    return err;
++    {
++      return err;
++    }
+ 
+   buf = grub_malloc (size);
+   efihttp_read (dev, buf, size);
diff --git a/SOURCES/0110-Make-grub_error-more-verbose.patch b/SOURCES/0110-Make-grub_error-more-verbose.patch
new file mode 100644
index 0000000..2492e9f
--- /dev/null
+++ b/SOURCES/0110-Make-grub_error-more-verbose.patch
@@ -0,0 +1,61 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 27 Aug 2018 13:14:06 -0400
+Subject: [PATCH] Make grub_error() more verbose
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/kern/err.c | 13 +++++++++++--
+ include/grub/err.h   |  8 ++++++--
+ 2 files changed, 17 insertions(+), 4 deletions(-)
+
+diff --git a/grub-core/kern/err.c b/grub-core/kern/err.c
+index 53c734de70e..aebfe0cf839 100644
+--- a/grub-core/kern/err.c
++++ b/grub-core/kern/err.c
+@@ -33,15 +33,24 @@ static struct grub_error_saved grub_error_stack_items[GRUB_ERROR_STACK_SIZE];
+ static int grub_error_stack_pos;
+ static int grub_error_stack_assert;
+ 
++#ifdef grub_error
++#undef grub_error
++#endif
++
+ grub_err_t
+-grub_error (grub_err_t n, const char *fmt, ...)
++grub_error (grub_err_t n, const char *file, const int line, const char *fmt, ...)
+ {
+   va_list ap;
++  int m;
+ 
+   grub_errno = n;
+ 
++  m = grub_snprintf (grub_errmsg, sizeof (grub_errmsg), "%s:%d:", file, line);
++  if (m < 0)
++    m = 0;
++
+   va_start (ap, fmt);
+-  grub_vsnprintf (grub_errmsg, sizeof (grub_errmsg), _(fmt), ap);
++  grub_vsnprintf (grub_errmsg + m, sizeof (grub_errmsg) - m, _(fmt), ap);
+   va_end (ap);
+ 
+   return n;
+diff --git a/include/grub/err.h b/include/grub/err.h
+index b08d5d0de4c..c0f90ef07c8 100644
+--- a/include/grub/err.h
++++ b/include/grub/err.h
+@@ -85,8 +85,12 @@ struct grub_error_saved
+ extern grub_err_t EXPORT_VAR(grub_errno);
+ extern char EXPORT_VAR(grub_errmsg)[GRUB_MAX_ERRMSG];
+ 
+-grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *fmt, ...)
+-    __attribute__ ((format (GNU_PRINTF, 2, 3)));
++grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *file, const int line, const char *fmt, ...)
++	__attribute__ ((format (GNU_PRINTF, 4, 5)));
++
++#define grub_error(n, fmt, ...) grub_error (n, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
++
++
+ void EXPORT_FUNC(grub_fatal) (const char *fmt, ...) __attribute__ ((noreturn));
+ void EXPORT_FUNC(grub_error_push) (void);
+ int EXPORT_FUNC(grub_error_pop) (void);
diff --git a/SOURCES/0111-Make-reset-an-alias-for-the-reboot-command.patch b/SOURCES/0111-Make-reset-an-alias-for-the-reboot-command.patch
new file mode 100644
index 0000000..c86acf1
--- /dev/null
+++ b/SOURCES/0111-Make-reset-an-alias-for-the-reboot-command.patch
@@ -0,0 +1,40 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Fri, 31 Aug 2018 16:42:03 -0400
+Subject: [PATCH] Make "reset" an alias for the "reboot" command.
+
+I'm really tired of half the tools I get to use having one and the other half
+having the other.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/commands/reboot.c | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+diff --git a/grub-core/commands/reboot.c b/grub-core/commands/reboot.c
+index 46d364c99a9..f5cc2283636 100644
+--- a/grub-core/commands/reboot.c
++++ b/grub-core/commands/reboot.c
+@@ -32,15 +32,18 @@ grub_cmd_reboot (grub_command_t cmd __attribute__ ((unused)),
+   grub_reboot ();
+ }
+ 
+-static grub_command_t cmd;
++static grub_command_t reboot_cmd, reset_cmd;
+ 
+ GRUB_MOD_INIT(reboot)
+ {
+-  cmd = grub_register_command ("reboot", grub_cmd_reboot,
+-			       0, N_("Reboot the computer."));
++  reboot_cmd = grub_register_command ("reboot", grub_cmd_reboot,
++				      0, N_("Reboot the computer."));
++  reset_cmd = grub_register_command ("reset", grub_cmd_reboot,
++				     0, N_("Reboot the computer."));
+ }
+ 
+ GRUB_MOD_FINI(reboot)
+ {
+-  grub_unregister_command (cmd);
++  grub_unregister_command (reboot_cmd);
++  grub_unregister_command (reset_cmd);
+ }
diff --git a/SOURCES/0112-Add-a-version-command.patch b/SOURCES/0112-Add-a-version-command.patch
new file mode 100644
index 0000000..30c8224
--- /dev/null
+++ b/SOURCES/0112-Add-a-version-command.patch
@@ -0,0 +1,132 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 11 Sep 2018 14:20:37 -0400
+Subject: [PATCH] Add a "version" command.
+
+This adds a command that shows you info about grub's version, the grub target
+platform, the compiler version, and if you built with
+--with-rpm-version=<string>, the rpm package version.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ configure.ac                 | 13 ++++++++++
+ grub-core/Makefile.core.def  |  5 ++++
+ grub-core/commands/version.c | 56 ++++++++++++++++++++++++++++++++++++++++++++
+ config.h.in                  |  1 +
+ 4 files changed, 75 insertions(+)
+ create mode 100644 grub-core/commands/version.c
+
+diff --git a/configure.ac b/configure.ac
+index 152e7dba652..cfdac6bed5a 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -312,6 +312,19 @@ AC_SUBST(target_cpu)
+ AC_SUBST(platform)
+ 
+ # Define default variables
++have_with_rpm_version=n
++AC_ARG_WITH([rpm_version],
++	    AS_HELP_STRING([--with-rpm-version=VERSION],
++			   [set the rpm package version [[guessed]]]),
++	    [have_with_rpm_version=y],
++	    [have_with_rpm_version=n])
++if test x$have_with_rpm_version = xy; then
++  rpm_version="$with_rpm_version"
++else
++  rpm_version=""
++fi
++GRUB_RPM_VERSION="$rpm_version"
++AC_SUBST(GRUB_RPM_VERSION)
+ 
+ have_with_bootdir=n
+ AC_ARG_WITH([bootdir],
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 84a3d89de9a..498ca11762a 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -579,6 +579,11 @@ image = {
+   enable = mips_loongson;
+ };
+ 
++module = {
++  name = version;
++  common = commands/version.c;
++};
++
+ module = {
+   name = disk;
+   common = lib/disk.c;
+diff --git a/grub-core/commands/version.c b/grub-core/commands/version.c
+new file mode 100644
+index 00000000000..f0966a518f7
+--- /dev/null
++++ b/grub-core/commands/version.c
+@@ -0,0 +1,56 @@
++/* version.c - Command to print the grub version and build info. */
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2006,2007,2008  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/dl.h>
++#include <grub/term.h>
++#include <grub/time.h>
++#include <grub/types.h>
++#include <grub/misc.h>
++#include <grub/extcmd.h>
++#include <grub/i18n.h>
++
++GRUB_MOD_LICENSE ("GPLv3+");
++
++static grub_err_t
++grub_cmd_version (grub_command_t cmd UNUSED, int argc, char **args UNUSED)
++{
++  if (argc != 0)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("no arguments expected"));
++
++  grub_printf (_("GNU GRUB  version %s\n"), PACKAGE_VERSION);
++  grub_printf (_("Platform %s-%s\n"), GRUB_TARGET_CPU, GRUB_PLATFORM);
++  if (grub_strlen(GRUB_RPM_VERSION) != 0)
++    grub_printf (_("RPM package version %s\n"), GRUB_RPM_VERSION);
++  grub_printf (_("Compiler version %s\n"), __VERSION__);
++
++  return 0;
++}
++
++static grub_command_t cmd;
++
++GRUB_MOD_INIT(version)
++{
++  cmd = grub_register_command ("version", grub_cmd_version, NULL,
++			       N_("Print version and build information."));
++}
++
++GRUB_MOD_FINI(version)
++{
++  grub_unregister_command (cmd);
++}
+diff --git a/config.h.in b/config.h.in
+index 9e8f9911b18..c7e316f0f1f 100644
+--- a/config.h.in
++++ b/config.h.in
+@@ -59,6 +59,7 @@
+ 
+ #define GRUB_TARGET_CPU "@GRUB_TARGET_CPU@"
+ #define GRUB_PLATFORM "@GRUB_PLATFORM@"
++#define GRUB_RPM_VERSION "@GRUB_RPM_VERSION@"
+ 
+ #define RE_ENABLE_I18N 1
+ 
diff --git a/SOURCES/0113-Add-more-dprintf-and-nerf-dprintf-in-script.c.patch b/SOURCES/0113-Add-more-dprintf-and-nerf-dprintf-in-script.c.patch
new file mode 100644
index 0000000..9afb315
--- /dev/null
+++ b/SOURCES/0113-Add-more-dprintf-and-nerf-dprintf-in-script.c.patch
@@ -0,0 +1,74 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 11 Sep 2018 15:58:29 -0400
+Subject: [PATCH] Add more dprintf, and nerf dprintf in script.c
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/disk/diskfilter.c  | 3 +++
+ grub-core/disk/efi/efidisk.c | 1 +
+ grub-core/kern/device.c      | 1 +
+ grub-core/script/script.c    | 5 +++++
+ 4 files changed, 10 insertions(+)
+
+diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c
+index 0320115662f..7cdffe3ebd5 100644
+--- a/grub-core/disk/diskfilter.c
++++ b/grub-core/disk/diskfilter.c
+@@ -188,6 +188,8 @@ scan_disk (const char *name, int accept_diskfilter)
+   grub_disk_t disk;
+   static int scan_depth = 0;
+ 
++  grub_dprintf ("diskfilter", "scanning %s\n", name);
++
+   if (!accept_diskfilter && is_valid_diskfilter_name (name))
+     return 0;
+ 
+@@ -1212,6 +1214,7 @@ insert_array (grub_disk_t disk, const struct grub_diskfilter_pv_id *id,
+ 	   the same.  */
+ 	if (pv->disk && grub_disk_native_sectors (disk) >= pv->part_size)
+ 	  return GRUB_ERR_NONE;
++	grub_dprintf ("diskfilter", "checking %s\n", disk->name);
+ 	pv->disk = grub_disk_open (disk->name);
+ 	if (!pv->disk)
+ 	  return grub_errno;
+diff --git a/grub-core/disk/efi/efidisk.c b/grub-core/disk/efi/efidisk.c
+index f077b5f5535..fe8ba6e6c93 100644
+--- a/grub-core/disk/efi/efidisk.c
++++ b/grub-core/disk/efi/efidisk.c
+@@ -855,6 +855,7 @@ grub_efidisk_get_device_name (grub_efi_handle_t *handle)
+ 	  return 0;
+ 	}
+ 
++      grub_dprintf ("efidisk", "getting disk for %s\n", device_name);
+       parent = grub_disk_open (device_name);
+       grub_free (dup_dp);
+ 
+diff --git a/grub-core/kern/device.c b/grub-core/kern/device.c
+index 73b8ecc0c09..f58b58c89d5 100644
+--- a/grub-core/kern/device.c
++++ b/grub-core/kern/device.c
+@@ -34,6 +34,7 @@ grub_device_open (const char *name)
+ {
+   grub_device_t dev = 0;
+ 
++  grub_dprintf ("device", "opening device %s\n", name);
+   if (! name)
+     {
+       name = grub_env_get ("root");
+diff --git a/grub-core/script/script.c b/grub-core/script/script.c
+index ec4d4337c66..844e8343ca7 100644
+--- a/grub-core/script/script.c
++++ b/grub-core/script/script.c
+@@ -22,6 +22,11 @@
+ #include <grub/parser.h>
+ #include <grub/mm.h>
+ 
++#ifdef grub_dprintf
++#undef grub_dprintf
++#endif
++#define grub_dprintf(no, fmt, ...)
++
+ /* It is not possible to deallocate the memory when a syntax error was
+    found.  Because of that it is required to keep track of all memory
+    allocations.  The memory is freed in case of an error, or assigned
diff --git a/SOURCES/0114-arm-arm64-loader-Better-memory-allocation-and-error-.patch b/SOURCES/0114-arm-arm64-loader-Better-memory-allocation-and-error-.patch
new file mode 100644
index 0000000..1fdc552
--- /dev/null
+++ b/SOURCES/0114-arm-arm64-loader-Better-memory-allocation-and-error-.patch
@@ -0,0 +1,279 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 11 Jul 2019 14:38:57 +0200
+Subject: [PATCH] arm/arm64 loader: Better memory allocation and error
+ messages.
+
+On mustang, our memory map looks like:
+
+Type      Physical start  - end             #Pages        Size Attributes
+reserved  0000004000000000-00000040001fffff 00000200      2MiB UC WC WT WB
+conv-mem  0000004000200000-0000004393ffffff 00393e00  14654MiB UC WC WT WB
+ldr-code  0000004394000000-00000043f7ffffff 00064000   1600MiB UC WC WT WB
+BS-data   00000043f8000000-00000043f801ffff 00000020    128KiB UC WC WT WB
+conv-mem  00000043f8020000-00000043fa15bfff 0000213c  34032KiB UC WC WT WB
+ldr-code  00000043fa15c000-00000043fa2a1fff 00000146   1304KiB UC WC WT WB
+ldr-data  00000043fa2a2000-00000043fa3e8fff 00000147   1308KiB UC WC WT WB
+conv-mem  00000043fa3e9000-00000043fa3e9fff 00000001      4KiB UC WC WT WB
+ldr-data  00000043fa3ea000-00000043fa3eafff 00000001      4KiB UC WC WT WB
+ldr-code  00000043fa3eb000-00000043fa4affff 000000c5    788KiB UC WC WT WB
+BS-code   00000043fa4b0000-00000043fa59ffff 000000f0    960KiB UC WC WT WB
+RT-code   00000043fa5a0000-00000043fa5affff 00000010     64KiB RT UC WC WT WB
+RT-data   00000043fa5b0000-00000043fa5bffff 00000010     64KiB RT UC WC WT WB
+RT-code   00000043fa5c0000-00000043fa5cffff 00000010     64KiB RT UC WC WT WB
+ldr-data  00000043fa5d0000-00000043fa5d0fff 00000001      4KiB UC WC WT WB
+BS-code   00000043fa5d1000-00000043fa5ddfff 0000000d     52KiB UC WC WT WB
+reserved  00000043fa5de000-00000043fa60ffff 00000032    200KiB UC WC WT WB
+ACPI-rec  00000043fa610000-00000043fa6affff 000000a0    640KiB UC WC WT WB
+ACPI-nvs  00000043fa6b0000-00000043fa6bffff 00000010     64KiB UC WC WT WB
+ACPI-rec  00000043fa6c0000-00000043fa70ffff 00000050    320KiB UC WC WT WB
+RT-code   00000043fa710000-00000043fa72ffff 00000020    128KiB RT UC WC WT WB
+RT-data   00000043fa730000-00000043fa78ffff 00000060    384KiB RT UC WC WT WB
+RT-code   00000043fa790000-00000043fa79ffff 00000010     64KiB RT UC WC WT WB
+RT-data   00000043fa7a0000-00000043fa99ffff 00000200      2MiB RT UC WC WT WB
+RT-code   00000043fa9a0000-00000043fa9affff 00000010     64KiB RT UC WC WT WB
+RT-data   00000043fa9b0000-00000043fa9cffff 00000020    128KiB RT UC WC WT WB
+BS-code   00000043fa9d0000-00000043fa9d9fff 0000000a     40KiB UC WC WT WB
+reserved  00000043fa9da000-00000043fa9dbfff 00000002      8KiB UC WC WT WB
+conv-mem  00000043fa9dc000-00000043fc29dfff 000018c2  25352KiB UC WC WT WB
+BS-data   00000043fc29e000-00000043fc78afff 000004ed   5044KiB UC WC WT WB
+conv-mem  00000043fc78b000-00000043fca01fff 00000277   2524KiB UC WC WT WB
+BS-data   00000043fca02000-00000043fcea3fff 000004a2   4744KiB UC WC WT WB
+conv-mem  00000043fcea4000-00000043fcea4fff 00000001      4KiB UC WC WT WB
+BS-data   00000043fcea5000-00000043fd192fff 000002ee   3000KiB UC WC WT WB
+conv-mem  00000043fd193000-00000043fd2b0fff 0000011e   1144KiB UC WC WT WB
+BS-data   00000043fd2b1000-00000043ff80ffff 0000255f  38268KiB UC WC WT WB
+BS-code   00000043ff810000-00000043ff99ffff 00000190   1600KiB UC WC WT WB
+RT-code   00000043ff9a0000-00000043ff9affff 00000010     64KiB RT UC WC WT WB
+conv-mem  00000043ff9b0000-00000043ff9bffff 00000010     64KiB UC WC WT WB
+RT-data   00000043ff9c0000-00000043ff9effff 00000030    192KiB RT UC WC WT WB
+conv-mem  00000043ff9f0000-00000043ffa05fff 00000016     88KiB UC WC WT WB
+BS-data   00000043ffa06000-00000043ffffffff 000005fa   6120KiB UC WC WT WB
+MMIO      0000000010510000-0000000010510fff 00000001      4KiB RT
+MMIO      0000000010548000-0000000010549fff 00000002      8KiB RT
+MMIO      0000000017000000-0000000017001fff 00000002      8KiB RT
+MMIO      000000001c025000-000000001c025fff 00000001      4KiB RT
+
+This patch adds a requirement when we're trying to find the base of ram, that
+the memory we choose is actually /allocatable/ conventional memory, not merely
+write-combining.  On this machine that means we wind up with an allocation
+around 0x4392XXXXXX, which is a reasonable address.
+
+This also changes grub_efi_allocate_pages_real() so that if 0 is allocated, it
+tries to allocate again starting with the same max address it did the first
+time, rather than interposing GRUB_EFI_MAX_USABLE_ADDRESS there, so that any
+per-platform constraints on its given address are maintained.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/kern/efi/mm.c        | 33 +++++++++++++++-----
+ grub-core/loader/arm64/linux.c | 68 +++++++++++++++++++++++++++++++-----------
+ 2 files changed, 76 insertions(+), 25 deletions(-)
+
+diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
+index f6aef0ef649..85ad4b4494c 100644
+--- a/grub-core/kern/efi/mm.c
++++ b/grub-core/kern/efi/mm.c
+@@ -154,6 +154,7 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address,
+ {
+   grub_efi_status_t status;
+   grub_efi_boot_services_t *b;
++  grub_efi_physical_address_t ret = address;
+ 
+   /* Limit the memory access to less than 4GB for 32-bit platforms.  */
+   if (address > GRUB_EFI_MAX_USABLE_ADDRESS)
+@@ -170,19 +171,22 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address,
+     }
+ 
+   b = grub_efi_system_table->boot_services;
+-  status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &address);
++  status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &ret);
+   if (status != GRUB_EFI_SUCCESS)
+     {
++      grub_dprintf ("efi",
++		    "allocate_pages(%d, %d, 0x%0lx, 0x%016lx) = 0x%016lx\n",
++		    alloctype, memtype, pages, address, status);
+       grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+       return NULL;
+     }
+ 
+-  if (address == 0)
++  if (ret == 0)
+     {
+       /* Uggh, the address 0 was allocated... This is too annoying,
+ 	 so reallocate another one.  */
+-      address = GRUB_EFI_MAX_USABLE_ADDRESS;
+-      status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &address);
++      ret = address;
++      status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &ret);
+       grub_efi_free_pages (0, pages);
+       if (status != GRUB_EFI_SUCCESS)
+ 	{
+@@ -191,9 +195,9 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address,
+ 	}
+     }
+ 
+-  grub_efi_store_alloc (address, pages);
++  grub_efi_store_alloc (ret, pages);
+ 
+-  return (void *) ((grub_addr_t) address);
++  return (void *) ((grub_addr_t) ret);
+ }
+ 
+ void *
+@@ -713,8 +717,21 @@ grub_efi_get_ram_base(grub_addr_t *base_addr)
+   for (desc = memory_map, *base_addr = GRUB_EFI_MAX_USABLE_ADDRESS;
+        (grub_addr_t) desc < ((grub_addr_t) memory_map + memory_map_size);
+        desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
+-    if (desc->attribute & GRUB_EFI_MEMORY_WB)
+-      *base_addr = grub_min (*base_addr, desc->physical_start);
++    {
++      if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY &&
++          (desc->attribute & GRUB_EFI_MEMORY_WB))
++        {
++          *base_addr = grub_min (*base_addr, desc->physical_start);
++          grub_dprintf ("efi", "setting base_addr=0x%016lx\n", *base_addr);
++        }
++      else
++        {
++          grub_dprintf ("efi", "ignoring address 0x%016lx\n", desc->physical_start);
++        }
++    }
++
++  if (*base_addr == GRUB_EFI_MAX_USABLE_ADDRESS)
++    grub_dprintf ("efi", "base_addr 0x%016lx is probably wrong.\n", *base_addr);
+ 
+   grub_free(memory_map);
+ 
+diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
+index 04994d5c67d..70a0075ec5e 100644
+--- a/grub-core/loader/arm64/linux.c
++++ b/grub-core/loader/arm64/linux.c
+@@ -71,20 +71,25 @@ finalize_params_linux (void)
+ {
+   grub_efi_loaded_image_t *loaded_image = NULL;
+   int node, retval, len;
+-
++  grub_err_t err = GRUB_ERR_NONE;
+   void *fdt;
+ 
+   fdt = grub_fdt_load (GRUB_EFI_LINUX_FDT_EXTRA_SPACE);
+-
+   if (!fdt)
+-    goto failure;
++    {
++      err = grub_error(GRUB_ERR_BAD_OS, "failed to load FDT");
++      goto failure;
++    }
+ 
+   node = grub_fdt_find_subnode (fdt, 0, "chosen");
+   if (node < 0)
+     node = grub_fdt_add_subnode (fdt, 0, "chosen");
+ 
+   if (node < 1)
+-    goto failure;
++    {
++      err = grub_error(grub_errno, "failed to load chosen fdt node.");
++      goto failure;
++    }
+ 
+   /* Set initrd info */
+   if (initrd_start && initrd_end > initrd_start)
+@@ -95,15 +100,26 @@ finalize_params_linux (void)
+       retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-start",
+ 				    initrd_start);
+       if (retval)
+-	goto failure;
++	{
++	  err = grub_error(retval, "Failed to set linux,initrd-start property");
++	  goto failure;
++	}
++
+       retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-end",
+ 				    initrd_end);
+       if (retval)
+-	goto failure;
++	{
++	  err = grub_error(retval, "Failed to set linux,initrd-end property");
++	  goto failure;
++	}
+     }
+ 
+-  if (grub_fdt_install() != GRUB_ERR_NONE)
+-    goto failure;
++  retval = grub_fdt_install();
++  if (retval != GRUB_ERR_NONE)
++    {
++      err = grub_error(retval, "Failed to install fdt");
++      goto failure;
++    }
+ 
+   grub_dprintf ("linux", "Installed/updated FDT configuration table @ %p\n",
+                 fdt);
+@@ -111,14 +127,20 @@ finalize_params_linux (void)
+   /* Convert command line to UCS-2 */
+   loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle);
+   if (!loaded_image)
+-    goto failure;
++    {
++      err = grub_error(grub_errno, "Failed to install fdt");
++      goto failure;
++    }
+ 
+   loaded_image->load_options_size = len =
+     (grub_strlen (linux_args) + 1) * sizeof (grub_efi_char16_t);
+   loaded_image->load_options =
+     grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
+   if (!loaded_image->load_options)
+-    return grub_error(GRUB_ERR_BAD_OS, "failed to create kernel parameters");
++    {
++      err = grub_error(GRUB_ERR_BAD_OS, "failed to create kernel parameters");
++      goto failure;
++    }
+ 
+   loaded_image->load_options_size =
+     2 * grub_utf8_to_utf16 (loaded_image->load_options, len,
+@@ -128,7 +150,7 @@ finalize_params_linux (void)
+ 
+ failure:
+   grub_fdt_unload();
+-  return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT");
++  return err;
+ }
+ 
+ static void
+@@ -212,16 +234,28 @@ grub_linux_unload (void)
+ static void *
+ allocate_initrd_mem (int initrd_pages)
+ {
+-  grub_addr_t max_addr;
++  grub_addr_t max_addr = 0;
++  grub_err_t err;
++  void *ret;
+ 
+-  if (grub_efi_get_ram_base (&max_addr) != GRUB_ERR_NONE)
+-    return NULL;
++  err = grub_efi_get_ram_base (&max_addr);
++  if (err != GRUB_ERR_NONE)
++    {
++      grub_error (err, "grub_efi_get_ram_base() failed");
++      return NULL;
++    }
++
++  grub_dprintf ("linux", "max_addr: 0x%016lx, INITRD_MAX_ADDRESS_OFFSET: 0x%016llx\n",
++		max_addr, INITRD_MAX_ADDRESS_OFFSET);
+ 
+   max_addr += INITRD_MAX_ADDRESS_OFFSET - 1;
++  grub_dprintf ("linux", "calling grub_efi_allocate_pages_real (0x%016lx, 0x%08x, EFI_ALLOCATE_MAX_ADDRESS, EFI_LOADER_DATA)", max_addr, initrd_pages);
+ 
+-  return grub_efi_allocate_pages_real (max_addr, initrd_pages,
+-				       GRUB_EFI_ALLOCATE_MAX_ADDRESS,
+-				       GRUB_EFI_LOADER_DATA);
++  ret = grub_efi_allocate_pages_real (max_addr, initrd_pages,
++				      GRUB_EFI_ALLOCATE_MAX_ADDRESS,
++				      GRUB_EFI_LOADER_DATA);
++  grub_dprintf ("linux", "got 0x%016llx\n", (unsigned long long)ret);
++  return ret;
+ }
+ 
+ static grub_err_t
diff --git a/SOURCES/0115-Try-to-pick-better-locations-for-kernel-and-initrd.patch b/SOURCES/0115-Try-to-pick-better-locations-for-kernel-and-initrd.patch
new file mode 100644
index 0000000..ba918ed
--- /dev/null
+++ b/SOURCES/0115-Try-to-pick-better-locations-for-kernel-and-initrd.patch
@@ -0,0 +1,196 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 11 Jul 2019 17:17:02 +0200
+Subject: [PATCH] Try to pick better locations for kernel and initrd
+
+- Don't limit allocations on 64-bit platforms to < 0x[37f]fffffff if
+  we're using the "large" code model ; use __UINTPTR_MAX__.
+- Get the comparison right to check the address we've allocated.
+- Fix the allocation for the command line as well.
+
+*But*, when we did this some systems started failing badly; coudln't
+parse partition tables, etc.  What's going on here is the disk controller
+is silently failing DMAs to addresses above 4GB, so we're trying to parse
+uninitialized (or HW zeroed) ram when looking for the partition table,
+etc.
+
+So to limit this, we make grub_malloc() pick addresses below 4GB on
+x86_64, but the direct EFI page allocation functions can get addresses
+above that.
+
+Additionally, we now try to locate kernel+initrd+cmdline+etc below
+0x7fffffff, and if they're too big to fit any memory window there, then
+we try a higher address.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/kern/efi/mm.c           |  8 ++++----
+ grub-core/loader/i386/efi/linux.c | 24 +++++++++++++++++-------
+ include/grub/arm/efi/memory.h     |  1 +
+ include/grub/arm64/efi/memory.h   |  1 +
+ include/grub/i386/efi/memory.h    |  1 +
+ include/grub/ia64/efi/memory.h    |  1 +
+ include/grub/x86_64/efi/memory.h  |  4 +++-
+ 7 files changed, 28 insertions(+), 12 deletions(-)
+
+diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
+index 85ad4b4494c..e84961d078c 100644
+--- a/grub-core/kern/efi/mm.c
++++ b/grub-core/kern/efi/mm.c
+@@ -122,7 +122,7 @@ grub_efi_allocate_pages_max (grub_efi_physical_address_t max,
+   grub_efi_boot_services_t *b;
+   grub_efi_physical_address_t address = max;
+ 
+-  if (max > 0xffffffff)
++  if (max > GRUB_EFI_MAX_USABLE_ADDRESS)
+     return 0;
+ 
+   b = grub_efi_system_table->boot_services;
+@@ -480,7 +480,7 @@ filter_memory_map (grub_efi_memory_descriptor_t *memory_map,
+     {
+       if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY
+ #if 1
+-	  && desc->physical_start <= GRUB_EFI_MAX_USABLE_ADDRESS
++	  && desc->physical_start <= GRUB_EFI_MAX_ALLOCATION_ADDRESS
+ #endif
+ 	  && desc->physical_start + PAGES_TO_BYTES (desc->num_pages) > 0x100000
+ 	  && desc->num_pages != 0)
+@@ -498,9 +498,9 @@ filter_memory_map (grub_efi_memory_descriptor_t *memory_map,
+ #if 1
+ 	  if (BYTES_TO_PAGES (filtered_desc->physical_start)
+ 	      + filtered_desc->num_pages
+-	      > BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_USABLE_ADDRESS))
++	      > BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_ALLOCATION_ADDRESS))
+ 	    filtered_desc->num_pages
+-	      = (BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_USABLE_ADDRESS)
++	      = (BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_ALLOCATION_ADDRESS)
+ 		 - BYTES_TO_PAGES (filtered_desc->physical_start));
+ #endif
+ 
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index 3017d0f3e52..33e981e76e7 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -27,6 +27,7 @@
+ #include <grub/lib/cmdline.h>
+ #include <grub/efi/efi.h>
+ #include <grub/efi/linux.h>
++#include <grub/cpu/efi/memory.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -106,7 +107,9 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+       size += ALIGN_UP (grub_file_size (files[i]), 4);
+     }
+ 
+-  initrd_mem = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(size));
++  initrd_mem = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS, BYTES_TO_PAGES(size));
++  if (!initrd_mem)
++    initrd_mem = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS, BYTES_TO_PAGES(size));
+   if (!initrd_mem)
+     {
+       grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate initrd"));
+@@ -202,8 +205,11 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+       goto fail;
+     }
+ 
+-  params = grub_efi_allocate_pages_max (0x3fffffff,
++  params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS,
+ 					BYTES_TO_PAGES(sizeof(*params)));
++  if (!params)
++    params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS,
++					  BYTES_TO_PAGES(sizeof(*params)));
+   if (! params)
+     {
+       grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters");
+@@ -273,8 +279,11 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ #endif
+ 
+   grub_dprintf ("linux", "setting up cmdline\n");
+-  linux_cmdline = grub_efi_allocate_pages_max(0x3fffffff,
+-					 BYTES_TO_PAGES(lh->cmdline_size + 1));
++  linux_cmdline = grub_efi_allocate_pages_max(GRUB_EFI_MAX_ALLOCATION_ADDRESS,
++					      BYTES_TO_PAGES(lh->cmdline_size + 1));
++  if (!linux_cmdline)
++    linux_cmdline = grub_efi_allocate_pages_max(GRUB_EFI_MAX_USABLE_ADDRESS,
++						BYTES_TO_PAGES(lh->cmdline_size + 1));
+   if (!linux_cmdline)
+     {
+       grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate cmdline"));
+@@ -301,11 +310,12 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 
+   kernel_mem = grub_efi_allocate_pages_max(lh->pref_address,
+ 					   BYTES_TO_PAGES(lh->init_size));
+-
+   if (!kernel_mem)
+-    kernel_mem = grub_efi_allocate_pages_max(0x3fffffff,
++    kernel_mem = grub_efi_allocate_pages_max(GRUB_EFI_MAX_ALLOCATION_ADDRESS,
++					     BYTES_TO_PAGES(lh->init_size));
++  if (!kernel_mem)
++    kernel_mem = grub_efi_allocate_pages_max(GRUB_EFI_MAX_USABLE_ADDRESS,
+ 					     BYTES_TO_PAGES(lh->init_size));
+-
+   if (!kernel_mem)
+     {
+       grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate kernel"));
+diff --git a/include/grub/arm/efi/memory.h b/include/grub/arm/efi/memory.h
+index 2c64918e3f7..a4c2ec83502 100644
+--- a/include/grub/arm/efi/memory.h
++++ b/include/grub/arm/efi/memory.h
+@@ -2,5 +2,6 @@
+ #include <grub/efi/memory.h>
+ 
+ #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffff
++#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS
+ 
+ #endif /* ! GRUB_MEMORY_CPU_HEADER */
+diff --git a/include/grub/arm64/efi/memory.h b/include/grub/arm64/efi/memory.h
+index c6cb3241714..acb61dca44b 100644
+--- a/include/grub/arm64/efi/memory.h
++++ b/include/grub/arm64/efi/memory.h
+@@ -2,5 +2,6 @@
+ #include <grub/efi/memory.h>
+ 
+ #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffffffffULL
++#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS
+ 
+ #endif /* ! GRUB_MEMORY_CPU_HEADER */
+diff --git a/include/grub/i386/efi/memory.h b/include/grub/i386/efi/memory.h
+index 2c64918e3f7..a4c2ec83502 100644
+--- a/include/grub/i386/efi/memory.h
++++ b/include/grub/i386/efi/memory.h
+@@ -2,5 +2,6 @@
+ #include <grub/efi/memory.h>
+ 
+ #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffff
++#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS
+ 
+ #endif /* ! GRUB_MEMORY_CPU_HEADER */
+diff --git a/include/grub/ia64/efi/memory.h b/include/grub/ia64/efi/memory.h
+index 2c64918e3f7..a4c2ec83502 100644
+--- a/include/grub/ia64/efi/memory.h
++++ b/include/grub/ia64/efi/memory.h
+@@ -2,5 +2,6 @@
+ #include <grub/efi/memory.h>
+ 
+ #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffff
++#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS
+ 
+ #endif /* ! GRUB_MEMORY_CPU_HEADER */
+diff --git a/include/grub/x86_64/efi/memory.h b/include/grub/x86_64/efi/memory.h
+index 46e9145a308..e81cfb32213 100644
+--- a/include/grub/x86_64/efi/memory.h
++++ b/include/grub/x86_64/efi/memory.h
+@@ -2,9 +2,11 @@
+ #include <grub/efi/memory.h>
+ 
+ #if defined (__code_model_large__)
+-#define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffff
++#define GRUB_EFI_MAX_USABLE_ADDRESS __UINTPTR_MAX__
++#define GRUB_EFI_MAX_ALLOCATION_ADDRESS 0x7fffffff
+ #else
+ #define GRUB_EFI_MAX_USABLE_ADDRESS 0x7fffffff
++#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS
+ #endif
+ 
+ #endif /* ! GRUB_MEMORY_CPU_HEADER */
diff --git a/SOURCES/0116-Attempt-to-fix-up-all-the-places-Wsign-compare-error.patch b/SOURCES/0116-Attempt-to-fix-up-all-the-places-Wsign-compare-error.patch
new file mode 100644
index 0000000..c2f2a59
--- /dev/null
+++ b/SOURCES/0116-Attempt-to-fix-up-all-the-places-Wsign-compare-error.patch
@@ -0,0 +1,397 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 11 Jul 2019 18:03:25 +0200
+Subject: [PATCH] Attempt to fix up all the places -Wsign-compare=error finds.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/kern/emu/misc.c                          |   2 +-
+ grub-core/lib/reed_solomon.c                       |   4 +-
+ grub-core/osdep/linux/blocklist.c                  |   2 +-
+ grub-core/osdep/linux/getroot.c                    |   2 +-
+ grub-core/osdep/linux/hostdisk.c                   |   2 +-
+ util/grub-fstest.c                                 |   2 +-
+ util/grub-menulst2cfg.c                            |   2 +-
+ util/grub-mkfont.c                                 |  13 +-
+ util/grub-probe.c                                  |   2 +-
+ util/grub-rpm-sort.c                               |   2 +-
+ util/setup.c                                       |   2 +-
+ bootstrap.conf                                     |   3 +-
+ .../gnulib-patches/fix-sign-compare-errors.patch   | 161 +++++++++++++++++++++
+ 13 files changed, 181 insertions(+), 18 deletions(-)
+ create mode 100644 grub-core/lib/gnulib-patches/fix-sign-compare-errors.patch
+
+diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c
+index eeea092752d..f08a1bb8415 100644
+--- a/grub-core/kern/emu/misc.c
++++ b/grub-core/kern/emu/misc.c
+@@ -189,7 +189,7 @@ grub_util_get_image_size (const char *path)
+   sz = ftello (f);
+   if (sz < 0)
+     grub_util_error (_("cannot open `%s': %s"), path, strerror (errno));
+-  if (sz != (size_t) sz)
++  if (sz > (off_t)(GRUB_SIZE_MAX >> 1))
+     grub_util_error (_("file `%s' is too big"), path);
+   ret = (size_t) sz;
+ 
+diff --git a/grub-core/lib/reed_solomon.c b/grub-core/lib/reed_solomon.c
+index 467305b46ab..79037c093f7 100644
+--- a/grub-core/lib/reed_solomon.c
++++ b/grub-core/lib/reed_solomon.c
+@@ -157,7 +157,7 @@ static void
+ rs_encode (gf_single_t *data, grub_size_t s, grub_size_t rs)
+ {
+   gf_single_t *rs_polynomial;
+-  int i, j;
++  unsigned int i, j;
+   gf_single_t *m;
+   m = xcalloc (s + rs, sizeof (gf_single_t));
+   grub_memcpy (m, data, s * sizeof (gf_single_t));
+@@ -324,7 +324,7 @@ static void
+ encode_block (gf_single_t *ptr, grub_size_t s,
+ 	      gf_single_t *rptr, grub_size_t rs)
+ {
+-  int i, j;
++  unsigned int i, j;
+   for (i = 0; i < SECTOR_SIZE; i++)
+     {
+       grub_size_t ds = (s + SECTOR_SIZE - 1 - i) / SECTOR_SIZE;
+diff --git a/grub-core/osdep/linux/blocklist.c b/grub-core/osdep/linux/blocklist.c
+index c77d6085ccb..42a315031ff 100644
+--- a/grub-core/osdep/linux/blocklist.c
++++ b/grub-core/osdep/linux/blocklist.c
+@@ -109,7 +109,7 @@ grub_install_get_blocklist (grub_device_t root_dev,
+   else
+     {
+       struct fiemap *fie2;
+-      int i;
++      unsigned int i;
+       fie2 = xmalloc (sizeof (*fie2)
+ 		      + fie1.fm_mapped_extents
+ 		      * sizeof (fie1.fm_extents[1]));
+diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c
+index 28790307e00..9f730b35189 100644
+--- a/grub-core/osdep/linux/getroot.c
++++ b/grub-core/osdep/linux/getroot.c
+@@ -236,7 +236,7 @@ grub_find_root_devices_from_btrfs (const char *dir)
+ {
+   int fd;
+   struct btrfs_ioctl_fs_info_args fsi;
+-  int i, j = 0;
++  unsigned int i, j = 0;
+   char **ret;
+ 
+   fd = open (dir, 0);
+diff --git a/grub-core/osdep/linux/hostdisk.c b/grub-core/osdep/linux/hostdisk.c
+index da62f924e35..7bc99ac1c1d 100644
+--- a/grub-core/osdep/linux/hostdisk.c
++++ b/grub-core/osdep/linux/hostdisk.c
+@@ -83,7 +83,7 @@ grub_util_get_fd_size_os (grub_util_fd_t fd, const char *name, unsigned *log_sec
+   if (sector_size & (sector_size - 1) || !sector_size)
+     return -1;
+   for (log_sector_size = 0;
+-       (1 << log_sector_size) < sector_size;
++       (1U << log_sector_size) < sector_size;
+        log_sector_size++);
+ 
+   if (log_secsize)
+diff --git a/util/grub-fstest.c b/util/grub-fstest.c
+index 83865642009..bfcef852d83 100644
+--- a/util/grub-fstest.c
++++ b/util/grub-fstest.c
+@@ -323,7 +323,7 @@ cmd_cmp (char *src, char *dest)
+   read_file (src, cmp_hook, ff);
+ 
+   {
+-    grub_uint64_t pre;
++    long long pre;
+     pre = ftell (ff);
+     fseek (ff, 0, SEEK_END);
+     if (pre != ftell (ff))
+diff --git a/util/grub-menulst2cfg.c b/util/grub-menulst2cfg.c
+index a39f8693947..358d604210b 100644
+--- a/util/grub-menulst2cfg.c
++++ b/util/grub-menulst2cfg.c
+@@ -34,7 +34,7 @@ main (int argc, char **argv)
+   char *buf = NULL;
+   size_t bufsize = 0;
+   char *suffix = xstrdup ("");
+-  int suffixlen = 0;
++  size_t suffixlen = 0;
+   const char *out_fname = 0;
+ 
+   grub_util_host_init (&argc, &argv);
+diff --git a/util/grub-mkfont.c b/util/grub-mkfont.c
+index 0fe45a6103d..3e09240b99f 100644
+--- a/util/grub-mkfont.c
++++ b/util/grub-mkfont.c
+@@ -138,7 +138,8 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face,
+   int width, height;
+   int cuttop, cutbottom, cutleft, cutright;
+   grub_uint8_t *data;
+-  int mask, i, j, bitmap_size;
++  int mask, i, bitmap_size;
++  unsigned int j;
+   FT_GlyphSlot glyph;
+   int flag = FT_LOAD_RENDER | FT_LOAD_MONOCHROME;
+   FT_Error err;
+@@ -183,7 +184,7 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face,
+     cuttop = cutbottom = cutleft = cutright = 0;
+   else
+     {
+-      for (cuttop = 0; cuttop < glyph->bitmap.rows; cuttop++)
++      for (cuttop = 0; cuttop < (long)glyph->bitmap.rows; cuttop++)
+ 	{
+ 	  for (j = 0; j < glyph->bitmap.width; j++)
+ 	    if (glyph->bitmap.buffer[j / 8 + cuttop * glyph->bitmap.pitch]
+@@ -203,10 +204,10 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face,
+ 	    break;
+ 	}
+       cutbottom = glyph->bitmap.rows - 1 - cutbottom;
+-      if (cutbottom + cuttop >= glyph->bitmap.rows)
++      if (cutbottom + cuttop >= (long)glyph->bitmap.rows)
+ 	cutbottom = 0;
+ 
+-      for (cutleft = 0; cutleft < glyph->bitmap.width; cutleft++)
++      for (cutleft = 0; cutleft < (long)glyph->bitmap.width; cutleft++)
+ 	{
+ 	  for (j = 0; j < glyph->bitmap.rows; j++)
+ 	    if (glyph->bitmap.buffer[cutleft / 8 + j * glyph->bitmap.pitch]
+@@ -225,7 +226,7 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face,
+ 	    break;
+ 	}
+       cutright = glyph->bitmap.width - 1 - cutright;
+-      if (cutright + cutleft >= glyph->bitmap.width)
++      if (cutright + cutleft >= (long)glyph->bitmap.width)
+ 	cutright = 0;
+     }
+ 
+@@ -262,7 +263,7 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face,
+ 
+   mask = 0;
+   data = &glyph_info->bitmap[0] - 1;
+-  for (j = cuttop; j < height + cuttop; j++)
++  for (j = cuttop; j < (long)height + cuttop; j++)
+     for (i = cutleft; i < width + cutleft; i++)
+       add_pixel (&data, &mask,
+ 		 glyph->bitmap.buffer[i / 8 + j * glyph->bitmap.pitch] &
+diff --git a/util/grub-probe.c b/util/grub-probe.c
+index c08e46bbb40..c6fac732b40 100644
+--- a/util/grub-probe.c
++++ b/util/grub-probe.c
+@@ -798,7 +798,7 @@ argp_parser (int key, char *arg, struct argp_state *state)
+ 
+     case 't':
+       {
+-	int i;
++	unsigned int i;
+ 
+ 	for (i = PRINT_FS; i < ARRAY_SIZE (targets); i++)
+ 	  if (strcmp (arg, targets[i]) == 0)
+diff --git a/util/grub-rpm-sort.c b/util/grub-rpm-sort.c
+index f33bd1ed568..8345944105f 100644
+--- a/util/grub-rpm-sort.c
++++ b/util/grub-rpm-sort.c
+@@ -232,7 +232,7 @@ main (int argc, char *argv[])
+   struct arguments arguments;
+   char **package_names = NULL;
+   size_t n_package_names = 0;
+-  int i;
++  unsigned int i;
+ 
+   grub_util_host_init (&argc, &argv);
+ 
+diff --git a/util/setup.c b/util/setup.c
+index da5f2c07f50..8b22bb8ccac 100644
+--- a/util/setup.c
++++ b/util/setup.c
+@@ -406,7 +406,7 @@ SETUP (const char *dir,
+     int is_ldm;
+     grub_err_t err;
+     grub_disk_addr_t *sectors;
+-    int i;
++    unsigned int i;
+     grub_fs_t fs;
+     unsigned int nsec, maxsec;
+ 
+diff --git a/bootstrap.conf b/bootstrap.conf
+index 6b043fc354c..186be9c48ce 100644
+--- a/bootstrap.conf
++++ b/bootstrap.conf
+@@ -80,7 +80,8 @@ cp -a INSTALL INSTALL.grub
+ bootstrap_post_import_hook () {
+   set -e
+   for patchname in fix-base64 fix-null-deref fix-null-state-deref fix-regcomp-uninit-token \
+-      fix-regexec-null-deref fix-uninit-structure fix-unused-value fix-width no-abort; do
++      fix-regexec-null-deref fix-uninit-structure fix-unused-value fix-width no-abort \
++      fix-sign-compare-errors; do
+     patch -d grub-core/lib/gnulib -p2 \
+       < "grub-core/lib/gnulib-patches/$patchname.patch"
+   done
+diff --git a/grub-core/lib/gnulib-patches/fix-sign-compare-errors.patch b/grub-core/lib/gnulib-patches/fix-sign-compare-errors.patch
+new file mode 100644
+index 00000000000..479029c0565
+--- /dev/null
++++ b/grub-core/lib/gnulib-patches/fix-sign-compare-errors.patch
+@@ -0,0 +1,161 @@
++diff --git a/lib/regcomp.c b/lib/regcomp.c
++index cc85f35ac58..361079d82d6 100644
++--- a/lib/regcomp.c
+++++ b/lib/regcomp.c
++@@ -322,7 +322,7 @@ re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state,
++ 		*p++ = dfa->nodes[node].opr.c;
++ 	      memset (&state, '\0', sizeof (state));
++ 	      if (__mbrtowc (&wc, (const char *) buf, p - buf,
++-			     &state) == p - buf
+++			     &state) == (size_t)(p - buf)
++ 		  && (__wcrtomb ((char *) buf, __towlower (wc), &state)
++ 		      != (size_t) -1))
++ 		re_set_fastmap (fastmap, false, buf[0]);
++@@ -3778,7 +3778,7 @@ fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax)
++       num = ((token->type != CHARACTER || c < '0' || '9' < c || num == -2)
++ 	     ? -2
++ 	     : num == -1
++-	     ? c - '0'
+++	     ? (Idx)(c - '0')
++ 	     : MIN (RE_DUP_MAX + 1, num * 10 + c - '0'));
++     }
++   return num;
++diff --git a/lib/regex_internal.c b/lib/regex_internal.c
++index 9004ce809eb..193a1e3d332 100644
++--- a/lib/regex_internal.c
+++++ b/lib/regex_internal.c
++@@ -233,7 +233,7 @@ build_wcs_buffer (re_string_t *pstr)
++       /* Apply the translation if we need.  */
++       if (__glibc_unlikely (pstr->trans != NULL))
++ 	{
++-	  int i, ch;
+++	  unsigned int i, ch;
++ 
++ 	  for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
++ 	    {
++@@ -376,7 +376,7 @@ build_wcs_upper_buffer (re_string_t *pstr)
++ 	prev_st = pstr->cur_state;
++ 	if (__glibc_unlikely (pstr->trans != NULL))
++ 	  {
++-	    int i, ch;
+++	    unsigned int i, ch;
++ 
++ 	    for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
++ 	      {
++@@ -754,7 +754,7 @@ re_string_reconstruct (re_string_t *pstr, Idx idx, int eflags)
++ 			  memset (&cur_state, 0, sizeof (cur_state));
++ 			  mbclen = __mbrtowc (&wc2, (const char *) pp, mlen,
++ 					      &cur_state);
++-			  if (raw + offset - p <= mbclen
+++			  if ((size_t)(raw + offset - p) <= mbclen
++ 			      && mbclen < (size_t) -2)
++ 			    {
++ 			      memset (&pstr->cur_state, '\0',
++diff --git a/lib/regex_internal.h b/lib/regex_internal.h
++index 5462419b787..e0f8292395d 100644
++--- a/lib/regex_internal.h
+++++ b/lib/regex_internal.h
++@@ -425,7 +425,7 @@ struct re_string_t
++   unsigned char offsets_needed;
++   unsigned char newline_anchor;
++   unsigned char word_ops_used;
++-  int mb_cur_max;
+++  unsigned int mb_cur_max;
++ };
++ typedef struct re_string_t re_string_t;
++ 
++@@ -702,7 +702,7 @@ struct re_dfa_t
++   unsigned int is_utf8 : 1;
++   unsigned int map_notascii : 1;
++   unsigned int word_ops_used : 1;
++-  int mb_cur_max;
+++  unsigned int mb_cur_max;
++   bitset_t word_char;
++   reg_syntax_t syntax;
++   Idx *subexp_map;
++diff --git a/lib/regexec.c b/lib/regexec.c
++index 0a7a27b772e..b57d4f9141d 100644
++--- a/lib/regexec.c
+++++ b/lib/regexec.c
++@@ -443,7 +443,7 @@ re_search_stub (struct re_pattern_buffer *bufp, const char *string, Idx length,
++     {
++       if (ret_len)
++ 	{
++-	  assert (pmatch[0].rm_so == start);
+++	  assert (pmatch[0].rm_so == (long)start);
++ 	  rval = pmatch[0].rm_eo - start;
++ 	}
++       else
++@@ -877,11 +877,11 @@ re_search_internal (const regex_t *preg, const char *string, Idx length,
++ 	    if (__glibc_unlikely (mctx.input.offsets_needed != 0))
++ 	      {
++ 		pmatch[reg_idx].rm_so =
++-		  (pmatch[reg_idx].rm_so == mctx.input.valid_len
+++		  (pmatch[reg_idx].rm_so == (long)mctx.input.valid_len
++ 		   ? mctx.input.valid_raw_len
++ 		   : mctx.input.offsets[pmatch[reg_idx].rm_so]);
++ 		pmatch[reg_idx].rm_eo =
++-		  (pmatch[reg_idx].rm_eo == mctx.input.valid_len
+++		  (pmatch[reg_idx].rm_eo == (long)mctx.input.valid_len
++ 		   ? mctx.input.valid_raw_len
++ 		   : mctx.input.offsets[pmatch[reg_idx].rm_eo]);
++ 	      }
++@@ -1418,11 +1418,11 @@ set_regs (const regex_t *preg, const re_match_context_t *mctx, size_t nmatch,
++     }
++   memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch);
++ 
++-  for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;)
+++  for (idx = pmatch[0].rm_so; idx <= (long)pmatch[0].rm_eo ;)
++     {
++       update_regs (dfa, pmatch, prev_idx_match, cur_node, idx, nmatch);
++ 
++-      if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node)
+++      if (idx == (long)pmatch[0].rm_eo && cur_node == mctx->last_node)
++ 	{
++ 	  Idx reg_idx;
++ 	  if (fs)
++@@ -1519,7 +1519,7 @@ update_regs (const re_dfa_t *dfa, regmatch_t *pmatch,
++       if (reg_num < nmatch)
++ 	{
++ 	  /* We are at the last node of this sub expression.  */
++-	  if (pmatch[reg_num].rm_so < cur_idx)
+++	  if (pmatch[reg_num].rm_so < (long)cur_idx)
++ 	    {
++ 	      pmatch[reg_num].rm_eo = cur_idx;
++ 	      /* This is a non-empty match or we are not inside an optional
++@@ -2938,7 +2938,7 @@ check_arrival (re_match_context_t *mctx, state_array_t *path, Idx top_node,
++       mctx->state_log[str_idx] = cur_state;
++     }
++ 
++-  for (null_cnt = 0; str_idx < last_str && null_cnt <= mctx->max_mb_elem_len;)
+++  for (null_cnt = 0; str_idx < last_str && null_cnt <= (long)mctx->max_mb_elem_len;)
++     {
++       re_node_set_empty (&next_nodes);
++       if (mctx->state_log[str_idx + 1])
++@@ -3718,7 +3718,7 @@ check_node_accept_bytes (const re_dfa_t *dfa, Idx node_idx,
++ 			 const re_string_t *input, Idx str_idx)
++ {
++   const re_token_t *node = dfa->nodes + node_idx;
++-  int char_len, elem_len;
+++  unsigned int char_len, elem_len;
++   Idx i;
++ 
++   if (__glibc_unlikely (node->type == OP_UTF8_PERIOD))
++@@ -4066,7 +4066,7 @@ extend_buffers (re_match_context_t *mctx, int min_len)
++   /* Double the lengths of the buffers, but allocate at least MIN_LEN.  */
++   ret = re_string_realloc_buffers (pstr,
++ 				   MAX (min_len,
++-					MIN (pstr->len, pstr->bufs_len * 2)));
+++					MIN ((long)pstr->len, pstr->bufs_len * 2)));
++   if (__glibc_unlikely (ret != REG_NOERROR))
++     return ret;
++ 
++@@ -4236,7 +4236,7 @@ match_ctx_add_entry (re_match_context_t *mctx, Idx node, Idx str_idx, Idx from,
++     = (from == to ? -1 : 0);
++ 
++   mctx->bkref_ents[mctx->nbkref_ents++].more = 0;
++-  if (mctx->max_mb_elem_len < to - from)
+++  if (mctx->max_mb_elem_len < (long)(to - from))
++     mctx->max_mb_elem_len = to - from;
++   return REG_NOERROR;
++ }
diff --git a/SOURCES/0117-Don-t-use-Wno-sign-compare-Wno-conversion-Wno-error-.patch b/SOURCES/0117-Don-t-use-Wno-sign-compare-Wno-conversion-Wno-error-.patch
new file mode 100644
index 0000000..de71484
--- /dev/null
+++ b/SOURCES/0117-Don-t-use-Wno-sign-compare-Wno-conversion-Wno-error-.patch
@@ -0,0 +1,59 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 11 Jul 2019 18:20:37 +0200
+Subject: [PATCH] Don't use -Wno-sign-compare -Wno-conversion -Wno-error, do
+ use -Wextra.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ configure.ac         | 14 +++++++++++---
+ conf/Makefile.common |  2 +-
+ 2 files changed, 12 insertions(+), 4 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index cfdac6bed5a..bd28edf3141 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1480,11 +1480,11 @@ fi
+ # Set them to their new values for the tests below.
+ CC="$TARGET_CC"
+ if test x"$platform" = xemu ; then
+-CFLAGS="$TARGET_CFLAGS -Wno-error"
++CFLAGS="$TARGET_CFLAGS"
+ elif test "x$TARGET_APPLE_LINKER" = x1 ; then
+-CFLAGS="$TARGET_CFLAGS -nostdlib -static -Wno-error"
++CFLAGS="$TARGET_CFLAGS -nostdlib -static"
+ else
+-CFLAGS="$TARGET_CFLAGS -nostdlib -Wno-error"
++CFLAGS="$TARGET_CFLAGS -nostdlib"
+ fi
+ CPPFLAGS="$TARGET_CPPFLAGS"
+ 
+@@ -2054,6 +2054,14 @@ if test x"$enable_werror" != xno ; then
+   HOST_CFLAGS="$HOST_CFLAGS -Werror"
+ fi
+ 
++AC_ARG_ENABLE([wextra],
++	      [AS_HELP_STRING([--disable-wextra],
++                             [do not use -Wextra when building GRUB])])
++if test x"$enable_wextra" != xno ; then
++  TARGET_CFLAGS="$TARGET_CFLAGS -Wextra"
++  HOST_CFLAGS="$HOST_CFLAGS -Wextra"
++fi
++
+ TARGET_CPP="$TARGET_CC -E"
+ TARGET_CCAS=$TARGET_CC
+ 
+diff --git a/conf/Makefile.common b/conf/Makefile.common
+index 2ff9b39357c..35e14ff017e 100644
+--- a/conf/Makefile.common
++++ b/conf/Makefile.common
+@@ -66,7 +66,7 @@ grubconfdir = $(sysconfdir)/grub.d
+ platformdir = $(pkglibdir)/$(target_cpu)-$(platform)
+ starfielddir = $(pkgdatadir)/themes/starfield
+ 
+-CFLAGS_GNULIB = -Wno-undef -Wno-sign-compare -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code -Wno-conversion
++CFLAGS_GNULIB = -Wno-undef -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code
+ CPPFLAGS_GNULIB = -I$(top_builddir)/grub-core/lib/gnulib -I$(top_srcdir)/grub-core/lib/gnulib
+ 
+ CFLAGS_POSIX = -fno-builtin
diff --git a/SOURCES/0118-x86-efi-Use-bounce-buffers-for-reading-to-addresses-.patch b/SOURCES/0118-x86-efi-Use-bounce-buffers-for-reading-to-addresses-.patch
new file mode 100644
index 0000000..fdd3096
--- /dev/null
+++ b/SOURCES/0118-x86-efi-Use-bounce-buffers-for-reading-to-addresses-.patch
@@ -0,0 +1,101 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Fri, 12 Jul 2019 09:53:32 +0200
+Subject: [PATCH] x86-efi: Use bounce buffers for reading to addresses > 4GB
+
+Lots of machines apparently can't DMA correctly above 4GB during UEFI,
+so use bounce buffers for the initramfs read.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/loader/i386/efi/linux.c | 52 +++++++++++++++++++++++++++++++++------
+ 1 file changed, 45 insertions(+), 7 deletions(-)
+
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index 33e981e76e7..2f0336809e7 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -35,11 +35,16 @@ static grub_dl_t my_mod;
+ static int loaded;
+ static void *kernel_mem;
+ static grub_uint64_t kernel_size;
+-static grub_uint8_t *initrd_mem;
++static void *initrd_mem;
+ static grub_uint32_t handover_offset;
+ struct linux_kernel_params *params;
+ static char *linux_cmdline;
+ 
++#define MIN(a, b) \
++  ({ typeof (a) _a = (a); \
++     typeof (b) _b = (b); \
++     _a < _b ? _a : _b; })
++
+ #define BYTES_TO_PAGES(bytes)   (((bytes) + 0xfff) >> 12)
+ 
+ static grub_err_t
+@@ -73,6 +78,44 @@ grub_linuxefi_unload (void)
+   return GRUB_ERR_NONE;
+ }
+ 
++#define BOUNCE_BUFFER_MAX 0x10000000ull
++
++static grub_ssize_t
++read(grub_file_t file, grub_uint8_t *bufp, grub_size_t len)
++{
++  grub_ssize_t bufpos = 0;
++  static grub_size_t bbufsz = 0;
++  static char *bbuf = NULL;
++
++  if (bbufsz == 0)
++    bbufsz = MIN(BOUNCE_BUFFER_MAX, len);
++
++  while (!bbuf && bbufsz)
++    {
++      bbuf = grub_malloc(bbufsz);
++      if (!bbuf)
++	bbufsz >>= 1;
++    }
++  if (!bbuf)
++    grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate bounce buffer"));
++
++  while (bufpos < (long long)len)
++    {
++      grub_ssize_t sz;
++
++      sz = grub_file_read (file, bbuf, MIN(bbufsz, len - bufpos));
++      if (sz < 0)
++	return sz;
++      if (sz == 0)
++	break;
++
++      grub_memcpy(bufp + bufpos, bbuf, sz);
++      bufpos += sz;
++    }
++
++  return bufpos;
++}
++
+ static grub_err_t
+ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+                  int argc, char *argv[])
+@@ -126,7 +169,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+   for (i = 0; i < nfiles; i++)
+     {
+       grub_ssize_t cursize = grub_file_size (files[i]);
+-      if (grub_file_read (files[i], ptr, cursize) != cursize)
++      if (read (files[i], ptr, cursize) != cursize)
+         {
+           if (!grub_errno)
+             grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
+@@ -152,11 +195,6 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+   return grub_errno;
+ }
+ 
+-#define MIN(a, b) \
+-  ({ typeof (a) _a = (a);  \
+-     typeof (b) _b = (b); \
+-     _a < _b ? _a : _b; })
+-
+ static grub_err_t
+ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 		int argc, char *argv[])
diff --git a/SOURCES/0119-x86-efi-Re-arrange-grub_cmd_linux-a-little-bit.patch b/SOURCES/0119-x86-efi-Re-arrange-grub_cmd_linux-a-little-bit.patch
new file mode 100644
index 0000000..f8284ec
--- /dev/null
+++ b/SOURCES/0119-x86-efi-Re-arrange-grub_cmd_linux-a-little-bit.patch
@@ -0,0 +1,133 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 13 Sep 2018 14:42:34 -0400
+Subject: [PATCH] x86-efi: Re-arrange grub_cmd_linux() a little bit.
+
+This just helps the next patch be easier to read.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/loader/i386/efi/linux.c | 75 +++++++++++++++++++++------------------
+ 1 file changed, 41 insertions(+), 34 deletions(-)
+
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index 2f0336809e7..5f48fa55619 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -243,32 +243,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+       goto fail;
+     }
+ 
+-  params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS,
+-					BYTES_TO_PAGES(sizeof(*params)));
+-  if (!params)
+-    params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS,
+-					  BYTES_TO_PAGES(sizeof(*params)));
+-  if (! params)
+-    {
+-      grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters");
+-      goto fail;
+-    }
++  lh = (struct linux_i386_kernel_header *)kernel;
++  grub_dprintf ("linux", "original lh is at %p\n", kernel);
+ 
+-  grub_dprintf ("linux", "params = %p\n", params);
+-
+-  grub_memset (params, 0, sizeof(*params));
+-
+-  setup_header_end_offset = *((grub_uint8_t *)kernel + 0x201);
+-  grub_dprintf ("linux", "copying %lu bytes from %p to %p\n",
+-		MIN((grub_size_t)0x202+setup_header_end_offset,
+-		    sizeof (*params)) - 0x1f1,
+-		(grub_uint8_t *)kernel + 0x1f1,
+-		(grub_uint8_t *)params + 0x1f1);
+-  grub_memcpy ((grub_uint8_t *)params + 0x1f1,
+-	       (grub_uint8_t *)kernel + 0x1f1,
+-		MIN((grub_size_t)0x202+setup_header_end_offset,sizeof (*params)) - 0x1f1);
+-  lh = (struct linux_i386_kernel_header *)params;
+-  grub_dprintf ("linux", "lh is at %p\n", lh);
+   grub_dprintf ("linux", "checking lh->boot_flag\n");
+   if (lh->boot_flag != grub_cpu_to_le16 (0xaa55))
+     {
+@@ -316,6 +293,34 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+     }
+ #endif
+ 
++  params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS,
++					BYTES_TO_PAGES(sizeof(*params)));
++  if (!params)
++    params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS,
++					  BYTES_TO_PAGES(sizeof(*params)));
++  if (! params)
++    {
++      grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters");
++      goto fail;
++    }
++
++  grub_dprintf ("linux", "params = %p\n", params);
++
++  grub_memset (params, 0, sizeof(*params));
++
++  setup_header_end_offset = *((grub_uint8_t *)kernel + 0x201);
++  grub_dprintf ("linux", "copying %lu bytes from %p to %p\n",
++		MIN((grub_size_t)0x202+setup_header_end_offset,
++		    sizeof (*params)) - 0x1f1,
++		(grub_uint8_t *)kernel + 0x1f1,
++		(grub_uint8_t *)params + 0x1f1);
++  grub_memcpy ((grub_uint8_t *)params + 0x1f1,
++	       (grub_uint8_t *)kernel + 0x1f1,
++		MIN((grub_size_t)0x202+setup_header_end_offset,sizeof (*params)) - 0x1f1);
++
++  lh = (struct linux_i386_kernel_header *)params;
++  grub_dprintf ("linux", "new lh is at %p\n", lh);
++
+   grub_dprintf ("linux", "setting up cmdline\n");
+   linux_cmdline = grub_efi_allocate_pages_max(GRUB_EFI_MAX_ALLOCATION_ADDRESS,
+ 					      BYTES_TO_PAGES(lh->cmdline_size + 1));
+@@ -341,8 +346,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   grub_dprintf ("linux", "setting lh->cmd_line_ptr\n");
+   lh->cmd_line_ptr = (grub_uint32_t)(grub_addr_t)linux_cmdline;
+ 
+-  grub_dprintf ("linux", "computing handover offset\n");
+   handover_offset = lh->handover_offset;
++  grub_dprintf("linux", "handover_offset: %08x\n", handover_offset);
+ 
+   start = (lh->setup_sects + 1) * 512;
+ 
+@@ -359,26 +364,28 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+       grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate kernel"));
+       goto fail;
+     }
+-
+-  grub_dprintf ("linux", "kernel_mem = %lx\n", (unsigned long) kernel_mem);
++  grub_dprintf("linux", "kernel_mem = %p\n", kernel_mem);
+ 
+   grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0);
+-  loaded=1;
++
++  loaded = 1;
++
+   grub_dprintf ("linux", "setting lh->code32_start to %p\n", kernel_mem);
+   lh->code32_start = (grub_uint32_t)(grub_addr_t) kernel_mem;
+ 
+   grub_memcpy (kernel_mem, (char *)kernel + start, filelen - start);
+ 
+-  grub_dprintf ("linux", "setting lh->type_of_loader\n");
+   lh->type_of_loader = 0x6;
++  grub_dprintf ("linux", "setting lh->type_of_loader = 0x%02x\n",
++		lh->type_of_loader);
+ 
+-  grub_dprintf ("linux", "setting lh->ext_loader_{type,ver}\n");
+   params->ext_loader_type = 0;
+   params->ext_loader_ver = 2;
+-  grub_dprintf("linux", "kernel_mem: %p handover_offset: %08x\n",
+-	       kernel_mem, handover_offset);
++  grub_dprintf ("linux",
++		"setting lh->ext_loader_{type,ver} = {0x%02x,0x%02x}\n",
++		params->ext_loader_type, params->ext_loader_ver);
+ 
+- fail:
++fail:
+   if (file)
+     grub_file_close (file);
+ 
diff --git a/SOURCES/0120-x86-efi-Make-our-own-allocator-for-kernel-stuff.patch b/SOURCES/0120-x86-efi-Make-our-own-allocator-for-kernel-stuff.patch
new file mode 100644
index 0000000..4ad0696
--- /dev/null
+++ b/SOURCES/0120-x86-efi-Make-our-own-allocator-for-kernel-stuff.patch
@@ -0,0 +1,258 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Wed, 12 Sep 2018 16:03:55 -0400
+Subject: [PATCH] x86-efi: Make our own allocator for kernel stuff
+
+This helps enable allocations above 4GB.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/loader/i386/efi/linux.c | 167 +++++++++++++++++++++-----------------
+ 1 file changed, 94 insertions(+), 73 deletions(-)
+
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index 5f48fa55619..3e4f7ef39f4 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -47,6 +47,65 @@ static char *linux_cmdline;
+ 
+ #define BYTES_TO_PAGES(bytes)   (((bytes) + 0xfff) >> 12)
+ 
++struct allocation_choice {
++    grub_efi_physical_address_t addr;
++    grub_efi_allocate_type_t alloc_type;
++};
++
++static struct allocation_choice max_addresses[] =
++  {
++    { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS },
++    { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS },
++    { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS },
++    { 0, 0 }
++  };
++
++static inline void
++kernel_free(void *addr, grub_efi_uintn_t size)
++{
++  if (addr && size)
++    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)addr,
++			 BYTES_TO_PAGES(size));
++}
++
++static void *
++kernel_alloc(grub_efi_uintn_t size, const char * const errmsg)
++{
++  void *addr = 0;
++  unsigned int i;
++  grub_efi_physical_address_t prev_max = 0;
++
++  for (i = 0; max_addresses[i].addr != 0 && addr == 0; i++)
++    {
++      grub_uint64_t max = max_addresses[i].addr;
++      grub_efi_uintn_t pages;
++
++      if (max == prev_max)
++	continue;
++
++      pages = BYTES_TO_PAGES(size);
++      grub_dprintf ("linux", "Trying to allocate %lu pages from %p\n",
++		    pages, (void *)max);
++
++      prev_max = max;
++      addr = grub_efi_allocate_pages_real (max, pages,
++					   max_addresses[i].alloc_type,
++					   GRUB_EFI_LOADER_DATA);
++      if (addr)
++	grub_dprintf ("linux", "Allocated at %p\n", addr);
++    }
++
++  while (grub_error_pop ())
++    {
++      ;
++    }
++
++  if (addr == NULL)
++    grub_error (GRUB_ERR_OUT_OF_MEMORY, "%s", errmsg);
++
++  return addr;
++}
++
+ static grub_err_t
+ grub_linuxefi_boot (void)
+ {
+@@ -62,19 +121,12 @@ grub_linuxefi_unload (void)
+ {
+   grub_dl_unref (my_mod);
+   loaded = 0;
+-  if (initrd_mem)
+-    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)initrd_mem,
+-			 BYTES_TO_PAGES(params->ramdisk_size));
+-  if (linux_cmdline)
+-    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)
+-			 linux_cmdline,
+-			 BYTES_TO_PAGES(params->cmdline_size + 1));
+-  if (kernel_mem)
+-    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)kernel_mem,
+-			 BYTES_TO_PAGES(kernel_size));
+-  if (params)
+-    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)params,
+-			 BYTES_TO_PAGES(16384));
++
++  kernel_free(initrd_mem, params->ramdisk_size);
++  kernel_free(linux_cmdline, params->cmdline_size + 1);
++  kernel_free(kernel_mem, kernel_size);
++  kernel_free(params, sizeof(*params));
++
+   return GRUB_ERR_NONE;
+ }
+ 
+@@ -150,19 +202,13 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+       size += ALIGN_UP (grub_file_size (files[i]), 4);
+     }
+ 
+-  initrd_mem = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS, BYTES_TO_PAGES(size));
+-  if (!initrd_mem)
+-    initrd_mem = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS, BYTES_TO_PAGES(size));
+-  if (!initrd_mem)
+-    {
+-      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate initrd"));
+-      goto fail;
+-    }
+-
+-  grub_dprintf ("linux", "initrd_mem = %lx\n", (unsigned long) initrd_mem);
++  initrd_mem = kernel_alloc(size, N_("can't allocate initrd"));
++  if (initrd_mem == NULL)
++    goto fail;
++  grub_dprintf ("linux", "initrd_mem = %p\n", initrd_mem);
+ 
+   params->ramdisk_size = size;
+-  params->ramdisk_image = (grub_uint32_t)(grub_addr_t) initrd_mem;
++  params->ramdisk_image = initrd_mem;
+ 
+   ptr = initrd_mem;
+ 
+@@ -221,7 +267,6 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   filelen = grub_file_size (file);
+ 
+   kernel = grub_malloc(filelen);
+-
+   if (!kernel)
+     {
+       grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer"));
+@@ -274,7 +319,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+       goto fail;
+     }
+ 
+-#if defined(__x86_64__) || defined(__aarch64__)
++#if defined(__x86_64__)
+   grub_dprintf ("linux", "checking lh->xloadflags\n");
+   if (!(lh->xloadflags & LINUX_XLF_KERNEL_64))
+     {
+@@ -293,17 +338,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+     }
+ #endif
+ 
+-  params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS,
+-					BYTES_TO_PAGES(sizeof(*params)));
++  params = kernel_alloc (sizeof(*params), "cannot allocate kernel parameters");
+   if (!params)
+-    params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS,
+-					  BYTES_TO_PAGES(sizeof(*params)));
+-  if (! params)
+-    {
+-      grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters");
+-      goto fail;
+-    }
+-
++    goto fail;
+   grub_dprintf ("linux", "params = %p\n", params);
+ 
+   grub_memset (params, 0, sizeof(*params));
+@@ -322,19 +359,10 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   grub_dprintf ("linux", "new lh is at %p\n", lh);
+ 
+   grub_dprintf ("linux", "setting up cmdline\n");
+-  linux_cmdline = grub_efi_allocate_pages_max(GRUB_EFI_MAX_ALLOCATION_ADDRESS,
+-					      BYTES_TO_PAGES(lh->cmdline_size + 1));
++  linux_cmdline = kernel_alloc (lh->cmdline_size + 1, N_("can't allocate cmdline"));
+   if (!linux_cmdline)
+-    linux_cmdline = grub_efi_allocate_pages_max(GRUB_EFI_MAX_USABLE_ADDRESS,
+-						BYTES_TO_PAGES(lh->cmdline_size + 1));
+-  if (!linux_cmdline)
+-    {
+-      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate cmdline"));
+-      goto fail;
+-    }
+-
+-  grub_dprintf ("linux", "linux_cmdline = %lx\n",
+-		(unsigned long)linux_cmdline);
++    goto fail;
++  grub_dprintf ("linux", "linux_cmdline = %p\n", linux_cmdline);
+ 
+   grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
+   grub_create_loader_cmdline (argc, argv,
+@@ -343,27 +371,24 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 			      GRUB_VERIFY_KERNEL_CMDLINE);
+ 
+   grub_dprintf ("linux", "cmdline:%s\n", linux_cmdline);
+-  grub_dprintf ("linux", "setting lh->cmd_line_ptr\n");
+-  lh->cmd_line_ptr = (grub_uint32_t)(grub_addr_t)linux_cmdline;
++  grub_dprintf ("linux", "setting lh->cmd_line_ptr to 0x%08x\n",
++		linux_cmdline);
++  lh->cmd_line_ptr = linux_cmdline;
+ 
+   handover_offset = lh->handover_offset;
+-  grub_dprintf("linux", "handover_offset: %08x\n", handover_offset);
++  grub_dprintf("linux", "handover_offset: 0x%08x\n", handover_offset);
+ 
+   start = (lh->setup_sects + 1) * 512;
+ 
+-  kernel_mem = grub_efi_allocate_pages_max(lh->pref_address,
+-					   BYTES_TO_PAGES(lh->init_size));
+-  if (!kernel_mem)
+-    kernel_mem = grub_efi_allocate_pages_max(GRUB_EFI_MAX_ALLOCATION_ADDRESS,
+-					     BYTES_TO_PAGES(lh->init_size));
+-  if (!kernel_mem)
+-    kernel_mem = grub_efi_allocate_pages_max(GRUB_EFI_MAX_USABLE_ADDRESS,
+-					     BYTES_TO_PAGES(lh->init_size));
+-  if (!kernel_mem)
++  grub_dprintf ("linux", "lh->pref_address: %p\n", (void *)(grub_addr_t)lh->pref_address);
++  if (lh->pref_address < (grub_uint64_t)GRUB_EFI_MAX_ALLOCATION_ADDRESS)
+     {
+-      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate kernel"));
+-      goto fail;
++      max_addresses[0].addr = lh->pref_address;
++      max_addresses[0].alloc_type = GRUB_EFI_ALLOCATE_ADDRESS;
+     }
++  kernel_mem = kernel_alloc (lh->init_size, N_("can't allocate kernel"));
++  if (!kernel_mem)
++    goto fail;
+   grub_dprintf("linux", "kernel_mem = %p\n", kernel_mem);
+ 
+   grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0);
+@@ -398,18 +423,14 @@ fail:
+       loaded = 0;
+     }
+ 
+-  if (linux_cmdline && lh && !loaded)
+-    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)
+-			 linux_cmdline,
+-			 BYTES_TO_PAGES(lh->cmdline_size + 1));
++  if (!loaded)
++    {
++      if (lh)
++	kernel_free (linux_cmdline, lh->cmdline_size + 1);
+ 
+-  if (kernel_mem && !loaded)
+-    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)kernel_mem,
+-			 BYTES_TO_PAGES(kernel_size));
+-
+-  if (params && !loaded)
+-    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)params,
+-			 BYTES_TO_PAGES(16384));
++      kernel_free (kernel_mem, kernel_size);
++      kernel_free (params, sizeof(*params));
++    }
+ 
+   return grub_errno;
+ }
diff --git a/SOURCES/0121-x86-efi-Allow-initrd-params-cmdline-allocations-abov.patch b/SOURCES/0121-x86-efi-Allow-initrd-params-cmdline-allocations-abov.patch
new file mode 100644
index 0000000..b1233ce
--- /dev/null
+++ b/SOURCES/0121-x86-efi-Allow-initrd-params-cmdline-allocations-abov.patch
@@ -0,0 +1,171 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Wed, 12 Sep 2018 16:12:27 -0400
+Subject: [PATCH] x86-efi: Allow initrd+params+cmdline allocations above 4GB.
+
+This enables everything except the kernel itself to be above 4GB.
+Putting the kernel up there still doesn't work, because of the way
+params->code32_start is used.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/loader/i386/efi/linux.c | 67 +++++++++++++++++++++++++++++++++++----
+ include/grub/i386/linux.h         |  6 +++-
+ 2 files changed, 65 insertions(+), 8 deletions(-)
+
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index 3e4f7ef39f4..6bc18d5aef5 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -52,13 +52,22 @@ struct allocation_choice {
+     grub_efi_allocate_type_t alloc_type;
+ };
+ 
+-static struct allocation_choice max_addresses[] =
++static struct allocation_choice max_addresses[4] =
+   {
++    /* the kernel overrides this one with pref_address and
++     * GRUB_EFI_ALLOCATE_ADDRESS */
+     { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS },
++    /* this one is always below 4GB, which we still *prefer* even if the flag
++     * is set. */
+     { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS },
++    /* If the flag in params is set, this one gets changed to be above 4GB. */
+     { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS },
+     { 0, 0 }
+   };
++static struct allocation_choice saved_addresses[4];
++
++#define save_addresses() grub_memcpy(saved_addresses, max_addresses, sizeof(max_addresses))
++#define restore_addresses() grub_memcpy(max_addresses, saved_addresses, sizeof(max_addresses))
+ 
+ static inline void
+ kernel_free(void *addr, grub_efi_uintn_t size)
+@@ -80,6 +89,11 @@ kernel_alloc(grub_efi_uintn_t size, const char * const errmsg)
+       grub_uint64_t max = max_addresses[i].addr;
+       grub_efi_uintn_t pages;
+ 
++      /*
++       * When we're *not* loading the kernel, or >4GB allocations aren't
++       * supported, these entries are basically all the same, so don't re-try
++       * the same parameters.
++       */
+       if (max == prev_max)
+ 	continue;
+ 
+@@ -168,6 +182,9 @@ read(grub_file_t file, grub_uint8_t *bufp, grub_size_t len)
+   return bufpos;
+ }
+ 
++#define LOW_U32(val) ((grub_uint32_t)(((grub_addr_t)(val)) & 0xffffffffull))
++#define HIGH_U32(val) ((grub_uint32_t)(((grub_addr_t)(val) >> 32) & 0xffffffffull))
++
+ static grub_err_t
+ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+                  int argc, char *argv[])
+@@ -207,8 +224,12 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+     goto fail;
+   grub_dprintf ("linux", "initrd_mem = %p\n", initrd_mem);
+ 
+-  params->ramdisk_size = size;
+-  params->ramdisk_image = initrd_mem;
++  params->ramdisk_size = LOW_U32(size);
++  params->ramdisk_image = LOW_U32(initrd_mem);
++#if defined(__x86_64__)
++  params->ext_ramdisk_size = HIGH_U32(size);
++  params->ext_ramdisk_image = HIGH_U32(initrd_mem);
++#endif
+ 
+   ptr = initrd_mem;
+ 
+@@ -338,6 +359,18 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+     }
+ #endif
+ 
++#if defined(__x86_64__)
++  if (lh->xloadflags & LINUX_XLF_CAN_BE_LOADED_ABOVE_4G)
++    {
++      grub_dprintf ("linux", "Loading kernel above 4GB is supported; enabling.\n");
++      max_addresses[2].addr = GRUB_EFI_MAX_USABLE_ADDRESS;
++    }
++  else
++    {
++      grub_dprintf ("linux", "Loading kernel above 4GB is not supported\n");
++    }
++#endif
++
+   params = kernel_alloc (sizeof(*params), "cannot allocate kernel parameters");
+   if (!params)
+     goto fail;
+@@ -372,21 +405,40 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 
+   grub_dprintf ("linux", "cmdline:%s\n", linux_cmdline);
+   grub_dprintf ("linux", "setting lh->cmd_line_ptr to 0x%08x\n",
+-		linux_cmdline);
+-  lh->cmd_line_ptr = linux_cmdline;
++		LOW_U32(linux_cmdline));
++  lh->cmd_line_ptr = LOW_U32(linux_cmdline);
++#if defined(__x86_64__)
++  if ((grub_efi_uintn_t)linux_cmdline > 0xffffffffull)
++    {
++      grub_dprintf ("linux", "setting params->ext_cmd_line_ptr to 0x%08x\n",
++		    HIGH_U32(linux_cmdline));
++      params->ext_cmd_line_ptr = HIGH_U32(linux_cmdline);
++    }
++#endif
+ 
+   handover_offset = lh->handover_offset;
+   grub_dprintf("linux", "handover_offset: 0x%08x\n", handover_offset);
+ 
+   start = (lh->setup_sects + 1) * 512;
+ 
++  /*
++   * AFAICS >4GB for kernel *cannot* work because of params->code32_start being
++   * 32-bit and getting called unconditionally in head_64.S from either entry
++   * point.
++   *
++   * so nerf that out here...
++   */
++  save_addresses();
+   grub_dprintf ("linux", "lh->pref_address: %p\n", (void *)(grub_addr_t)lh->pref_address);
+   if (lh->pref_address < (grub_uint64_t)GRUB_EFI_MAX_ALLOCATION_ADDRESS)
+     {
+       max_addresses[0].addr = lh->pref_address;
+       max_addresses[0].alloc_type = GRUB_EFI_ALLOCATE_ADDRESS;
+     }
++  max_addresses[1].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
++  max_addresses[2].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
+   kernel_mem = kernel_alloc (lh->init_size, N_("can't allocate kernel"));
++  restore_addresses();
+   if (!kernel_mem)
+     goto fail;
+   grub_dprintf("linux", "kernel_mem = %p\n", kernel_mem);
+@@ -395,8 +447,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 
+   loaded = 1;
+ 
+-  grub_dprintf ("linux", "setting lh->code32_start to %p\n", kernel_mem);
+-  lh->code32_start = (grub_uint32_t)(grub_addr_t) kernel_mem;
++  grub_dprintf ("linux", "setting lh->code32_start to 0x%08x\n",
++		LOW_U32(kernel_mem));
++  lh->code32_start = LOW_U32(kernel_mem);
+ 
+   grub_memcpy (kernel_mem, (char *)kernel + start, filelen - start);
+ 
+diff --git a/include/grub/i386/linux.h b/include/grub/i386/linux.h
+index 25ef52c04eb..fac22476cc5 100644
+--- a/include/grub/i386/linux.h
++++ b/include/grub/i386/linux.h
+@@ -236,7 +236,11 @@ struct linux_kernel_params
+   grub_uint32_t ofw_cif_handler;	/* b8 */
+   grub_uint32_t ofw_idt;		/* bc */
+ 
+-  grub_uint8_t padding7[0x1b8 - 0xc0];
++  grub_uint32_t ext_ramdisk_image;	/* 0xc0 */
++  grub_uint32_t ext_ramdisk_size;	/* 0xc4 */
++  grub_uint32_t ext_cmd_line_ptr;	/* 0xc8 */
++
++  grub_uint8_t padding7[0x1b8 - 0xcc];
+ 
+   union
+     {
diff --git a/SOURCES/0122-Fix-getroot.c-s-trampolines.patch b/SOURCES/0122-Fix-getroot.c-s-trampolines.patch
new file mode 100644
index 0000000..29ec44c
--- /dev/null
+++ b/SOURCES/0122-Fix-getroot.c-s-trampolines.patch
@@ -0,0 +1,47 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Fri, 28 Sep 2018 15:42:19 -0400
+Subject: [PATCH] Fix getroot.c's trampolines.
+
+This makes the stack executable on most of the grub utilities, which is
+bad, and rpmdiff complains about it.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/osdep/linux/getroot.c | 16 +++++++---------
+ 1 file changed, 7 insertions(+), 9 deletions(-)
+
+diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c
+index 9f730b35189..f0c503f43d3 100644
+--- a/grub-core/osdep/linux/getroot.c
++++ b/grub-core/osdep/linux/getroot.c
+@@ -1264,22 +1264,20 @@ grub_util_get_grub_dev_os (const char *os_dev)
+   return grub_dev;
+ }
+ 
++static void *mp = NULL;
++static void
++btrfs_mount_path_hook(const char *m)
++{
++  mp = strdup (m);
++}
+ 
+ char *
+ grub_util_get_btrfs_subvol (const char *path, char **mount_path)
+ {
+-  char *mp = NULL;
+-
+   if (mount_path)
+     *mount_path = NULL;
+ 
+-  auto void
+-  mount_path_hook (const char *m)
+-  {
+-    mp = strdup (m);
+-  }
+-
+-  grub_find_root_btrfs_mount_path_hook = mount_path_hook;
++  grub_find_root_btrfs_mount_path_hook = btrfs_mount_path_hook;
+   grub_free (grub_find_root_devices_from_mountinfo (path, NULL));
+   grub_find_root_btrfs_mount_path_hook = NULL;
+ 
diff --git a/SOURCES/0123-Do-not-allow-stack-trampolines-anywhere.patch b/SOURCES/0123-Do-not-allow-stack-trampolines-anywhere.patch
new file mode 100644
index 0000000..4ee639d
--- /dev/null
+++ b/SOURCES/0123-Do-not-allow-stack-trampolines-anywhere.patch
@@ -0,0 +1,38 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Fri, 12 Jul 2019 10:06:50 +0200
+Subject: [PATCH] Do not allow stack trampolines, anywhere.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ configure.ac         | 3 +++
+ conf/Makefile.common | 2 +-
+ 2 files changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/configure.ac b/configure.ac
+index bd28edf3141..907477a585c 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -2062,6 +2062,9 @@ if test x"$enable_wextra" != xno ; then
+   HOST_CFLAGS="$HOST_CFLAGS -Wextra"
+ fi
+ 
++TARGET_CFLAGS="$TARGET_CFLAGS -Werror=trampolines -fno-trampolines"
++HOST_CFLAGS="$HOST_CFLAGS -Werror=trampolines -fno-trampolines"
++
+ TARGET_CPP="$TARGET_CC -E"
+ TARGET_CCAS=$TARGET_CC
+ 
+diff --git a/conf/Makefile.common b/conf/Makefile.common
+index 35e14ff017e..0647c53b916 100644
+--- a/conf/Makefile.common
++++ b/conf/Makefile.common
+@@ -66,7 +66,7 @@ grubconfdir = $(sysconfdir)/grub.d
+ platformdir = $(pkglibdir)/$(target_cpu)-$(platform)
+ starfielddir = $(pkgdatadir)/themes/starfield
+ 
+-CFLAGS_GNULIB = -Wno-undef -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code
++CFLAGS_GNULIB = -Wno-undef -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code -Werror=trampolines -fno-trampolines
+ CPPFLAGS_GNULIB = -I$(top_builddir)/grub-core/lib/gnulib -I$(top_srcdir)/grub-core/lib/gnulib
+ 
+ CFLAGS_POSIX = -fno-builtin
diff --git a/SOURCES/0124-Reimplement-boot_counter.patch b/SOURCES/0124-Reimplement-boot_counter.patch
new file mode 100644
index 0000000..164b229
--- /dev/null
+++ b/SOURCES/0124-Reimplement-boot_counter.patch
@@ -0,0 +1,196 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 4 Oct 2018 14:22:09 -0400
+Subject: [PATCH] Reimplement boot_counter
+
+This adds "increment" and "decrement" commands, and uses them to maintain our
+variables in 01_fallback_counter.  It also simplifies the counter logic, so
+that there are no nested tests that conflict with each other.
+
+Apparently, this *really* wasn't tested well enough.
+
+Resolves: rhbz#1614637
+Signed-off-by: Peter Jones <pjones@redhat.com>
+[lorbus: add comments and revert logic changes in 01_fallback_counting]
+Signed-off-by: Christian Glombek <lorbus@fedoraproject.org>
+---
+ Makefile.util.def                   |   6 +++
+ grub-core/Makefile.core.def         |   5 ++
+ grub-core/commands/increment.c      | 105 ++++++++++++++++++++++++++++++++++++
+ util/grub.d/01_fallback_counting.in |  22 ++++++++
+ 4 files changed, 138 insertions(+)
+ create mode 100644 grub-core/commands/increment.c
+ create mode 100644 util/grub.d/01_fallback_counting.in
+
+diff --git a/Makefile.util.def b/Makefile.util.def
+index c6375933faa..2e5e05b25f1 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -458,6 +458,12 @@ script = {
+   installdir = grubconf;
+ };
+ 
++script = {
++  name = '01_fallback_counting';
++  common = util/grub.d/01_fallback_counting.in;
++  installdir = grubconf;
++};
++
+ script = {
+   name = '01_menu_auto_hide';
+   common = util/grub.d/01_menu_auto_hide.in;
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 498ca11762a..1e15345107e 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -398,6 +398,11 @@ kernel = {
+   extra_dist = kern/mips/cache_flush.S;
+ };
+ 
++module = {
++  name = increment;
++  common = commands/increment.c;
++};
++
+ program = {
+   name = grub-emu;
+   mansection = 1;
+diff --git a/grub-core/commands/increment.c b/grub-core/commands/increment.c
+new file mode 100644
+index 00000000000..79cf137656c
+--- /dev/null
++++ b/grub-core/commands/increment.c
+@@ -0,0 +1,105 @@
++/* increment.c - Commands to increment and decrement variables. */
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2006,2007,2008  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/dl.h>
++#include <grub/term.h>
++#include <grub/time.h>
++#include <grub/types.h>
++#include <grub/misc.h>
++#include <grub/extcmd.h>
++#include <grub/i18n.h>
++#include <grub/env.h>
++
++GRUB_MOD_LICENSE ("GPLv3+");
++
++typedef enum {
++    INCREMENT,
++    DECREMENT,
++} operation;
++
++static grub_err_t
++incr_decr(operation op, int argc, char **args)
++{
++  const char *old;
++  char *new;
++  long value;
++
++  if (argc < 1)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_ ("no variable specified"));
++  if (argc > 1)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_ ("too many arguments"));
++
++  old = grub_env_get (*args);
++  if (!old)
++    return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("No such variable \"%s\""),
++		       *args);
++
++  value = grub_strtol (old, NULL, 0);
++  if (grub_errno != GRUB_ERR_NONE)
++    return grub_errno;
++
++  switch (op)
++    {
++    case INCREMENT:
++      value += 1;
++      break;
++    case DECREMENT:
++      value -= 1;
++      break;
++    }
++
++  new = grub_xasprintf ("%ld", value);
++  if (!new)
++    return grub_errno;
++
++  grub_env_set (*args, new);
++  grub_free (new);
++
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_cmd_incr(struct grub_command *cmd UNUSED,
++              int argc, char **args)
++{
++  return incr_decr(INCREMENT, argc, args);
++}
++
++static grub_err_t
++grub_cmd_decr(struct grub_command *cmd UNUSED,
++              int argc, char **args)
++{
++  return incr_decr(DECREMENT, argc, args);
++}
++
++static grub_command_t cmd_incr, cmd_decr;
++
++GRUB_MOD_INIT(increment)
++{
++  cmd_incr = grub_register_command ("increment", grub_cmd_incr, N_("VARIABLE"),
++                                    N_("increment VARIABLE"));
++  cmd_decr = grub_register_command ("decrement", grub_cmd_decr, N_("VARIABLE"),
++                                    N_("decrement VARIABLE"));
++}
++
++GRUB_MOD_FINI(increment)
++{
++  grub_unregister_command (cmd_incr);
++  grub_unregister_command (cmd_decr);
++}
+diff --git a/util/grub.d/01_fallback_counting.in b/util/grub.d/01_fallback_counting.in
+new file mode 100644
+index 00000000000..be0e770ea82
+--- /dev/null
++++ b/util/grub.d/01_fallback_counting.in
+@@ -0,0 +1,22 @@
++#! /bin/sh -e
++
++# Boot Counting
++# The boot_counter env var can be used to count down boot attempts after an
++# OSTree upgrade and choose the rollback deployment when 0 is reached.  Both
++# boot_counter and boot_success need to be (re-)set from userspace.
++cat << EOF
++insmod increment
++# Check if boot_counter exists and boot_success=0 to activate this behaviour.
++if [ -n "\${boot_counter}" -a "\${boot_success}" = "0" ]; then
++  # if countdown has ended, choose to boot rollback deployment (default=1 on
++  # OSTree-based systems)
++  if  [ "\${boot_counter}" = "0" -o "\${boot_counter}" = "-1" ]; then
++    set default=1
++    set boot_counter=-1
++  # otherwise decrement boot_counter
++  else
++    decrement boot_counter
++  fi
++  save_env boot_counter
++fi
++EOF
diff --git a/SOURCES/0125-Fix-menu-entry-selection-based-on-ID-and-title.patch b/SOURCES/0125-Fix-menu-entry-selection-based-on-ID-and-title.patch
new file mode 100644
index 0000000..84c1370
--- /dev/null
+++ b/SOURCES/0125-Fix-menu-entry-selection-based-on-ID-and-title.patch
@@ -0,0 +1,233 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Fri, 19 Oct 2018 10:57:52 -0400
+Subject: [PATCH] Fix menu entry selection based on ID and title
+
+Currently if grub_strtoul(saved_entry_value, NULL, 0) does not return an
+error, we assume the value it has produced is a correct index into our
+menu entry list, and do not try to interpret the value as the "id" or
+"title" .  In cases where "id" or "title" start with a numeral, this
+makes them impossible to use as selection criteria.
+
+This patch splits the search into three phases - matching id, matching
+title, and only once those have been exhausted, trying to interpret the
+ID as a numeral.  In that case, we also require that the entire string
+is numeric, not merely a string with leading numeric characters.
+
+Resolves: rhbz#1640979
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+[javierm: fix menu entry selection based on title]
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/normal/menu.c | 141 ++++++++++++++++++++++++------------------------
+ 1 file changed, 71 insertions(+), 70 deletions(-)
+
+diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c
+index 37d753d8081..ea714d27176 100644
+--- a/grub-core/normal/menu.c
++++ b/grub-core/normal/menu.c
+@@ -164,12 +164,12 @@ grub_menu_set_timeout (int timeout)
+ }
+ 
+ static int
+-menuentry_eq (const char *id, const char *spec)
++menuentry_eq (const char *id, const char *spec, int limit)
+ {
+   const char *ptr1, *ptr2;
+   ptr1 = id;
+   ptr2 = spec;
+-  while (1)
++  while (limit == -1 || ptr1 - id <= limit)
+     {
+       if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0)
+ 	return ptr2 - spec;
+@@ -178,7 +178,11 @@ menuentry_eq (const char *id, const char *spec)
+       if (*ptr2 == '>')
+ 	ptr2++;
+       if (*ptr1 != *ptr2)
+-	return 0;
++	{
++	  if (limit > -1 && ptr1 - id == limit && !*ptr1 && grub_isspace(*ptr2))
++	    return ptr1 -id -1;
++	  return 0;
++	}
+       if (*ptr1 == 0)
+ 	return ptr1 - id;
+       ptr1++;
+@@ -187,6 +191,58 @@ menuentry_eq (const char *id, const char *spec)
+   return 0;
+ }
+ 
++static int
++get_entry_number_helper(grub_menu_t menu,
++			const char * const val, const char ** const tail)
++{
++  /* See if the variable matches the title of a menu entry.  */
++  int entry = -1;
++  grub_menu_entry_t e;
++  int i;
++
++  for (i = 0, e = menu->entry_list; e; i++)
++    {
++      int l = 0;
++      while (val[l] && !grub_isspace(val[l]))
++	l++;
++
++      if (menuentry_eq (e->id, val, l))
++	{
++	  if (tail)
++	    *tail = val + l;
++	  return i;
++	}
++      e = e->next;
++    }
++
++  for (i = 0, e = menu->entry_list; e; i++)
++    {
++
++      if (menuentry_eq (e->title, val, -1))
++	{
++	  if (tail)
++	    *tail = NULL;
++	  return i;
++	}
++      e = e->next;
++    }
++
++  if (tail)
++    *tail = NULL;
++
++  entry = (int) grub_strtoul (val, tail, 0);
++  if (grub_errno == GRUB_ERR_BAD_NUMBER ||
++      (*tail && **tail && !grub_isspace(**tail)))
++    {
++      entry = -1;
++      if (tail)
++	*tail = NULL;
++      grub_errno = GRUB_ERR_NONE;
++    }
++
++  return entry;
++}
++
+ /* Get the first entry number from the value of the environment variable NAME,
+    which is a space-separated list of non-negative integers.  The entry number
+    which is returned is stripped from the value of NAME.  If no entry number
+@@ -196,7 +252,6 @@ get_and_remove_first_entry_number (grub_menu_t menu, const char *name)
+ {
+   const char *val, *tail;
+   int entry;
+-  int sz = 0;
+ 
+   val = grub_env_get (name);
+   if (! val)
+@@ -204,50 +259,24 @@ get_and_remove_first_entry_number (grub_menu_t menu, const char *name)
+ 
+   grub_error_push ();
+ 
+-  entry = (int) grub_strtoul (val, &tail, 0);
++  entry = get_entry_number_helper(menu, val, &tail);
++  if (!(*tail == 0 || grub_isspace(*tail)))
++    entry = -1;
+ 
+-  if (grub_errno == GRUB_ERR_BAD_NUMBER)
++  if (entry >= 0)
+     {
+-      /* See if the variable matches the title of a menu entry.  */
+-      grub_menu_entry_t e = menu->entry_list;
+-      int i;
+-
+-      for (i = 0; e; i++)
+-	{
+-	  sz = menuentry_eq (e->title, val);
+-	  if (sz < 1)
+-	    sz = menuentry_eq (e->id, val);
+-
+-	  if (sz >= 1)
+-	    {
+-	      entry = i;
+-	      break;
+-	    }
+-	  e = e->next;
+-	}
+-
+-      if (sz > 0)
+-	grub_errno = GRUB_ERR_NONE;
+-
+-      if (! e)
+-	entry = -1;
+-    }
+-
+-  if (grub_errno == GRUB_ERR_NONE)
+-    {
+-      if (sz > 0)
+-	tail += sz;
+-
+       /* Skip whitespace to find the next entry.  */
+       while (*tail && grub_isspace (*tail))
+ 	tail++;
+-      grub_env_set (name, tail);
++      if (*tail)
++	grub_env_set (name, tail);
++      else
++	grub_env_unset (name);
+     }
+   else
+     {
+       grub_env_unset (name);
+       grub_errno = GRUB_ERR_NONE;
+-      entry = -1;
+     }
+ 
+   grub_error_pop ();
+@@ -524,6 +553,7 @@ static int
+ get_entry_number (grub_menu_t menu, const char *name)
+ {
+   const char *val;
++  const char *tail;
+   int entry;
+ 
+   val = grub_env_get (name);
+@@ -531,38 +561,9 @@ get_entry_number (grub_menu_t menu, const char *name)
+     return -1;
+ 
+   grub_error_push ();
+-
+-  entry = (int) grub_strtoul (val, 0, 0);
+-
+-  if (grub_errno == GRUB_ERR_BAD_NUMBER)
+-    {
+-      /* See if the variable matches the title of a menu entry.  */
+-      grub_menu_entry_t e = menu->entry_list;
+-      int i;
+-
+-      grub_errno = GRUB_ERR_NONE;
+-
+-      for (i = 0; e; i++)
+-	{
+-	  if (menuentry_eq (e->title, val)
+-	      || menuentry_eq (e->id, val))
+-	    {
+-	      entry = i;
+-	      break;
+-	    }
+-	  e = e->next;
+-	}
+-
+-      if (! e)
+-	entry = -1;
+-    }
+-
+-  if (grub_errno != GRUB_ERR_NONE)
+-    {
+-      grub_errno = GRUB_ERR_NONE;
+-      entry = -1;
+-    }
+-
++  entry = get_entry_number_helper(menu, val, &tail);
++  if (tail && *tail != '\0')
++    entry = -1;
+   grub_error_pop ();
+ 
+   return entry;
diff --git a/SOURCES/0126-Make-the-menu-entry-users-option-argument-to-be-opti.patch b/SOURCES/0126-Make-the-menu-entry-users-option-argument-to-be-opti.patch
new file mode 100644
index 0000000..e0d36db
--- /dev/null
+++ b/SOURCES/0126-Make-the-menu-entry-users-option-argument-to-be-opti.patch
@@ -0,0 +1,46 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Mon, 26 Nov 2018 10:06:42 +0100
+Subject: [PATCH] Make the menu entry users option argument to be optional
+
+The --users option is used to restrict the access to specific menu entries
+only to a set of users. But the option requires an argument to either be a
+constant or a variable that has been set. So for example the following:
+
+  menuentry "May be run by superusers or users in $users" --users $users {
+  	    linux /vmlinuz
+  }
+
+Would fail if $users is not defined and grub would discard the menu entry.
+Instead, allow the --users option to have an optional argument and ignore
+the option if the argument was not set.
+
+Related: rhbz#1652434
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/commands/menuentry.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c
+index b194123eb67..b175a1b43b7 100644
+--- a/grub-core/commands/menuentry.c
++++ b/grub-core/commands/menuentry.c
+@@ -29,7 +29,7 @@ static const struct grub_arg_option options[] =
+   {
+     {"class", 1, GRUB_ARG_OPTION_REPEATABLE,
+      N_("Menu entry type."), N_("STRING"), ARG_TYPE_STRING},
+-    {"users", 2, 0,
++    {"users", 2, GRUB_ARG_OPTION_OPTIONAL,
+      N_("List of users allowed to boot this entry."), N_("USERNAME[,USERNAME]"),
+      ARG_TYPE_STRING},
+     {"hotkey", 3, 0,
+@@ -281,7 +281,7 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
+   if (! ctxt->state[3].set && ! ctxt->script)
+     return grub_error (GRUB_ERR_BAD_ARGUMENT, "no menuentry definition");
+ 
+-  if (ctxt->state[1].set)
++  if (ctxt->state[1].set && ctxt->state[1].arg)
+     users = ctxt->state[1].arg;
+   else if (ctxt->state[5].set)
+     users = NULL;
diff --git a/SOURCES/0127-Add-efi-export-env-and-efi-load-env-commands.patch b/SOURCES/0127-Add-efi-export-env-and-efi-load-env-commands.patch
new file mode 100644
index 0000000..73456bc
--- /dev/null
+++ b/SOURCES/0127-Add-efi-export-env-and-efi-load-env-commands.patch
@@ -0,0 +1,346 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Wed, 16 Jan 2019 13:21:46 -0500
+Subject: [PATCH] Add efi-export-env and efi-load-env commands
+
+This adds "efi-export-env VARIABLE" and "efi-load-env", which manipulate the
+environment block stored in the EFI variable
+GRUB_ENV-91376aff-cba6-42be-949d-06fde81128e8.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/Makefile.core.def  |   6 ++
+ grub-core/commands/efi/env.c | 168 +++++++++++++++++++++++++++++++++++++++++++
+ grub-core/kern/efi/efi.c     |   3 +
+ grub-core/kern/efi/init.c    |   5 --
+ grub-core/lib/envblk.c       |  43 +++++++++++
+ util/grub-set-bootflag.c     |   1 +
+ include/grub/efi/efi.h       |   5 ++
+ include/grub/lib/envblk.h    |   3 +
+ 8 files changed, 229 insertions(+), 5 deletions(-)
+ create mode 100644 grub-core/commands/efi/env.c
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 1e15345107e..81fc274148e 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -820,6 +820,12 @@ module = {
+   enable = efi;
+ };
+ 
++module = {
++  name = efienv;
++  common = commands/efi/env.c;
++  enable = efi;
++};
++
+ module = {
+   name = efifwsetup;
+   efi = commands/efi/efifwsetup.c;
+diff --git a/grub-core/commands/efi/env.c b/grub-core/commands/efi/env.c
+new file mode 100644
+index 00000000000..cbd13e03e81
+--- /dev/null
++++ b/grub-core/commands/efi/env.c
+@@ -0,0 +1,168 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2012  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++#include <grub/dl.h>
++#include <grub/mm.h>
++#include <grub/misc.h>
++#include <grub/types.h>
++#include <grub/mm.h>
++#include <grub/misc.h>
++#include <grub/efi/api.h>
++#include <grub/efi/efi.h>
++#include <grub/env.h>
++#include <grub/lib/envblk.h>
++#include <grub/command.h>
++
++GRUB_MOD_LICENSE ("GPLv3+");
++
++static const grub_efi_guid_t grub_env_guid = GRUB_EFI_GRUB_VARIABLE_GUID;
++
++static grub_err_t
++grub_efi_export_env(grub_command_t cmd __attribute__ ((unused)),
++                    int argc, char *argv[])
++{
++  const char *value;
++  char *old_value;
++  struct grub_envblk envblk_s = { NULL, 0 };
++  grub_envblk_t envblk = &envblk_s;
++  grub_err_t err;
++  int changed = 1;
++  grub_efi_status_t status;
++
++  grub_dprintf ("efienv", "argc:%d\n", argc);
++  for (int i = 0; i < argc; i++)
++    grub_dprintf ("efienv", "argv[%d]: %s\n", i, argv[i]);
++
++  if (argc != 1)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("variable name expected"));
++
++  grub_efi_get_variable ("GRUB_ENV", &grub_env_guid, &envblk_s.size,
++                         (void **) &envblk_s.buf);
++  if (!envblk_s.buf || envblk_s.size < 1)
++    {
++      char *buf = grub_malloc (1025);
++      if (!buf)
++        return grub_errno;
++
++      grub_memcpy (buf, GRUB_ENVBLK_SIGNATURE, sizeof (GRUB_ENVBLK_SIGNATURE) - 1);
++      grub_memset (buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1, '#',
++	      DEFAULT_ENVBLK_SIZE - sizeof (GRUB_ENVBLK_SIGNATURE) + 1);
++      buf[1024] = '\0';
++
++      envblk_s.buf = buf;
++      envblk_s.size = 1024;
++    }
++  else
++    {
++      char *buf = grub_realloc (envblk_s.buf, envblk_s.size + 1);
++      if (!buf)
++	return grub_errno;
++
++      envblk_s.buf = buf;
++      envblk_s.buf[envblk_s.size] = '\0';
++    }
++
++  err = grub_envblk_get(envblk, argv[0], &old_value);
++  if (err != GRUB_ERR_NONE)
++    {
++      grub_dprintf ("efienv", "grub_envblk_get returned %d\n", err);
++      return err;
++    }
++
++  value = grub_env_get(argv[0]);
++  if ((!value && !old_value) ||
++      (value && old_value && !grub_strcmp(old_value, value)))
++    changed = 0;
++
++  if (old_value)
++    grub_free(old_value);
++
++  if (changed == 0)
++    {
++      grub_dprintf ("efienv", "No changes necessary\n");
++      return 0;
++    }
++
++  if (value)
++    {
++      grub_dprintf ("efienv", "setting \"%s\" to \"%s\"\n", argv[0], value);
++      grub_envblk_set(envblk, argv[0], value);
++    }
++  else
++    {
++      grub_dprintf ("efienv", "deleting \"%s\" from envblk\n", argv[0]);
++      grub_envblk_delete(envblk, argv[0]);
++    }
++
++  grub_dprintf ("efienv", "envblk is %lu bytes:\n\"%s\"\n", envblk_s.size, envblk_s.buf);
++
++  grub_dprintf ("efienv", "removing GRUB_ENV\n");
++  status = grub_efi_set_variable ("GRUB_ENV", &grub_env_guid, NULL, 0);
++  if (status != GRUB_EFI_SUCCESS)
++    grub_dprintf ("efienv", "removal returned %ld\n", status);
++
++  grub_dprintf ("efienv", "setting GRUB_ENV\n");
++  status = grub_efi_set_variable ("GRUB_ENV", &grub_env_guid,
++				  envblk_s.buf, envblk_s.size);
++  if (status != GRUB_EFI_SUCCESS)
++    grub_dprintf ("efienv", "setting GRUB_ENV returned %ld\n", status);
++
++  return 0;
++}
++
++static int
++set_var (const char *name, const char *value,
++	 void *whitelist __attribute__((__unused__)))
++{
++  grub_env_set (name, value);
++  return 0;
++}
++
++static grub_err_t
++grub_efi_load_env(grub_command_t cmd __attribute__ ((unused)),
++                    int argc, char *argv[] __attribute__((__unused__)))
++{
++  struct grub_envblk envblk_s = { NULL, 0 };
++  grub_envblk_t envblk = &envblk_s;
++
++  grub_efi_get_variable ("GRUB_ENV", &grub_env_guid, &envblk_s.size,
++                         (void **) &envblk_s.buf);
++  if (!envblk_s.buf || envblk_s.size < 1)
++    return 0;
++
++  if (argc > 0)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unexpected argument"));
++
++  grub_envblk_iterate (envblk, NULL, set_var);
++  grub_free (envblk_s.buf);
++}
++
++static grub_command_t export_cmd, loadenv_cmd;
++
++GRUB_MOD_INIT(lsefi)
++{
++  export_cmd = grub_register_command ("efi-export-env", grub_efi_export_env,
++	    N_("VARIABLE_NAME"), N_("Export environment variable to UEFI."));
++  loadenv_cmd = grub_register_command ("efi-load-env", grub_efi_load_env,
++	    NULL, N_("Load the grub environment from UEFI."));
++}
++
++GRUB_MOD_FINI(lsefi)
++{
++  grub_unregister_command (export_cmd);
++  grub_unregister_command (loadenv_cmd);
++}
+diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
+index 2a446f5031b..14bc10eb564 100644
+--- a/grub-core/kern/efi/efi.c
++++ b/grub-core/kern/efi/efi.c
+@@ -225,6 +225,9 @@ grub_efi_set_variable(const char *var, const grub_efi_guid_t *guid,
+   if (status == GRUB_EFI_SUCCESS)
+     return GRUB_ERR_NONE;
+ 
++  if (status == GRUB_EFI_NOT_FOUND && datasize == 0)
++    return GRUB_ERR_NONE;
++
+   return grub_error (GRUB_ERR_IO, "could not set EFI variable `%s'", var);
+ }
+ 
+diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c
+index 2d12e6188fd..0574d8d6217 100644
+--- a/grub-core/kern/efi/init.c
++++ b/grub-core/kern/efi/init.c
+@@ -85,11 +85,6 @@ stack_protector_init (void)
+ 
+ grub_addr_t grub_modbase;
+ 
+-#define GRUB_EFI_GRUB_VARIABLE_GUID \
+-  { 0x91376aff, 0xcba6, 0x42be, \
+-    { 0x94, 0x9d, 0x06, 0xfd, 0xe8, 0x11, 0x28, 0xe8 } \
+-  }
+-
+ /* Helper for grub_efi_env_init */
+ static int
+ set_var (const char *name, const char *value,
+diff --git a/grub-core/lib/envblk.c b/grub-core/lib/envblk.c
+index 2e4e78b132d..874506da169 100644
+--- a/grub-core/lib/envblk.c
++++ b/grub-core/lib/envblk.c
+@@ -223,6 +223,49 @@ grub_envblk_delete (grub_envblk_t envblk, const char *name)
+     }
+ }
+ 
++struct get_var_state {
++  const char * const name;
++  char * value;
++  int found;
++};
++
++static int
++get_var (const char * const name, const char * const value, void *statep)
++{
++  struct get_var_state *state = (struct get_var_state *)statep;
++
++  if (!grub_strcmp(state->name, name))
++    {
++      state->found = 1;
++      state->value = grub_strdup(value);
++      if (!state->value)
++	grub_errno = grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
++
++      return 1;
++    }
++
++  return 0;
++}
++
++grub_err_t
++grub_envblk_get (grub_envblk_t envblk, const char * const name, char ** const value)
++{
++  struct get_var_state state = {
++      .name = name,
++      .value = NULL,
++      .found = 0,
++  };
++
++  grub_envblk_iterate(envblk, (void *)&state, get_var);
++
++  *value = state.value;
++
++  if (state.found && !state.value)
++    return grub_errno;
++
++  return GRUB_ERR_NONE;
++}
++
+ void
+ grub_envblk_iterate (grub_envblk_t envblk,
+                      void *hook_data,
+diff --git a/util/grub-set-bootflag.c b/util/grub-set-bootflag.c
+index bb198f02351..6a79ee67444 100644
+--- a/util/grub-set-bootflag.c
++++ b/util/grub-set-bootflag.c
+@@ -25,6 +25,7 @@
+ 
+ #include <config-util.h>     /* For *_DIR_NAME defines */
+ #include <grub/types.h>
++#include <grub/err.h>
+ #include <grub/lib/envblk.h> /* For GRUB_ENVBLK_DEFCFG define */
+ #include <errno.h>
+ #include <stdio.h>
+diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
+index 2e0691454b1..8dfc89a33b9 100644
+--- a/include/grub/efi/efi.h
++++ b/include/grub/efi/efi.h
+@@ -24,6 +24,11 @@
+ #include <grub/dl.h>
+ #include <grub/efi/api.h>
+ 
++#define GRUB_EFI_GRUB_VARIABLE_GUID \
++  { 0x91376aff, 0xcba6, 0x42be, \
++    { 0x94, 0x9d, 0x06, 0xfd, 0xe8, 0x11, 0x28, 0xe8 } \
++  }
++
+ /* Variables.  */
+ extern grub_efi_system_table_t *EXPORT_VAR(grub_efi_system_table);
+ extern grub_efi_handle_t EXPORT_VAR(grub_efi_image_handle);
+diff --git a/include/grub/lib/envblk.h b/include/grub/lib/envblk.h
+index c3e65592170..ab969af2461 100644
+--- a/include/grub/lib/envblk.h
++++ b/include/grub/lib/envblk.h
+@@ -22,6 +22,8 @@
+ #define GRUB_ENVBLK_SIGNATURE	"# GRUB Environment Block\n"
+ #define GRUB_ENVBLK_DEFCFG	"grubenv"
+ 
++#define DEFAULT_ENVBLK_SIZE	1024
++
+ #ifndef ASM_FILE
+ 
+ struct grub_envblk
+@@ -33,6 +35,7 @@ typedef struct grub_envblk *grub_envblk_t;
+ 
+ grub_envblk_t grub_envblk_open (char *buf, grub_size_t size);
+ int grub_envblk_set (grub_envblk_t envblk, const char *name, const char *value);
++grub_err_t grub_envblk_get (grub_envblk_t envblk, const char * const name, char ** const value);
+ void grub_envblk_delete (grub_envblk_t envblk, const char *name);
+ void grub_envblk_iterate (grub_envblk_t envblk,
+                           void *hook_data,
diff --git a/SOURCES/0128-Make-it-possible-to-subtract-conditions-from-debug.patch b/SOURCES/0128-Make-it-possible-to-subtract-conditions-from-debug.patch
new file mode 100644
index 0000000..fce51ea
--- /dev/null
+++ b/SOURCES/0128-Make-it-possible-to-subtract-conditions-from-debug.patch
@@ -0,0 +1,45 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 17 Jan 2019 13:10:39 -0500
+Subject: [PATCH] Make it possible to subtract conditions from debug=
+
+This makes it so you can do set debug to "all,-scripting,-lexer" and get the
+obvious outcome.  Any negation present will take preference over that
+conditional, so "all,-scripting,scripting" is the same thing as
+"all,-scripting".
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/kern/misc.c | 14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
+index 9a2fae6398e..578bf51a5fc 100644
+--- a/grub-core/kern/misc.c
++++ b/grub-core/kern/misc.c
+@@ -164,12 +164,24 @@ int
+ grub_debug_enabled (const char * condition)
+ {
+   const char *debug;
++  char *negcond;
++  int negated = 0;
+ 
+   debug = grub_env_get ("debug");
+   if (!debug)
+     return 0;
+ 
+-  if (grub_strword (debug, "all") || grub_strword (debug, condition))
++  negcond = grub_zalloc (grub_strlen (condition) + 2);
++  if (negcond)
++    {
++      grub_strcpy (negcond, "-");
++      grub_strcpy (negcond+1, condition);
++      negated = grub_strword (debug, negcond);
++      grub_free (negcond);
++    }
++
++  if (!negated &&
++      (grub_strword (debug, "all") || grub_strword (debug, condition)))
+     return 1;
+ 
+   return 0;
diff --git a/SOURCES/0129-Export-all-variables-from-the-initial-context-when-c.patch b/SOURCES/0129-Export-all-variables-from-the-initial-context-when-c.patch
new file mode 100644
index 0000000..bfe3165
--- /dev/null
+++ b/SOURCES/0129-Export-all-variables-from-the-initial-context-when-c.patch
@@ -0,0 +1,44 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Tue, 22 Jan 2019 15:40:25 +0100
+Subject: [PATCH] Export all variables from the initial context when creating a
+ submenu
+
+When a submenu is created, only the exported variables are copied to the
+new menu context. But we want the variables to be global, so export lets
+export all variables to the new created submenu.
+
+Also, don't unset the default variable when a new submenu is created.
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/normal/context.c | 2 +-
+ grub-core/normal/menu.c    | 2 --
+ 2 files changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/grub-core/normal/context.c b/grub-core/normal/context.c
+index ee53d4a68e5..87edd254c44 100644
+--- a/grub-core/normal/context.c
++++ b/grub-core/normal/context.c
+@@ -99,7 +99,7 @@ grub_env_new_context (int export_all)
+ grub_err_t
+ grub_env_context_open (void)
+ {
+-  return grub_env_new_context (0);
++  return grub_env_new_context (1);
+ }
+ 
+ int grub_extractor_level = 0;
+diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c
+index ea714d27176..d4832f17699 100644
+--- a/grub-core/normal/menu.c
++++ b/grub-core/normal/menu.c
+@@ -375,8 +375,6 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot)
+ 
+   if (ptr && ptr[0] && ptr[1])
+     grub_env_set ("default", ptr + 1);
+-  else
+-    grub_env_unset ("default");
+ 
+   grub_script_execute_new_scope (entry->sourcecode, entry->argc, entry->args);
+ 
diff --git a/SOURCES/0130-grub.d-Split-out-boot-success-reset-from-menu-auto-h.patch b/SOURCES/0130-grub.d-Split-out-boot-success-reset-from-menu-auto-h.patch
new file mode 100644
index 0000000..9788fd5
--- /dev/null
+++ b/SOURCES/0130-grub.d-Split-out-boot-success-reset-from-menu-auto-h.patch
@@ -0,0 +1,165 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Christian Glombek <lorbus@fedoraproject.org>
+Date: Tue, 2 Apr 2019 16:22:21 +0200
+Subject: [PATCH] grub.d: Split out boot success reset from menu auto hide
+ script
+
+Also rename fallback and menu auto hide script to be executed
+before and after boot success reset script.
+In menu auto hide script, rename last_boot_ok var to menu_hide_ok
+---
+ Makefile.util.def                                  | 14 ++++++++----
+ ...allback_counting.in => 08_fallback_counting.in} | 14 ++++++------
+ util/grub.d/10_reset_boot_success.in               | 25 ++++++++++++++++++++++
+ .../{01_menu_auto_hide.in => 12_menu_auto_hide.in} | 23 +++++---------------
+ 4 files changed, 48 insertions(+), 28 deletions(-)
+ rename util/grub.d/{01_fallback_counting.in => 08_fallback_counting.in} (65%)
+ create mode 100644 util/grub.d/10_reset_boot_success.in
+ rename util/grub.d/{01_menu_auto_hide.in => 12_menu_auto_hide.in} (58%)
+
+diff --git a/Makefile.util.def b/Makefile.util.def
+index 2e5e05b25f1..11ab2d6fad1 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -459,14 +459,14 @@ script = {
+ };
+ 
+ script = {
+-  name = '01_fallback_counting';
+-  common = util/grub.d/01_fallback_counting.in;
++  name = '08_fallback_counting';
++  common = util/grub.d/08_fallback_counting.in;
+   installdir = grubconf;
+ };
+ 
+ script = {
+-  name = '01_menu_auto_hide';
+-  common = util/grub.d/01_menu_auto_hide.in;
++  name = '12_menu_auto_hide';
++  common = util/grub.d/12_menu_auto_hide.in;
+   installdir = grubconf;
+ };
+ 
+@@ -518,6 +518,12 @@ script = {
+   condition = COND_HOST_LINUX;
+ };
+ 
++script = {
++  name = '10_reset_boot_success';
++  common = util/grub.d/10_reset_boot_success.in;
++  installdir = grubconf;
++};
++
+ script = {
+   name = '10_xnu';
+   common = util/grub.d/10_xnu.in;
+diff --git a/util/grub.d/01_fallback_counting.in b/util/grub.d/08_fallback_counting.in
+similarity index 65%
+rename from util/grub.d/01_fallback_counting.in
+rename to util/grub.d/08_fallback_counting.in
+index be0e770ea82..2e2c3ff7d31 100644
+--- a/util/grub.d/01_fallback_counting.in
++++ b/util/grub.d/08_fallback_counting.in
+@@ -1,15 +1,17 @@
+ #! /bin/sh -e
+-
+-# Boot Counting
++# Fallback Countdown
++#
++# This snippet depends on 10_reset_boot_success and needs to be kept in sync.
++#
+ # The boot_counter env var can be used to count down boot attempts after an
+-# OSTree upgrade and choose the rollback deployment when 0 is reached.  Both
+-# boot_counter and boot_success need to be (re-)set from userspace.
++# OSTree upgrade and choose the rollback deployment when 0 is reached.
++# Both boot_counter=X and boot_success=1 need to be set from userspace.
+ cat << EOF
+ insmod increment
+ # Check if boot_counter exists and boot_success=0 to activate this behaviour.
+ if [ -n "\${boot_counter}" -a "\${boot_success}" = "0" ]; then
+-  # if countdown has ended, choose to boot rollback deployment (default=1 on
+-  # OSTree-based systems)
++  # if countdown has ended, choose to boot rollback deployment,
++  # i.e. default=1 on OSTree-based systems.
+   if  [ "\${boot_counter}" = "0" -o "\${boot_counter}" = "-1" ]; then
+     set default=1
+     set boot_counter=-1
+diff --git a/util/grub.d/10_reset_boot_success.in b/util/grub.d/10_reset_boot_success.in
+new file mode 100644
+index 00000000000..6c88d933dde
+--- /dev/null
++++ b/util/grub.d/10_reset_boot_success.in
+@@ -0,0 +1,25 @@
++#! /bin/sh -e
++# Reset Boot Success
++#
++# The 08_fallback_counting and 12_menu_auto_hide snippets rely on this one
++# and need to be kept in sync.
++#
++# The boot_success var needs to be set to 1 from userspace to mark a boot successful.
++cat << EOF
++insmod increment
++# Hiding the menu is ok if last boot was ok or if this is a first boot attempt to boot the entry
++if [ "\${boot_success}" = "1" -o "\${boot_indeterminate}" = "1" ]; then
++  set menu_hide_ok=1
++else
++  set menu_hide_ok=0 
++fi
++# Reset boot_indeterminate after a successful boot, increment otherwise
++if [ "\${boot_success}" = "1" ] ; then
++  set boot_indeterminate=0
++else
++  increment boot_indeterminate
++fi
++# Reset boot_success for current boot 
++set boot_success=0
++save_env boot_success boot_indeterminate
++EOF
+diff --git a/util/grub.d/01_menu_auto_hide.in b/util/grub.d/12_menu_auto_hide.in
+similarity index 58%
+rename from util/grub.d/01_menu_auto_hide.in
+rename to util/grub.d/12_menu_auto_hide.in
+index ad175870a54..6a7c0fa0d43 100644
+--- a/util/grub.d/01_menu_auto_hide.in
++++ b/util/grub.d/12_menu_auto_hide.in
+@@ -1,5 +1,8 @@
+ #! /bin/sh
+-
++# Menu Auto Hide
++#
++# This snippet depends on 10_reset_boot_success and needs to be kept in sync.
++#
+ # Disable / skip generating menu-auto-hide config parts on serial terminals
+ for x in ${GRUB_TERMINAL_INPUT} ${GRUB_TERMINAL_OUTPUT}; do
+   case "$x" in
+@@ -10,29 +13,13 @@ for x in ${GRUB_TERMINAL_INPUT} ${GRUB_TERMINAL_OUTPUT}; do
+ done
+ 
+ cat << EOF
+-if [ "\${boot_success}" = "1" -o "\${boot_indeterminate}" = "1" ]; then
+-  set last_boot_ok=1
+-else
+-  set last_boot_ok=0
+-fi
+-
+-# Reset boot_indeterminate after a successful boot
+-if [ "\${boot_success}" = "1" ] ; then
+-  set boot_indeterminate=0
+-# Avoid boot_indeterminate causing the menu to be hidden more then once
+-elif [ "\${boot_indeterminate}" = "1" ]; then
+-  set boot_indeterminate=2
+-fi
+-set boot_success=0
+-save_env boot_success boot_indeterminate
+-
+ if [ x\$feature_timeout_style = xy ] ; then
+   if [ "\${menu_show_once}" ]; then
+     unset menu_show_once
+     save_env menu_show_once
+     set timeout_style=menu
+     set timeout=60
+-  elif [ "\${menu_auto_hide}" -a "\${last_boot_ok}" = "1" ]; then
++  elif [ "\${menu_auto_hide}" -a "\${menu_hide_ok}" = "1" ]; then
+     set orig_timeout_style=\${timeout_style}
+     set orig_timeout=\${timeout}
+     if [ "\${fastboot}" = "1" ]; then
diff --git a/SOURCES/0131-Fix-systemctl-kexec-exit-status-check.patch b/SOURCES/0131-Fix-systemctl-kexec-exit-status-check.patch
new file mode 100644
index 0000000..74ecedc
--- /dev/null
+++ b/SOURCES/0131-Fix-systemctl-kexec-exit-status-check.patch
@@ -0,0 +1,37 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Tue, 9 Apr 2019 12:30:38 +0200
+Subject: [PATCH] Fix systemctl kexec exit status check
+
+There's always an error printed even when the systemctl kexec command does
+succeed. That's because systemctl executes it asynchronously, but the emu
+loader seems to expect it to be synchronous and that should never return.
+
+Also, it's wrong to test if kexecute == 1 since we already know that's the
+case or otherwise the function wouldn't had called grub_fatal() earlier.
+
+Finally, systemctl kexec failing shouldn't be a fatal error since the emu
+loader fallbacks to executing the kexec command in case of a failure.
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/loader/emu/linux.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/loader/emu/linux.c b/grub-core/loader/emu/linux.c
+index fda9e00d24c..5b85b225eed 100644
+--- a/grub-core/loader/emu/linux.c
++++ b/grub-core/loader/emu/linux.c
+@@ -71,8 +71,10 @@ grub_linux_boot (void)
+ 		(kexecute==1) ? "do-or-die" : "just-in-case");
+   rc = grub_util_exec (systemctl);
+ 
+-  if (kexecute == 1)
+-    grub_fatal (N_("Error trying to perform 'systemctl kexec'"));
++  if (rc == GRUB_ERR_NONE)
++    return rc;
++
++  grub_error (rc, N_("Error trying to perform 'systemctl kexec'"));
+ 
+   /* need to check read-only root before resetting hard!? */
+   grub_printf("Performing 'kexec -e'");
diff --git a/SOURCES/0132-Print-grub-emu-linux-loader-messages-as-debug.patch b/SOURCES/0132-Print-grub-emu-linux-loader-messages-as-debug.patch
new file mode 100644
index 0000000..a49ec44
--- /dev/null
+++ b/SOURCES/0132-Print-grub-emu-linux-loader-messages-as-debug.patch
@@ -0,0 +1,34 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Tue, 9 Apr 2019 12:42:37 +0200
+Subject: [PATCH] Print grub-emu linux loader messages as debug
+
+They just polute the output and should better be debug messages instead.
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/loader/emu/linux.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/loader/emu/linux.c b/grub-core/loader/emu/linux.c
+index 5b85b225eed..22ab6af1727 100644
+--- a/grub-core/loader/emu/linux.c
++++ b/grub-core/loader/emu/linux.c
+@@ -50,7 +50,7 @@ grub_linux_boot (void)
+     initrd_param = grub_xasprintf("%s", "");
+   }
+ 
+-  grub_printf("%serforming 'kexec -l %s %s %s'\n",
++  grub_dprintf ("linux", "%serforming 'kexec -l %s %s %s'\n",
+ 	(kexecute) ? "P" : "Not p",
+ 	kernel_path, initrd_param, boot_cmdline);
+ 
+@@ -67,7 +67,7 @@ grub_linux_boot (void)
+   if (kexecute < 1)
+     grub_fatal (N_("Use '"PACKAGE"-emu --kexec' to force a system restart."));
+ 
+-  grub_printf("Performing 'systemctl kexec' (%s) ",
++  grub_dprintf ("linux", "Performing 'systemctl kexec' (%s) ",
+ 		(kexecute==1) ? "do-or-die" : "just-in-case");
+   rc = grub_util_exec (systemctl);
+ 
diff --git a/SOURCES/0133-Don-t-assume-that-boot-commands-will-only-return-on-.patch b/SOURCES/0133-Don-t-assume-that-boot-commands-will-only-return-on-.patch
new file mode 100644
index 0000000..418a1ef
--- /dev/null
+++ b/SOURCES/0133-Don-t-assume-that-boot-commands-will-only-return-on-.patch
@@ -0,0 +1,97 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Tue, 9 Apr 2019 13:12:40 +0200
+Subject: [PATCH] Don't assume that boot commands will only return on fail
+
+While it's true that for most loaders the boot command never returns, it
+may be the case that it does. For example the GRUB emulator boot command
+calls to systemctl kexec which in turn does an asynchonous call to kexec.
+
+So in this case GRUB will wrongly assume that the boot command fails and
+print a "Failed to boot both default and fallback entries" even when the
+kexec call later succeeds.
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/normal/menu.c | 23 +++++++++++++----------
+ 1 file changed, 13 insertions(+), 10 deletions(-)
+
+diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c
+index d4832f17699..14ceb9bb060 100644
+--- a/grub-core/normal/menu.c
++++ b/grub-core/normal/menu.c
+@@ -285,7 +285,7 @@ get_and_remove_first_entry_number (grub_menu_t menu, const char *name)
+ }
+ 
+ /* Run a menu entry.  */
+-static void
++static grub_err_t
+ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot)
+ {
+   grub_err_t err = GRUB_ERR_NONE;
+@@ -302,7 +302,7 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot)
+     {
+       grub_print_error ();
+       grub_errno = GRUB_ERR_NONE;
+-      return;
++      return grub_errno;
+     }
+ 
+   errs_before = grub_err_printed_errors;
+@@ -315,7 +315,7 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot)
+       grub_env_context_open ();
+       menu = grub_zalloc (sizeof (*menu));
+       if (! menu)
+-	return;
++	return grub_errno;
+       grub_env_set_menu (menu);
+       if (auto_boot)
+ 	grub_env_set ("timeout", "0");
+@@ -385,7 +385,7 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot)
+ 
+   if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
+     /* Implicit execution of boot, only if something is loaded.  */
+-    grub_command_execute ("boot", 0, 0);
++    err = grub_command_execute ("boot", 0, 0);
+ 
+   if (errs_before != grub_err_printed_errors)
+     grub_wait_after_message ();
+@@ -408,6 +408,8 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot)
+   else
+     grub_env_unset ("default");
+   grub_env_unset ("timeout");
++
++  return err;
+ }
+ 
+ /* Execute ENTRY from the menu MENU, falling back to entries specified
+@@ -422,10 +424,13 @@ grub_menu_execute_with_fallback (grub_menu_t menu,
+ 				 void *callback_data)
+ {
+   int fallback_entry;
++  grub_err_t err;
+ 
+   callback->notify_booting (entry, callback_data);
+ 
+-  grub_menu_execute_entry (entry, 1);
++  err = grub_menu_execute_entry (entry, 1);
++  if (err == GRUB_ERR_NONE)
++    return;
+ 
+   /* Deal with fallback entries.  */
+   while ((fallback_entry = get_and_remove_first_entry_number (menu, "fallback"))
+@@ -436,11 +441,9 @@ grub_menu_execute_with_fallback (grub_menu_t menu,
+ 
+       entry = grub_menu_get_entry (menu, fallback_entry);
+       callback->notify_fallback (entry, callback_data);
+-      grub_menu_execute_entry (entry, 1);
+-      /* If the function call to execute the entry returns at all, then this is
+-	 taken to indicate a boot failure.  For menu entries that do something
+-	 other than actually boot an operating system, this could assume
+-	 incorrectly that something failed.  */
++      err = grub_menu_execute_entry (entry, 1);
++      if (err == GRUB_ERR_NONE)
++        return;
+     }
+ 
+   if (!autobooted)
diff --git a/SOURCES/0134-Fix-undefined-references-for-fdt-when-building-with-.patch b/SOURCES/0134-Fix-undefined-references-for-fdt-when-building-with-.patch
new file mode 100644
index 0000000..9065da8
--- /dev/null
+++ b/SOURCES/0134-Fix-undefined-references-for-fdt-when-building-with-.patch
@@ -0,0 +1,41 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Wed, 1 May 2019 00:36:19 +0200
+Subject: [PATCH] Fix undefined references for fdt when building with platform
+ emu
+
+The fdt module isn't build for this platform, so adding the declarations
+with platform emu will lead to the following undefined reference errors:
+
+BUILDSTDERR: /usr/bin/ld: grub_emu_lite-symlist.o:(.data+0x500): undefined reference to `grub_fdt_add_subnode'
+BUILDSTDERR: /usr/bin/ld: grub_emu_lite-symlist.o:(.data+0x518): undefined reference to `grub_fdt_check_header'
+BUILDSTDERR: /usr/bin/ld: grub_emu_lite-symlist.o:(.data+0x530): undefined reference to `grub_fdt_check_header_nosize'
+BUILDSTDERR: /usr/bin/ld: grub_emu_lite-symlist.o:(.data+0x548): undefined reference to `grub_fdt_create_empty_tree'
+BUILDSTDERR: /usr/bin/ld: grub_emu_lite-symlist.o:(.data+0x560): undefined reference to `grub_fdt_find_subnode'
+BUILDSTDERR: /usr/bin/ld: grub_emu_lite-symlist.o:(.data+0x578): undefined reference to `grub_fdt_first_node'
+BUILDSTDERR: /usr/bin/ld: grub_emu_lite-symlist.o:(.data+0x590): undefined reference to `grub_fdt_get_nodename'
+BUILDSTDERR: /usr/bin/ld: grub_emu_lite-symlist.o:(.data+0x5a8): undefined reference to `grub_fdt_get_prop'
+BUILDSTDERR: /usr/bin/ld: grub_emu_lite-symlist.o:(.data+0x5c0): undefined reference to `grub_fdt_next_node'
+BUILDSTDERR: /usr/bin/ld: grub_emu_lite-symlist.o:(.data+0x5d8): undefined reference to `grub_fdt_set_prop'
+BUILDSTDERR: collect2: error: ld returned 1 exit status
+BUILDSTDERR: make[1]: *** [Makefile:27093: grub-emu-lite] Error 1
+BUILDSTDERR: make[1]: *** Waiting for unfinished jobs....
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ include/grub/fdt.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/grub/fdt.h b/include/grub/fdt.h
+index 22b7c5463fc..2041341fd68 100644
+--- a/include/grub/fdt.h
++++ b/include/grub/fdt.h
+@@ -19,7 +19,7 @@
+ #ifndef GRUB_FDT_HEADER
+ #define GRUB_FDT_HEADER	1
+ 
+-#if defined(__arm__) || defined(__aarch64__)
++#if !defined(GRUB_MACHINE_EMU) && (defined(__arm__) || defined(__aarch64__))
+ 
+ #include <grub/types.h>
+ #include <grub/symbol.h>
diff --git a/SOURCES/0135-Do-better-in-bootstrap.conf.patch b/SOURCES/0135-Do-better-in-bootstrap.conf.patch
new file mode 100644
index 0000000..ec9d8ec
--- /dev/null
+++ b/SOURCES/0135-Do-better-in-bootstrap.conf.patch
@@ -0,0 +1,28 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 29 Jul 2019 10:58:52 -0400
+Subject: [PATCH] Do better in bootstrap.conf
+
+---
+ bootstrap.conf | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/bootstrap.conf b/bootstrap.conf
+index 186be9c48ce..9259526e891 100644
+--- a/bootstrap.conf
++++ b/bootstrap.conf
+@@ -16,7 +16,13 @@
+ # along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ 
+ 
+-GNULIB_REVISION=d271f868a8df9bbec29049d01e056481b7a1a263
++# GNULIB_REVISION=d271f868a8df9bbec29049d01e056481b7a1a263
++if [[ -z "${GNULIB_REVISION}" ]] ;then
++  GNULIB_REVISION=fixes
++fi
++if [[ -z "${GNULIB_URL}" ]] ;then
++  GNULIB_URL=https://github.com/rhboot/gnulib.git
++fi
+ 
+ # gnulib modules used by this package.
+ # mbswidth is used by gnulib-fix-width.diff's changes to argp rather than
diff --git a/SOURCES/0136-Use-git-to-apply-gnulib-patches.patch b/SOURCES/0136-Use-git-to-apply-gnulib-patches.patch
new file mode 100644
index 0000000..7654648
--- /dev/null
+++ b/SOURCES/0136-Use-git-to-apply-gnulib-patches.patch
@@ -0,0 +1,631 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 29 Jul 2019 11:21:27 -0400
+Subject: [PATCH] Use git to apply gnulib patches.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ bootstrap.conf                                     |   6 -
+ conf/Makefile.extra-dist                           |  10 -
+ grub-core/lib/gnulib-patches/fix-base64.patch      |  21 --
+ grub-core/lib/gnulib-patches/fix-null-deref.patch  |  13 --
+ .../lib/gnulib-patches/fix-null-state-deref.patch  |  12 --
+ .../gnulib-patches/fix-regcomp-uninit-token.patch  |  15 --
+ .../gnulib-patches/fix-regexec-null-deref.patch    |  12 --
+ .../gnulib-patches/fix-sign-compare-errors.patch   | 161 ---------------
+ .../lib/gnulib-patches/fix-uninit-structure.patch  |  11 --
+ .../lib/gnulib-patches/fix-unused-value.patch      |  14 --
+ grub-core/lib/gnulib-patches/fix-width.patch       | 217 ---------------------
+ grub-core/lib/gnulib-patches/no-abort.patch        |  26 ---
+ 12 files changed, 518 deletions(-)
+ delete mode 100644 grub-core/lib/gnulib-patches/fix-base64.patch
+ delete mode 100644 grub-core/lib/gnulib-patches/fix-null-deref.patch
+ delete mode 100644 grub-core/lib/gnulib-patches/fix-null-state-deref.patch
+ delete mode 100644 grub-core/lib/gnulib-patches/fix-regcomp-uninit-token.patch
+ delete mode 100644 grub-core/lib/gnulib-patches/fix-regexec-null-deref.patch
+ delete mode 100644 grub-core/lib/gnulib-patches/fix-sign-compare-errors.patch
+ delete mode 100644 grub-core/lib/gnulib-patches/fix-uninit-structure.patch
+ delete mode 100644 grub-core/lib/gnulib-patches/fix-unused-value.patch
+ delete mode 100644 grub-core/lib/gnulib-patches/fix-width.patch
+ delete mode 100644 grub-core/lib/gnulib-patches/no-abort.patch
+
+diff --git a/bootstrap.conf b/bootstrap.conf
+index 9259526e891..452f4d79b0d 100644
+--- a/bootstrap.conf
++++ b/bootstrap.conf
+@@ -85,12 +85,6 @@ cp -a INSTALL INSTALL.grub
+ 
+ bootstrap_post_import_hook () {
+   set -e
+-  for patchname in fix-base64 fix-null-deref fix-null-state-deref fix-regcomp-uninit-token \
+-      fix-regexec-null-deref fix-uninit-structure fix-unused-value fix-width no-abort \
+-      fix-sign-compare-errors; do
+-    patch -d grub-core/lib/gnulib -p2 \
+-      < "grub-core/lib/gnulib-patches/$patchname.patch"
+-  done
+   for patchname in \
+       0001-Support-POTFILES-shell \
+       0002-Handle-gettext_printf-shell-function \
+diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist
+index ea58362b555..8ddf22e6c99 100644
+--- a/conf/Makefile.extra-dist
++++ b/conf/Makefile.extra-dist
+@@ -30,16 +30,6 @@ EXTRA_DIST += grub-core/gensymlist.sh
+ EXTRA_DIST += grub-core/genemuinit.sh
+ EXTRA_DIST += grub-core/genemuinitheader.sh
+ 
+-EXTRA_DIST += grub-core/lib/gnulib-patches/fix-base64.patch
+-EXTRA_DIST += grub-core/lib/gnulib-patches/fix-null-deref.patch
+-EXTRA_DIST += grub-core/lib/gnulib-patches/fix-null-state-deref.patch
+-EXTRA_DIST += grub-core/lib/gnulib-patches/fix-regcomp-uninit-token.patch
+-EXTRA_DIST += grub-core/lib/gnulib-patches/fix-regexec-null-deref.patch
+-EXTRA_DIST += grub-core/lib/gnulib-patches/fix-uninit-structure.patch
+-EXTRA_DIST += grub-core/lib/gnulib-patches/fix-unused-value.patch
+-EXTRA_DIST += grub-core/lib/gnulib-patches/fix-width.patch
+-EXTRA_DIST += grub-core/lib/gnulib-patches/no-abort.patch
+-
+ EXTRA_DIST += grub-core/lib/libgcrypt
+ EXTRA_DIST += grub-core/lib/libgcrypt-grub/mpi/generic
+ EXTRA_DIST += $(shell find $(top_srcdir)/include -name '*.h')
+diff --git a/grub-core/lib/gnulib-patches/fix-base64.patch b/grub-core/lib/gnulib-patches/fix-base64.patch
+deleted file mode 100644
+index 985db127971..00000000000
+--- a/grub-core/lib/gnulib-patches/fix-base64.patch
++++ /dev/null
+@@ -1,21 +0,0 @@
+-diff --git a/lib/base64.h b/lib/base64.h
+-index 9cd0183b8..185a2afa1 100644
+---- a/lib/base64.h
+-+++ b/lib/base64.h
+-@@ -21,8 +21,14 @@
+- /* Get size_t. */
+- # include <stddef.h>
+- 
+--/* Get bool. */
+--# include <stdbool.h>
+-+#ifndef GRUB_POSIX_BOOL_DEFINED
+-+typedef enum { false = 0, true = 1 } bool;
+-+#define GRUB_POSIX_BOOL_DEFINED 1
+-+#endif
+-+
+-+#ifndef _GL_ATTRIBUTE_CONST
+-+# define _GL_ATTRIBUTE_CONST /* empty */
+-+#endif
+- 
+- # ifdef __cplusplus
+- extern "C" {
+diff --git a/grub-core/lib/gnulib-patches/fix-null-deref.patch b/grub-core/lib/gnulib-patches/fix-null-deref.patch
+deleted file mode 100644
+index 8fafa153a47..00000000000
+--- a/grub-core/lib/gnulib-patches/fix-null-deref.patch
++++ /dev/null
+@@ -1,13 +0,0 @@
+-diff --git a/lib/argp-parse.c b/lib/argp-parse.c
+-index 6dec57310..900adad54 100644
+---- a/lib/argp-parse.c
+-+++ b/lib/argp-parse.c
+-@@ -940,7 +940,7 @@ weak_alias (__argp_parse, argp_parse)
+- void *
+- __argp_input (const struct argp *argp, const struct argp_state *state)
+- {
+--  if (state)
+-+  if (state && state->pstate)
+-     {
+-       struct group *group;
+-       struct parser *parser = state->pstate;
+diff --git a/grub-core/lib/gnulib-patches/fix-null-state-deref.patch b/grub-core/lib/gnulib-patches/fix-null-state-deref.patch
+deleted file mode 100644
+index 813ec09c8a1..00000000000
+--- a/grub-core/lib/gnulib-patches/fix-null-state-deref.patch
++++ /dev/null
+@@ -1,12 +0,0 @@
+---- a/lib/argp-help.c	2020-10-28 14:32:19.189215988 +0000
+-+++ b/lib/argp-help.c	2020-10-28 14:38:21.204673940 +0000
+-@@ -145,7 +145,8 @@
+-       if (*(int *)((char *)upptr + up->uparams_offs) >= upptr->rmargin)
+-         {
+-           __argp_failure (state, 0, 0,
+--                          dgettext (state->root_argp->argp_domain,
+-+                          dgettext (state == NULL ? NULL
+-+                                    : state->root_argp->argp_domain,
+-                                     "\
+- ARGP_HELP_FMT: %s value is less than or equal to %s"),
+-                           "rmargin", up->name);
+diff --git a/grub-core/lib/gnulib-patches/fix-regcomp-uninit-token.patch b/grub-core/lib/gnulib-patches/fix-regcomp-uninit-token.patch
+deleted file mode 100644
+index 02e06315dff..00000000000
+--- a/grub-core/lib/gnulib-patches/fix-regcomp-uninit-token.patch
++++ /dev/null
+@@ -1,15 +0,0 @@
+---- a/lib/regcomp.c	2020-11-24 17:06:08.159223858 +0000
+-+++ b/lib/regcomp.c	2020-11-24 17:06:15.630253923 +0000
+-@@ -3808,11 +3808,7 @@
+- create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right,
+- 	     re_token_type_t type)
+- {
+--  re_token_t t;
+--#if defined GCC_LINT || defined lint
+--  memset (&t, 0, sizeof t);
+--#endif
+--  t.type = type;
+-+  re_token_t t = { .type = type };
+-   return create_token_tree (dfa, left, right, &t);
+- }
+- 
+diff --git a/grub-core/lib/gnulib-patches/fix-regexec-null-deref.patch b/grub-core/lib/gnulib-patches/fix-regexec-null-deref.patch
+deleted file mode 100644
+index db6dac9c9e3..00000000000
+--- a/grub-core/lib/gnulib-patches/fix-regexec-null-deref.patch
++++ /dev/null
+@@ -1,12 +0,0 @@
+---- a/lib/regexec.c	2020-10-21 14:25:35.310195912 +0000
+-+++ b/lib/regexec.c	2020-11-05 10:55:09.621542984 +0000
+-@@ -1692,6 +1692,9 @@
+- {
+-   Idx top = mctx->state_log_top;
+-
+-+  if (mctx->state_log == NULL)
+-+    return REG_NOERROR;
+-+
+-   if ((next_state_log_idx >= mctx->input.bufs_len
+-        && mctx->input.bufs_len < mctx->input.len)
+-       || (next_state_log_idx >= mctx->input.valid_len
+diff --git a/grub-core/lib/gnulib-patches/fix-sign-compare-errors.patch b/grub-core/lib/gnulib-patches/fix-sign-compare-errors.patch
+deleted file mode 100644
+index 479029c0565..00000000000
+--- a/grub-core/lib/gnulib-patches/fix-sign-compare-errors.patch
++++ /dev/null
+@@ -1,161 +0,0 @@
+-diff --git a/lib/regcomp.c b/lib/regcomp.c
+-index cc85f35ac58..361079d82d6 100644
+---- a/lib/regcomp.c
+-+++ b/lib/regcomp.c
+-@@ -322,7 +322,7 @@ re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state,
+- 		*p++ = dfa->nodes[node].opr.c;
+- 	      memset (&state, '\0', sizeof (state));
+- 	      if (__mbrtowc (&wc, (const char *) buf, p - buf,
+--			     &state) == p - buf
+-+			     &state) == (size_t)(p - buf)
+- 		  && (__wcrtomb ((char *) buf, __towlower (wc), &state)
+- 		      != (size_t) -1))
+- 		re_set_fastmap (fastmap, false, buf[0]);
+-@@ -3778,7 +3778,7 @@ fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax)
+-       num = ((token->type != CHARACTER || c < '0' || '9' < c || num == -2)
+- 	     ? -2
+- 	     : num == -1
+--	     ? c - '0'
+-+	     ? (Idx)(c - '0')
+- 	     : MIN (RE_DUP_MAX + 1, num * 10 + c - '0'));
+-     }
+-   return num;
+-diff --git a/lib/regex_internal.c b/lib/regex_internal.c
+-index 9004ce809eb..193a1e3d332 100644
+---- a/lib/regex_internal.c
+-+++ b/lib/regex_internal.c
+-@@ -233,7 +233,7 @@ build_wcs_buffer (re_string_t *pstr)
+-       /* Apply the translation if we need.  */
+-       if (__glibc_unlikely (pstr->trans != NULL))
+- 	{
+--	  int i, ch;
+-+	  unsigned int i, ch;
+- 
+- 	  for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
+- 	    {
+-@@ -376,7 +376,7 @@ build_wcs_upper_buffer (re_string_t *pstr)
+- 	prev_st = pstr->cur_state;
+- 	if (__glibc_unlikely (pstr->trans != NULL))
+- 	  {
+--	    int i, ch;
+-+	    unsigned int i, ch;
+- 
+- 	    for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
+- 	      {
+-@@ -754,7 +754,7 @@ re_string_reconstruct (re_string_t *pstr, Idx idx, int eflags)
+- 			  memset (&cur_state, 0, sizeof (cur_state));
+- 			  mbclen = __mbrtowc (&wc2, (const char *) pp, mlen,
+- 					      &cur_state);
+--			  if (raw + offset - p <= mbclen
+-+			  if ((size_t)(raw + offset - p) <= mbclen
+- 			      && mbclen < (size_t) -2)
+- 			    {
+- 			      memset (&pstr->cur_state, '\0',
+-diff --git a/lib/regex_internal.h b/lib/regex_internal.h
+-index 5462419b787..e0f8292395d 100644
+---- a/lib/regex_internal.h
+-+++ b/lib/regex_internal.h
+-@@ -425,7 +425,7 @@ struct re_string_t
+-   unsigned char offsets_needed;
+-   unsigned char newline_anchor;
+-   unsigned char word_ops_used;
+--  int mb_cur_max;
+-+  unsigned int mb_cur_max;
+- };
+- typedef struct re_string_t re_string_t;
+- 
+-@@ -702,7 +702,7 @@ struct re_dfa_t
+-   unsigned int is_utf8 : 1;
+-   unsigned int map_notascii : 1;
+-   unsigned int word_ops_used : 1;
+--  int mb_cur_max;
+-+  unsigned int mb_cur_max;
+-   bitset_t word_char;
+-   reg_syntax_t syntax;
+-   Idx *subexp_map;
+-diff --git a/lib/regexec.c b/lib/regexec.c
+-index 0a7a27b772e..b57d4f9141d 100644
+---- a/lib/regexec.c
+-+++ b/lib/regexec.c
+-@@ -443,7 +443,7 @@ re_search_stub (struct re_pattern_buffer *bufp, const char *string, Idx length,
+-     {
+-       if (ret_len)
+- 	{
+--	  assert (pmatch[0].rm_so == start);
+-+	  assert (pmatch[0].rm_so == (long)start);
+- 	  rval = pmatch[0].rm_eo - start;
+- 	}
+-       else
+-@@ -877,11 +877,11 @@ re_search_internal (const regex_t *preg, const char *string, Idx length,
+- 	    if (__glibc_unlikely (mctx.input.offsets_needed != 0))
+- 	      {
+- 		pmatch[reg_idx].rm_so =
+--		  (pmatch[reg_idx].rm_so == mctx.input.valid_len
+-+		  (pmatch[reg_idx].rm_so == (long)mctx.input.valid_len
+- 		   ? mctx.input.valid_raw_len
+- 		   : mctx.input.offsets[pmatch[reg_idx].rm_so]);
+- 		pmatch[reg_idx].rm_eo =
+--		  (pmatch[reg_idx].rm_eo == mctx.input.valid_len
+-+		  (pmatch[reg_idx].rm_eo == (long)mctx.input.valid_len
+- 		   ? mctx.input.valid_raw_len
+- 		   : mctx.input.offsets[pmatch[reg_idx].rm_eo]);
+- 	      }
+-@@ -1418,11 +1418,11 @@ set_regs (const regex_t *preg, const re_match_context_t *mctx, size_t nmatch,
+-     }
+-   memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch);
+- 
+--  for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;)
+-+  for (idx = pmatch[0].rm_so; idx <= (long)pmatch[0].rm_eo ;)
+-     {
+-       update_regs (dfa, pmatch, prev_idx_match, cur_node, idx, nmatch);
+- 
+--      if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node)
+-+      if (idx == (long)pmatch[0].rm_eo && cur_node == mctx->last_node)
+- 	{
+- 	  Idx reg_idx;
+- 	  if (fs)
+-@@ -1519,7 +1519,7 @@ update_regs (const re_dfa_t *dfa, regmatch_t *pmatch,
+-       if (reg_num < nmatch)
+- 	{
+- 	  /* We are at the last node of this sub expression.  */
+--	  if (pmatch[reg_num].rm_so < cur_idx)
+-+	  if (pmatch[reg_num].rm_so < (long)cur_idx)
+- 	    {
+- 	      pmatch[reg_num].rm_eo = cur_idx;
+- 	      /* This is a non-empty match or we are not inside an optional
+-@@ -2938,7 +2938,7 @@ check_arrival (re_match_context_t *mctx, state_array_t *path, Idx top_node,
+-       mctx->state_log[str_idx] = cur_state;
+-     }
+- 
+--  for (null_cnt = 0; str_idx < last_str && null_cnt <= mctx->max_mb_elem_len;)
+-+  for (null_cnt = 0; str_idx < last_str && null_cnt <= (long)mctx->max_mb_elem_len;)
+-     {
+-       re_node_set_empty (&next_nodes);
+-       if (mctx->state_log[str_idx + 1])
+-@@ -3718,7 +3718,7 @@ check_node_accept_bytes (const re_dfa_t *dfa, Idx node_idx,
+- 			 const re_string_t *input, Idx str_idx)
+- {
+-   const re_token_t *node = dfa->nodes + node_idx;
+--  int char_len, elem_len;
+-+  unsigned int char_len, elem_len;
+-   Idx i;
+- 
+-   if (__glibc_unlikely (node->type == OP_UTF8_PERIOD))
+-@@ -4066,7 +4066,7 @@ extend_buffers (re_match_context_t *mctx, int min_len)
+-   /* Double the lengths of the buffers, but allocate at least MIN_LEN.  */
+-   ret = re_string_realloc_buffers (pstr,
+- 				   MAX (min_len,
+--					MIN (pstr->len, pstr->bufs_len * 2)));
+-+					MIN ((long)pstr->len, pstr->bufs_len * 2)));
+-   if (__glibc_unlikely (ret != REG_NOERROR))
+-     return ret;
+- 
+-@@ -4236,7 +4236,7 @@ match_ctx_add_entry (re_match_context_t *mctx, Idx node, Idx str_idx, Idx from,
+-     = (from == to ? -1 : 0);
+- 
+-   mctx->bkref_ents[mctx->nbkref_ents++].more = 0;
+--  if (mctx->max_mb_elem_len < to - from)
+-+  if (mctx->max_mb_elem_len < (long)(to - from))
+-     mctx->max_mb_elem_len = to - from;
+-   return REG_NOERROR;
+- }
+diff --git a/grub-core/lib/gnulib-patches/fix-uninit-structure.patch b/grub-core/lib/gnulib-patches/fix-uninit-structure.patch
+deleted file mode 100644
+index 7b4d9f67af4..00000000000
+--- a/grub-core/lib/gnulib-patches/fix-uninit-structure.patch
++++ /dev/null
+@@ -1,11 +0,0 @@
+---- a/lib/regcomp.c	2020-10-22 13:49:06.770168928 +0000
+-+++ b/lib/regcomp.c	2020-10-22 13:50:37.026528298 +0000
+-@@ -3662,7 +3662,7 @@
+-   Idx alloc = 0;
+- #endif /* not RE_ENABLE_I18N */
+-   reg_errcode_t ret;
+--  re_token_t br_token;
+-+  re_token_t br_token = {0};
+-   bin_tree_t *tree;
+- 
+-   sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+diff --git a/grub-core/lib/gnulib-patches/fix-unused-value.patch b/grub-core/lib/gnulib-patches/fix-unused-value.patch
+deleted file mode 100644
+index ba51f1bf223..00000000000
+--- a/grub-core/lib/gnulib-patches/fix-unused-value.patch
++++ /dev/null
+@@ -1,14 +0,0 @@
+---- a/lib/regexec.c	2020-10-21 14:25:35.310195912 +0000
+-+++ b/lib/regexec.c	2020-10-21 14:32:07.961765604 +0000
+-@@ -828,7 +828,11 @@
+- 		    break;
+- 		  if (__glibc_unlikely (err != REG_NOMATCH))
+- 		    goto free_return;
+-+#ifdef DEBUG
+-+		  /* Only used for assertion below when DEBUG is set, otherwise
+-+		     it will be over-written when we loop around.  */
+- 		  match_last = -1;
+-+#endif
+- 		}
+- 	      else
+- 		break; /* We found a match.  */
+diff --git a/grub-core/lib/gnulib-patches/fix-width.patch b/grub-core/lib/gnulib-patches/fix-width.patch
+deleted file mode 100644
+index 0a208ad08b5..00000000000
+--- a/grub-core/lib/gnulib-patches/fix-width.patch
++++ /dev/null
+@@ -1,217 +0,0 @@
+-diff --git a/lib/argp-fmtstream.c b/lib/argp-fmtstream.c
+-index ba6a407f7..d0685b3d4 100644
+---- a/lib/argp-fmtstream.c
+-+++ b/lib/argp-fmtstream.c
+-@@ -28,9 +28,11 @@
+- #include <errno.h>
+- #include <stdarg.h>
+- #include <ctype.h>
+-+#include <wchar.h>
+- 
+- #include "argp-fmtstream.h"
+- #include "argp-namefrob.h"
+-+#include "mbswidth.h"
+- 
+- #ifndef ARGP_FMTSTREAM_USE_LINEWRAP
+- 
+-@@ -115,6 +117,51 @@ weak_alias (__argp_fmtstream_free, argp_fmtstream_free)
+- #endif
+- #endif
+- 
+-+
+-+/* Return the pointer to the first character that doesn't fit in l columns.  */
+-+static inline const ptrdiff_t
+-+add_width (const char *ptr, const char *end, size_t l)
+-+{
+-+  mbstate_t ps;
+-+  const char *ptr0 = ptr;
+-+
+-+  memset (&ps, 0, sizeof (ps));
+-+
+-+  while (ptr < end)
+-+    {
+-+      wchar_t wc;
+-+      size_t s, k;
+-+
+-+      s = mbrtowc (&wc, ptr, end - ptr, &ps);
+-+      if (s == (size_t) -1)
+-+	break;
+-+      if (s == (size_t) -2)
+-+	{
+-+	  if (1 >= l)
+-+	    break;
+-+	  l--;
+-+	  ptr++;
+-+	  continue;
+-+	}
+-+
+-+      if (wc == '\e' && ptr + 3 < end
+-+	  && ptr[1] == '[' && (ptr[2] == '0' || ptr[2] == '1')
+-+	  && ptr[3] == 'm')
+-+	{
+-+	  ptr += 4;
+-+	  continue;
+-+	}
+-+
+-+      k = wcwidth (wc);
+-+
+-+      if (k >= l)
+-+	break;
+-+      l -= k;
+-+      ptr += s;
+-+    }
+-+  return ptr - ptr0;
+-+}
+-+
+- /* Process FS's buffer so that line wrapping is done from POINT_OFFS to the
+-    end of its buffer.  This code is mostly from glibc stdio/linewrap.c.  */
+- void
+-@@ -168,13 +215,15 @@ __argp_fmtstream_update (argp_fmtstream_t fs)
+-       if (!nl)
+-         {
+-           /* The buffer ends in a partial line.  */
+-+          size_t display_width = mbsnwidth (buf, fs->p - buf,
+-+                                            MBSW_STOP_AT_NUL);
+- 
+--          if (fs->point_col + len < fs->rmargin)
+-+          if (fs->point_col + display_width < fs->rmargin)
+-             {
+-               /* The remaining buffer text is a partial line and fits
+-                  within the maximum line width.  Advance point for the
+-                  characters to be written and stop scanning.  */
+--              fs->point_col += len;
+-+              fs->point_col += display_width;
+-               break;
+-             }
+-           else
+-@@ -182,14 +231,18 @@ __argp_fmtstream_update (argp_fmtstream_t fs)
+-                the end of the buffer.  */
+-             nl = fs->p;
+-         }
+--      else if (fs->point_col + (nl - buf) < (ssize_t) fs->rmargin)
+--        {
+--          /* The buffer contains a full line that fits within the maximum
+--             line width.  Reset point and scan the next line.  */
+--          fs->point_col = 0;
+--          buf = nl + 1;
+--          continue;
+--        }
+-+      else
+-+	{
+-+	  size_t display_width = mbsnwidth (buf, nl - buf, MBSW_STOP_AT_NUL);
+-+	  if (display_width < (ssize_t) fs->rmargin)
+-+	    {
+-+	      /* The buffer contains a full line that fits within the maximum
+-+		 line width.  Reset point and scan the next line.  */
+-+	      fs->point_col = 0;
+-+	      buf = nl + 1;
+-+	      continue;
+-+	    }
+-+	}
+- 
+-       /* This line is too long.  */
+-       r = fs->rmargin - 1;
+-@@ -225,7 +278,7 @@ __argp_fmtstream_update (argp_fmtstream_t fs)
+-           char *p, *nextline;
+-           int i;
+- 
+--          p = buf + (r + 1 - fs->point_col);
+-+          p = buf + add_width (buf, fs->p, (r + 1 - fs->point_col));
+-           while (p >= buf && !isblank ((unsigned char) *p))
+-             --p;
+-           nextline = p + 1;     /* This will begin the next line.  */
+-@@ -243,7 +296,7 @@ __argp_fmtstream_update (argp_fmtstream_t fs)
+-             {
+-               /* A single word that is greater than the maximum line width.
+-                  Oh well.  Put it on an overlong line by itself.  */
+--              p = buf + (r + 1 - fs->point_col);
+-+              p = buf + add_width (buf, fs->p, (r + 1 - fs->point_col));
+-               /* Find the end of the long word.  */
+-               if (p < nl)
+-                 do
+-@@ -277,7 +330,8 @@ __argp_fmtstream_update (argp_fmtstream_t fs)
+-               && fs->p > nextline)
+-             {
+-               /* The margin needs more blanks than we removed.  */
+--              if (fs->end - fs->p > fs->wmargin + 1)
+-+              if (mbsnwidth (fs->p, fs->end - fs->p, MBSW_STOP_AT_NUL)
+-+                  > fs->wmargin + 1)
+-                 /* Make some space for them.  */
+-                 {
+-                   size_t mv = fs->p - nextline;
+-diff --git a/lib/argp-help.c b/lib/argp-help.c
+-index e5375a0f0..5d8f451ec 100644
+---- a/lib/argp-help.c
+-+++ b/lib/argp-help.c
+-@@ -51,6 +51,7 @@
+- #include "argp.h"
+- #include "argp-fmtstream.h"
+- #include "argp-namefrob.h"
+-+#include "mbswidth.h"
+- 
+- #ifndef SIZE_MAX
+- # define SIZE_MAX ((size_t) -1)
+-@@ -1432,7 +1433,7 @@ argp_args_usage (const struct argp *argp, const struct argp_state *state,
+- 
+-       /* Manually do line wrapping so that it (probably) won't get wrapped at
+-          any embedded spaces.  */
+--      space (stream, 1 + nl - cp);
+-+      space (stream, 1 + mbsnwidth (cp, nl - cp, MBSW_STOP_AT_NUL));
+- 
+-       __argp_fmtstream_write (stream, cp, nl - cp);
+-     }
+-diff --git a/lib/mbswidth.c b/lib/mbswidth.c
+-index 408a15e34..b3fb7f83a 100644
+---- a/lib/mbswidth.c
+-+++ b/lib/mbswidth.c
+-@@ -38,6 +38,14 @@
+- /* Get INT_MAX.  */
+- #include <limits.h>
+- 
+-+#ifndef FALLTHROUGH
+-+# if __GNUC__ < 7
+-+#  define FALLTHROUGH ((void) 0)
+-+# else
+-+#  define FALLTHROUGH __attribute__ ((__fallthrough__))
+-+# endif
+-+#endif
+-+
+- /* Returns the number of columns needed to represent the multibyte
+-    character string pointed to by STRING.  If a non-printable character
+-    occurs, and MBSW_REJECT_UNPRINTABLE is specified, -1 is returned.
+-@@ -90,6 +98,10 @@ mbsnwidth (const char *string, size_t nbytes, int flags)
+-               p++;
+-               width++;
+-               break;
+-+            case '\0':
+-+              if (flags & MBSW_STOP_AT_NUL)
+-+                return width;
+-+              FALLTHROUGH;
+-             default:
+-               /* If we have a multibyte sequence, scan it up to its end.  */
+-               {
+-@@ -168,6 +180,9 @@ mbsnwidth (const char *string, size_t nbytes, int flags)
+-     {
+-       unsigned char c = (unsigned char) *p++;
+- 
+-+      if (c == 0 && (flags & MBSW_STOP_AT_NUL))
+-+        return width;
+-+
+-       if (isprint (c))
+-         {
+-           if (width == INT_MAX)
+-diff --git a/lib/mbswidth.h b/lib/mbswidth.h
+-index 2b5c53c37..45a123e63 100644
+---- a/lib/mbswidth.h
+-+++ b/lib/mbswidth.h
+-@@ -45,6 +45,10 @@ extern "C" {
+-    control characters and 1 otherwise.  */
+- #define MBSW_REJECT_UNPRINTABLE 2
+- 
+-+/* If this bit is set \0 is treated as the end of string.
+-+   Otherwise it's treated as a normal one column width character.  */
+-+#define MBSW_STOP_AT_NUL 4
+-+
+- 
+- /* Returns the number of screen columns needed for STRING.  */
+- #define mbswidth gnu_mbswidth  /* avoid clash with UnixWare 7.1.1 function */
+diff --git a/grub-core/lib/gnulib-patches/no-abort.patch b/grub-core/lib/gnulib-patches/no-abort.patch
+deleted file mode 100644
+index e469c4762eb..00000000000
+--- a/grub-core/lib/gnulib-patches/no-abort.patch
++++ /dev/null
+@@ -1,26 +0,0 @@
+-diff --git a/lib/regcomp.c b/lib/regcomp.c
+-index cc85f35ac..de45ebb5c 100644
+---- a/lib/regcomp.c
+-+++ b/lib/regcomp.c
+-@@ -528,9 +528,9 @@ regerror (int errcode, const regex_t *__restrict preg, char *__restrict errbuf,
+-        to this routine.  If we are given anything else, or if other regex
+-        code generates an invalid error code, then the program has a bug.
+-        Dump core so we can fix it.  */
+--    abort ();
+--
+--  msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]);
+-+    msg = gettext ("unknown regexp error");
+-+  else
+-+    msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]);
+- 
+-   msg_size = strlen (msg) + 1; /* Includes the null.  */
+- 
+-@@ -1136,7 +1136,7 @@ optimize_utf8 (re_dfa_t *dfa)
+- 	}
+- 	break;
+-       default:
+--	abort ();
+-+	break;
+-       }
+- 
+-   if (mb_chars || has_period)
diff --git a/SOURCES/0137-Fix-build-error-with-the-fdt-module-on-risc-v.patch b/SOURCES/0137-Fix-build-error-with-the-fdt-module-on-risc-v.patch
new file mode 100644
index 0000000..35f2878
--- /dev/null
+++ b/SOURCES/0137-Fix-build-error-with-the-fdt-module-on-risc-v.patch
@@ -0,0 +1,40 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Tue, 27 Aug 2019 10:34:24 +0200
+Subject: [PATCH] Fix build error with the fdt module on risc-v
+
+The risc-v architecture also uses Device Trees, but the symbols in the
+fdt header aren't defined for this arch which lead to following error:
+
+BUILDSTDERR: ../../grub-core/loader/efi/fdt.c: In function 'grub_fdt_load':
+BUILDSTDERR: ../../grub-core/loader/efi/fdt.c:48:39: warning: implicit declaration of function 'grub_fdt_get_totalsize' [-Wimplicit-function-declaration]
+BUILDSTDERR:    48 |       size = GRUB_EFI_BYTES_TO_PAGES (grub_fdt_get_totalsize (fdt));
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ include/grub/fdt.h | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/include/grub/fdt.h b/include/grub/fdt.h
+index 2041341fd68..3514aa4a5b6 100644
+--- a/include/grub/fdt.h
++++ b/include/grub/fdt.h
+@@ -19,7 +19,8 @@
+ #ifndef GRUB_FDT_HEADER
+ #define GRUB_FDT_HEADER	1
+ 
+-#if !defined(GRUB_MACHINE_EMU) && (defined(__arm__) || defined(__aarch64__))
++#if !defined(GRUB_MACHINE_EMU) && \
++    (defined(__arm__) || defined(__aarch64__) || defined(__riscv))
+ 
+ #include <grub/types.h>
+ #include <grub/symbol.h>
+@@ -146,6 +147,7 @@ int EXPORT_FUNC(grub_fdt_set_prop) (void *fdt, unsigned int nodeoffset, const ch
+   grub_fdt_set_prop ((fdt), (nodeoffset), "reg", reg_64, 16);  \
+ })
+ 
+-#endif /* defined(__arm__) || defined(__aarch64__) */
++#endif /* !defined(GRUB_MACHINE_EMU) && \
++          (defined(__arm__) || defined(__aarch64__) || defined(__riscv)) */
+ 
+ #endif	/* ! GRUB_FDT_HEADER */
diff --git a/SOURCES/0138-grub-set-bootflag-Update-comment-about-running-as-ro.patch b/SOURCES/0138-grub-set-bootflag-Update-comment-about-running-as-ro.patch
new file mode 100644
index 0000000..cd4ef77
--- /dev/null
+++ b/SOURCES/0138-grub-set-bootflag-Update-comment-about-running-as-ro.patch
@@ -0,0 +1,27 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Wed, 13 Nov 2019 12:15:43 +0100
+Subject: [PATCH] grub-set-bootflag: Update comment about running as root
+ through pkexec
+
+We have stopped using pkexec for grub-set-bootflag, instead it is now
+installed suid root, update the comment accordingly.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ util/grub-set-bootflag.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/util/grub-set-bootflag.c b/util/grub-set-bootflag.c
+index 6a79ee67444..65d74ce010f 100644
+--- a/util/grub-set-bootflag.c
++++ b/util/grub-set-bootflag.c
+@@ -18,7 +18,7 @@
+  */
+ 
+ /*
+- * NOTE this gets run by users as root (through pkexec), so this does not
++ * NOTE this gets run by users as root (its suid root), so this does not
+  * use any grub library / util functions to allow for easy auditing.
+  * The grub headers are only included to get certain defines.
+  */
diff --git a/SOURCES/0139-grub-set-bootflag-Write-new-env-to-tmpfile-and-then-.patch b/SOURCES/0139-grub-set-bootflag-Write-new-env-to-tmpfile-and-then-.patch
new file mode 100644
index 0000000..122bf68
--- /dev/null
+++ b/SOURCES/0139-grub-set-bootflag-Write-new-env-to-tmpfile-and-then-.patch
@@ -0,0 +1,152 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Wed, 13 Nov 2019 13:02:01 +0100
+Subject: [PATCH] grub-set-bootflag: Write new env to tmpfile and then rename
+
+Make the grubenv writing code in grub-set-bootflag more robust by
+writing the modified grubenv to a tmpfile first and then renaming the
+tmpfile over the old grubenv (following symlinks).
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ util/grub-set-bootflag.c | 87 +++++++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 78 insertions(+), 9 deletions(-)
+
+diff --git a/util/grub-set-bootflag.c b/util/grub-set-bootflag.c
+index 65d74ce010f..d1c5e28862b 100644
+--- a/util/grub-set-bootflag.c
++++ b/util/grub-set-bootflag.c
+@@ -28,7 +28,9 @@
+ #include <grub/err.h>
+ #include <grub/lib/envblk.h> /* For GRUB_ENVBLK_DEFCFG define */
+ #include <errno.h>
++#include <limits.h>
+ #include <stdio.h>
++#include <stdlib.h>
+ #include <string.h>
+ #include <unistd.h>
+ 
+@@ -54,8 +56,10 @@ int main(int argc, char *argv[])
+ {
+   /* NOTE buf must be at least the longest bootflag length + 4 bytes */
+   char env[GRUBENV_SIZE + 1], buf[64], *s;
++  /* +1 for 0 termination, +6 for "XXXXXX" in tmp filename */
++  char env_filename[PATH_MAX + 1], tmp_filename[PATH_MAX + 6 + 1];
+   const char *bootflag;
+-  int i, len, ret;
++  int i, fd, len, ret;
+   FILE *f;
+ 
+   if (argc != 2)
+@@ -77,7 +81,32 @@ int main(int argc, char *argv[])
+   bootflag = bootflags[i];
+   len = strlen (bootflag);
+ 
+-  f = fopen (GRUBENV, "r");
++  /*
++   * Really become root. setuid avoids an user killing us, possibly leaking
++   * the tmpfile. setgid avoids the new grubenv's gid being that of the user.
++   */
++  ret = setuid(0);
++  if (ret)
++    {
++      perror ("Error setuid(0) failed");
++      return 1;
++    }
++
++  ret = setgid(0);
++  if (ret)
++    {
++      perror ("Error setgid(0) failed");
++      return 1;
++    }
++
++  /* Canonicalize GRUBENV filename, resolving symlinks, etc. */
++  if (!realpath(GRUBENV, env_filename))
++    {
++      perror ("Error canonicalizing " GRUBENV " filename");
++      return 1;
++    }
++
++  f = fopen (env_filename, "r");
+   if (!f)
+     {
+       perror ("Error opening " GRUBENV " for reading");
+@@ -132,30 +161,70 @@ int main(int argc, char *argv[])
+   snprintf(buf, sizeof(buf), "%s=1\n", bootflag);
+   memcpy(s, buf, len + 3);
+ 
+-  /* "r+", don't truncate so that the diskspace stays reserved */
+-  f = fopen (GRUBENV, "r+");
++
++  /*
++   * Create a tempfile for writing the new env.  Use the canonicalized filename
++   * for the template so that the tmpfile is in the same dir / on same fs.
++   */
++  snprintf(tmp_filename, sizeof(tmp_filename), "%sXXXXXX", env_filename);
++  fd = mkstemp(tmp_filename);
++  if (fd == -1)
++    {
++      perror ("Creating tmpfile failed");
++      return 1;
++    }
++
++  f = fdopen (fd, "w");
+   if (!f)
+     {
+-      perror ("Error opening " GRUBENV " for writing");
++      perror ("Error fdopen of tmpfile failed");
++      unlink(tmp_filename);
+       return 1;     
+     }
+ 
+   ret = fwrite (env, 1, GRUBENV_SIZE, f);
+   if (ret != GRUBENV_SIZE)
+     {
+-      perror ("Error writing to " GRUBENV);
++      perror ("Error writing tmpfile");
++      unlink(tmp_filename);
+       return 1;     
+     }
+ 
+   ret = fflush (f);
+   if (ret)
+     {
+-      perror ("Error flushing " GRUBENV);
++      perror ("Error flushing tmpfile");
++      unlink(tmp_filename);
+       return 1;     
+     }
+ 
+-  fsync (fileno (f));
+-  fclose (f);
++  ret = fsync (fileno (f));
++  if (ret)
++    {
++      perror ("Error syncing tmpfile");
++      unlink(tmp_filename);
++      return 1;
++    }
++
++  ret = fclose (f);
++  if (ret)
++    {
++      perror ("Error closing tmpfile");
++      unlink(tmp_filename);
++      return 1;
++    }
++
++  /*
++   * And finally rename the tmpfile with the new env over the old env, the
++   * linux kernel guarantees that this is atomic (from a syscall pov).
++   */
++  ret = rename(tmp_filename, env_filename);
++  if (ret)
++    {
++      perror ("Error renaming tmpfile to " GRUBENV " failed");
++      unlink(tmp_filename);
++      return 1;
++    }
+ 
+   return 0;
+ }
diff --git a/SOURCES/0140-grub.d-Fix-boot_indeterminate-getting-set-on-boot_su.patch b/SOURCES/0140-grub.d-Fix-boot_indeterminate-getting-set-on-boot_su.patch
new file mode 100644
index 0000000..54b73e6
--- /dev/null
+++ b/SOURCES/0140-grub.d-Fix-boot_indeterminate-getting-set-on-boot_su.patch
@@ -0,0 +1,75 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 26 Nov 2019 09:51:41 +0100
+Subject: [PATCH] grub.d: Fix boot_indeterminate getting set on boot_success=0
+ boot
+
+The "grub.d: Split out boot success reset from menu auto hide script"
+not only moved the code to clear boot_success and boot_indeterminate
+but for some reason also mixed in some broken changes to the
+boot_indeterminate handling.
+
+The boot_indeterminate var is meant to suppress the boot menu after
+a reboot from either a selinux-relabel or offline-updates. These
+2 special boot scenarios do not set boot_success since there is no
+successfull interaction with the user. Instead they increment
+boot_indeterminate, and if it is 1 and only when it is 1, so the
+first reboot after a "special" boot we suppress the menu.
+
+To ensure that we do show the menu if we somehow get stuck in a
+"special" boot loop where we do special-boots without them
+incrementing boot_indeterminate, the code before the
+"grub.d: Split out boot success reset from menu auto hide script"
+commit would increment boot_indeterminate once when it is 1, so that
+even if the "special" boot reboot-loop immediately we would show the
+menu on the next boot.
+
+That commit broke this however, because it not only moves the code,
+it also changes it from only "incrementing" boot_indeterminate once to
+always incrementing it, except when boot_success == 1 (and we reset it).
+
+This broken behavior causes the following problem:
+
+1. Boot a broken kernel, system hangs, power-cycle
+2. boot_success now != 1, so we increment boot_indeterminate from 0
+   (unset!) to 1. User either simply tries again, or makes some changes
+   but the end-result still is a system hang, power-cycle
+3. Now boot_indeterminate==1 so we do not show the menu even though the
+   previous boot failed -> BAD
+
+This commit fixes this by restoring the behavior of setting
+boot_indeterminate to 2 when it was 1 before.
+
+Fixes: "grub.d: Split out boot success reset from menu auto hide script"
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ util/grub.d/10_reset_boot_success.in | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/util/grub.d/10_reset_boot_success.in b/util/grub.d/10_reset_boot_success.in
+index 6c88d933dde..737e1ae5b68 100644
+--- a/util/grub.d/10_reset_boot_success.in
++++ b/util/grub.d/10_reset_boot_success.in
+@@ -6,18 +6,18 @@
+ #
+ # The boot_success var needs to be set to 1 from userspace to mark a boot successful.
+ cat << EOF
+-insmod increment
+ # Hiding the menu is ok if last boot was ok or if this is a first boot attempt to boot the entry
+ if [ "\${boot_success}" = "1" -o "\${boot_indeterminate}" = "1" ]; then
+   set menu_hide_ok=1
+ else
+   set menu_hide_ok=0 
+ fi
+-# Reset boot_indeterminate after a successful boot, increment otherwise
++# Reset boot_indeterminate after a successful boot
+ if [ "\${boot_success}" = "1" ] ; then
+   set boot_indeterminate=0
+-else
+-  increment boot_indeterminate
++# Avoid boot_indeterminate causing the menu to be hidden more then once
++elif [ "\${boot_indeterminate}" = "1" ]; then
++  set boot_indeterminate=2
+ fi
+ # Reset boot_success for current boot 
+ set boot_success=0
diff --git a/SOURCES/0141-Also-define-GRUB_EFI_MAX_ALLOCATION_ADDRESS-for-RISC.patch b/SOURCES/0141-Also-define-GRUB_EFI_MAX_ALLOCATION_ADDRESS-for-RISC.patch
new file mode 100644
index 0000000..b694ec9
--- /dev/null
+++ b/SOURCES/0141-Also-define-GRUB_EFI_MAX_ALLOCATION_ADDRESS-for-RISC.patch
@@ -0,0 +1,24 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: David Abdurachmanov <david.abdurachmanov@sifive.com>
+Date: Thu, 16 Jan 2020 13:10:10 +0100
+Subject: [PATCH] Also define GRUB_EFI_MAX_ALLOCATION_ADDRESS for RISC-V
+
+The commit "Try to pick better locations for kernel and initrd" missed to
+define this macro for the RISC-V (riscv64) architecture, so add it there.
+
+Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
+---
+ include/grub/riscv64/efi/memory.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/include/grub/riscv64/efi/memory.h b/include/grub/riscv64/efi/memory.h
+index c6cb3241714..acb61dca44b 100644
+--- a/include/grub/riscv64/efi/memory.h
++++ b/include/grub/riscv64/efi/memory.h
+@@ -2,5 +2,6 @@
+ #include <grub/efi/memory.h>
+ 
+ #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffffffffULL
++#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS
+ 
+ #endif /* ! GRUB_MEMORY_CPU_HEADER */
diff --git a/SOURCES/0142-chainloader-Define-machine-types-for-RISC-V.patch b/SOURCES/0142-chainloader-Define-machine-types-for-RISC-V.patch
new file mode 100644
index 0000000..480db52
--- /dev/null
+++ b/SOURCES/0142-chainloader-Define-machine-types-for-RISC-V.patch
@@ -0,0 +1,31 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: David Abdurachmanov <david.abdurachmanov@sifive.com>
+Date: Sat, 9 Nov 2019 18:06:32 +0000
+Subject: [PATCH] chainloader: Define machine types for RISC-V
+
+The commit "Add secureboot support on efi chainloader" didn't add machine
+types for RISC-V, so this patch adds them.
+
+Note, that grub-core/loader/riscv/linux.c is skipped because Linux is not
+supported yet. This patch might need a new revision once that's the case.
+
+Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
+---
+ grub-core/loader/efi/chainloader.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
+index 47f5aa14817..ac8dfd40c61 100644
+--- a/grub-core/loader/efi/chainloader.c
++++ b/grub-core/loader/efi/chainloader.c
+@@ -333,6 +333,10 @@ static const grub_uint16_t machine_type __attribute__((__unused__)) =
+   GRUB_PE32_MACHINE_I386;
+ #elif defined(__ia64__)
+   GRUB_PE32_MACHINE_IA64;
++#elif defined(__riscv) && (__riscv_xlen == 32)
++  GRUB_PE32_MACHINE_RISCV32;
++#elif defined(__riscv) && (__riscv_xlen == 64)
++  GRUB_PE32_MACHINE_RISCV64;
+ #else
+ #error this architecture is not supported by grub2
+ #endif
diff --git a/SOURCES/0143-Add-start-symbol-for-RISC-V.patch b/SOURCES/0143-Add-start-symbol-for-RISC-V.patch
new file mode 100644
index 0000000..677efae
--- /dev/null
+++ b/SOURCES/0143-Add-start-symbol-for-RISC-V.patch
@@ -0,0 +1,28 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: David Abdurachmanov <david.abdurachmanov@sifive.com>
+Date: Sat, 9 Nov 2019 19:51:57 +0000
+Subject: [PATCH] Add start symbol for RISC-V
+
+All other architectures have start symbol.
+
+Hopefully this resolves:
+
+    BUILDSTDERR: ././grub-mkimage: error: undefined symbol start.
+
+Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
+---
+ grub-core/kern/riscv/efi/startup.S | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/grub-core/kern/riscv/efi/startup.S b/grub-core/kern/riscv/efi/startup.S
+index f2a7b2b1ede..781773136e8 100644
+--- a/grub-core/kern/riscv/efi/startup.S
++++ b/grub-core/kern/riscv/efi/startup.S
+@@ -29,6 +29,7 @@
+ 
+ 	.file 	"startup.S"
+ 	.text
++FUNCTION(start)
+ FUNCTION(_start)
+ 	/*
+ 	 *  EFI_SYSTEM_TABLE and EFI_HANDLE are passed in a1/a0.
diff --git a/SOURCES/0144-bootstrap.conf-Force-autogen.sh-to-use-python3.patch b/SOURCES/0144-bootstrap.conf-Force-autogen.sh-to-use-python3.patch
new file mode 100644
index 0000000..5c3b968
--- /dev/null
+++ b/SOURCES/0144-bootstrap.conf-Force-autogen.sh-to-use-python3.patch
@@ -0,0 +1,33 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Wed, 15 Jan 2020 12:47:46 +0100
+Subject: [PATCH] bootstrap.conf: Force autogen.sh to use python3
+
+The python-unversioned-command package is not installed in the buildroot,
+but the bootstrap script expects the python command to be present if one
+is not defined. So building the package leads to the following error:
+
+./autogen.sh: line 20: python: command not found
+
+This is harmless since gnulib is included as a source anyways, because the
+builders can't download. But still the issue should be fixed by forcing to
+use python3 that's the default in Fedora now.
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ bootstrap.conf | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/bootstrap.conf b/bootstrap.conf
+index 452f4d79b0d..03f10930230 100644
+--- a/bootstrap.conf
++++ b/bootstrap.conf
+@@ -93,7 +93,7 @@ bootstrap_post_import_hook () {
+     patch -d po -p3 \
+       < "po/gettext-patches/$patchname.patch"
+   done
+-  FROM_BOOTSTRAP=1 ./autogen.sh
++  PYTHON=python3 FROM_BOOTSTRAP=1 ./autogen.sh
+   set +e  # bootstrap expects this
+ }
+ 
diff --git a/SOURCES/0145-efi-http-Export-fw-http-_path-variables-to-make-them.patch b/SOURCES/0145-efi-http-Export-fw-http-_path-variables-to-make-them.patch
new file mode 100644
index 0000000..6f2e93d
--- /dev/null
+++ b/SOURCES/0145-efi-http-Export-fw-http-_path-variables-to-make-them.patch
@@ -0,0 +1,50 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Thu, 5 Mar 2020 16:21:47 +0100
+Subject: [PATCH] efi/http: Export {fw,http}_path variables to make them global
+
+The fw_path environment variable is used by http_configure() function to
+determine the HTTP path that should be used as prefix when using relative
+HTTP paths. And this is stored in the http_path environment variable.
+
+Later, that variable is looked up by grub_efihttp_open() to generate the
+complete path to be used in the HTTP request.
+
+But these variables are not exported, which means that are not global and
+so are only found in the initial context.
+
+This can cause commands like configfile that create a new context to fail
+because the fw_path and http_path variables will not be found.
+
+Resolves: rhbz#1616395
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/kern/main.c    | 1 +
+ grub-core/net/efi/http.c | 1 +
+ 2 files changed, 2 insertions(+)
+
+diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c
+index 4ec3f5e4d33..0285e95a2bb 100644
+--- a/grub-core/kern/main.c
++++ b/grub-core/kern/main.c
+@@ -143,6 +143,7 @@ grub_set_prefix_and_root (void)
+       if (fw_path)
+ 	{
+ 	  grub_env_set ("fw_path", fw_path);
++	  grub_env_export ("fw_path");
+ 	  grub_dprintf ("fw_path", "fw_path:\"%s\"\n", fw_path);
+ 	  grub_free (fw_path);
+ 	}
+diff --git a/grub-core/net/efi/http.c b/grub-core/net/efi/http.c
+index de351b2cd03..755b7a6d054 100644
+--- a/grub-core/net/efi/http.c
++++ b/grub-core/net/efi/http.c
+@@ -39,6 +39,7 @@ http_configure (struct grub_efi_net_device *dev, int prefer_ip6)
+ 	  http_path++;
+ 	  grub_env_unset ("http_path");
+ 	  grub_env_set ("http_path", http_path);
++	  grub_env_export ("http_path");
+ 	}
+     }
+ 
diff --git a/SOURCES/0146-efi-http-Enclose-literal-IPv6-addresses-in-square-br.patch b/SOURCES/0146-efi-http-Enclose-literal-IPv6-addresses-in-square-br.patch
new file mode 100644
index 0000000..c394549
--- /dev/null
+++ b/SOURCES/0146-efi-http-Enclose-literal-IPv6-addresses-in-square-br.patch
@@ -0,0 +1,114 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Thu, 5 Mar 2020 16:21:58 +0100
+Subject: [PATCH] efi/http: Enclose literal IPv6 addresses in square brackets
+
+According to RFC 2732 (https://www.ietf.org/rfc/rfc2732.txt), literal IPv6
+addresses must be enclosed in square brackets. But GRUB currently does not
+do this and is causing HTTP servers to send Bad Request (400) responses.
+
+For example, the following is the HTTP stream when fetching a config file:
+
+HEAD /EFI/BOOT/grub.cfg HTTP/1.1
+Host: 2000:dead:beef:a::1
+Accept: */*
+User-Agent: UefiHttpBoot/1.0
+
+HTTP/1.1 400 Bad Request
+Date: Thu, 05 Mar 2020 14:46:02 GMT
+Server: Apache/2.4.41 (Fedora) OpenSSL/1.1.1d
+Connection: close
+Content-Type: text/html; charset=iso-8859-1
+
+and after enclosing the IPv6 address the HTTP request is successful:
+
+HEAD /EFI/BOOT/grub.cfg HTTP/1.1
+Host: [2000:dead:beef:a::1]
+Accept: */*
+User-Agent: UefiHttpBoot/1.0
+
+HTTP/1.1 200 OK
+Date: Thu, 05 Mar 2020 14:48:04 GMT
+Server: Apache/2.4.41 (Fedora) OpenSSL/1.1.1d
+Last-Modified: Thu, 27 Feb 2020 17:45:58 GMT
+ETag: "206-59f924b24b1da"
+Accept-Ranges: bytes
+Content-Length: 518
+
+Resolves: rhbz#1732765
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/net/efi/http.c | 37 ++++++++++++++++++++++++++++---------
+ 1 file changed, 28 insertions(+), 9 deletions(-)
+
+diff --git a/grub-core/net/efi/http.c b/grub-core/net/efi/http.c
+index 755b7a6d054..fc8cb25ae0a 100644
+--- a/grub-core/net/efi/http.c
++++ b/grub-core/net/efi/http.c
+@@ -158,13 +158,7 @@ efihttp_request (grub_efi_http_t *http, char *server, char *name, int use_https,
+   grub_efi_status_t status;
+   grub_efi_boot_services_t *b = grub_efi_system_table->boot_services;
+   char *url = NULL;
+-
+-  request_headers[0].field_name = (grub_efi_char8_t *)"Host";
+-  request_headers[0].field_value = (grub_efi_char8_t *)server;
+-  request_headers[1].field_name = (grub_efi_char8_t *)"Accept";
+-  request_headers[1].field_value = (grub_efi_char8_t *)"*/*";
+-  request_headers[2].field_name = (grub_efi_char8_t *)"User-Agent";
+-  request_headers[2].field_value = (grub_efi_char8_t *)"UefiHttpBoot/1.0";
++  char *hostname = NULL;
+ 
+   {
+     grub_efi_ipv6_address_t address;
+@@ -174,9 +168,24 @@ efihttp_request (grub_efi_http_t *http, char *server, char *name, int use_https,
+     const char *protocol = (use_https == 1) ? "https" : "http";
+ 
+     if (grub_efi_string_to_ip6_address (server, &address, &rest) && *rest == 0)
+-      url = grub_xasprintf ("%s://[%s]%s", protocol, server, name);
++      {
++        hostname = grub_xasprintf ("[%s]", server);
++        if (!hostname)
++          return GRUB_ERR_OUT_OF_MEMORY;
++
++        server = hostname;
++
++        url = grub_xasprintf ("%s://%s%s", protocol, server, name);
++        if (!url)
++          {
++            grub_free (hostname);
++            return GRUB_ERR_OUT_OF_MEMORY;
++          }
++      }
+     else
+-      url = grub_xasprintf ("%s://%s%s", protocol, server, name);
++      {
++        url = grub_xasprintf ("%s://%s%s", protocol, server, name);
++      }
+ 
+     if (!url)
+       {
+@@ -199,6 +208,13 @@ efihttp_request (grub_efi_http_t *http, char *server, char *name, int use_https,
+     request_data.url = ucs2_url;
+   }
+ 
++  request_headers[0].field_name = (grub_efi_char8_t *)"Host";
++  request_headers[0].field_value = (grub_efi_char8_t *)server;
++  request_headers[1].field_name = (grub_efi_char8_t *)"Accept";
++  request_headers[1].field_value = (grub_efi_char8_t *)"*/*";
++  request_headers[2].field_name = (grub_efi_char8_t *)"User-Agent";
++  request_headers[2].field_value = (grub_efi_char8_t *)"UefiHttpBoot/1.0";
++
+   request_data.method = (headeronly > 0) ? GRUB_EFI_HTTPMETHODHEAD : GRUB_EFI_HTTPMETHODGET;
+ 
+   request_message.data.request = &request_data;
+@@ -228,6 +244,9 @@ efihttp_request (grub_efi_http_t *http, char *server, char *name, int use_https,
+ 
+   status = efi_call_2 (http->request, http, &request_token);
+ 
++  if (hostname)
++    grub_free (hostname);
++
+   if (status != GRUB_EFI_SUCCESS)
+     {
+       efi_call_1 (b->close_event, request_token.event);
diff --git a/SOURCES/0147-efi-net-Allow-to-specify-a-port-number-in-addresses.patch b/SOURCES/0147-efi-net-Allow-to-specify-a-port-number-in-addresses.patch
new file mode 100644
index 0000000..209aed8
--- /dev/null
+++ b/SOURCES/0147-efi-net-Allow-to-specify-a-port-number-in-addresses.patch
@@ -0,0 +1,48 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Mon, 9 Mar 2020 15:29:45 +0100
+Subject: [PATCH] efi/net: Allow to specify a port number in addresses
+
+The grub_efi_net_parse_address() function is not covering the case where a
+port number is specified in an IPv4 or IPv6 address, so will fail to parse
+the network address.
+
+For most cases the issue is harmless, because the function is only used to
+match an address with a network interface and if fails the default is used.
+
+But still is a bug that has to be fixed and it causes error messages to be
+printed like the following:
+
+error: net/efi/net.c:782:unrecognised network address '192.168.122.1:8080'
+
+error: net/efi/net.c:781:unrecognised network address '[2000:dead:beef:a::1]:8080'
+
+Resolves: rhbz#1732765
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/net/efi/net.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/net/efi/net.c b/grub-core/net/efi/net.c
+index 6603cd83edc..84573937b18 100644
+--- a/grub-core/net/efi/net.c
++++ b/grub-core/net/efi/net.c
+@@ -742,7 +742,7 @@ grub_efi_net_parse_address (const char *address,
+ 	      return GRUB_ERR_NONE;
+ 	    }
+ 	}
+-      else if (*rest == 0)
++      else if (*rest == 0 || *rest == ':')
+ 	{
+ 	  grub_uint32_t subnet_mask = 0xffffffffU;
+ 	  grub_memcpy (ip4->subnet_mask, &subnet_mask, sizeof (ip4->subnet_mask));
+@@ -768,7 +768,7 @@ grub_efi_net_parse_address (const char *address,
+ 	      return GRUB_ERR_NONE;
+ 	    }
+ 	}
+-      else if (*rest == 0)
++      else if (*rest == 0 || *rest == ':')
+ 	{
+ 	  ip6->prefix_length = 128;
+ 	  ip6->is_anycast = 0;
diff --git a/SOURCES/0148-efi-ip4_config-Improve-check-to-detect-literal-IPv6-.patch b/SOURCES/0148-efi-ip4_config-Improve-check-to-detect-literal-IPv6-.patch
new file mode 100644
index 0000000..f92dee4
--- /dev/null
+++ b/SOURCES/0148-efi-ip4_config-Improve-check-to-detect-literal-IPv6-.patch
@@ -0,0 +1,48 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Mon, 9 Mar 2020 15:30:05 +0100
+Subject: [PATCH] efi/ip4_config: Improve check to detect literal IPv6
+ addresses
+
+The grub_efi_string_to_ip4_address() function wrongly assumes that an IPv6
+address is an IPv4 address, because it doesn't take into account the case
+of a caller passing an IPv6 address as a string.
+
+This leads to the grub_efi_net_parse_address() function to fail and print
+the following error message:
+
+error: net/efi/net.c:785:unrecognised network address '2000:dead:beef:a::1'
+
+Resolves: rhbz#1732765
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/net/efi/ip4_config.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/net/efi/ip4_config.c b/grub-core/net/efi/ip4_config.c
+index b711a5d9457..313c818b184 100644
+--- a/grub-core/net/efi/ip4_config.c
++++ b/grub-core/net/efi/ip4_config.c
+@@ -56,9 +56,20 @@ int
+ grub_efi_string_to_ip4_address (const char *val, grub_efi_ipv4_address_t *address, const char **rest)
+ {
+   grub_uint32_t newip = 0;
+-  int i;
++  int i, ncolon = 0;
+   const char *ptr = val;
+ 
++  /* Check that is not an IPv6 address */
++  for (i = 0; i < grub_strlen(ptr); i++)
++    {
++      if (ptr[i] == '[' && i == 0)
++        return 0;
++
++      if (ptr[i] == ':')
++          if (i == 0 || ++ncolon == 2)
++            return 0;
++    }
++
+   for (i = 0; i < 4; i++)
+     {
+       unsigned long t;
diff --git a/SOURCES/0149-efi-net-Print-a-debug-message-if-parsing-the-address.patch b/SOURCES/0149-efi-net-Print-a-debug-message-if-parsing-the-address.patch
new file mode 100644
index 0000000..33d8f88
--- /dev/null
+++ b/SOURCES/0149-efi-net-Print-a-debug-message-if-parsing-the-address.patch
@@ -0,0 +1,68 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Tue, 10 Mar 2020 11:23:49 +0100
+Subject: [PATCH] efi/net: Print a debug message if parsing the address fails
+
+Currently if parsing the address fails an error message is printed. But in
+most cases this isn't a fatal error since the grub_efi_net_parse_address()
+function is only used to match an address with a network interface to use.
+
+And if this fails, the default interface is used which is good enough for
+most cases. So instead of printing an error that would pollute the console
+just print a debug message if the address is not parsed correctly.
+
+A user can enable debug messages for the efinet driver to have information
+about the failure and the fact that the default interface is being used.
+
+Related: rhbz#1732765
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/net/efi/net.c | 18 +++++++++++-------
+ 1 file changed, 11 insertions(+), 7 deletions(-)
+
+diff --git a/grub-core/net/efi/net.c b/grub-core/net/efi/net.c
+index 84573937b18..a3f0535d43c 100644
+--- a/grub-core/net/efi/net.c
++++ b/grub-core/net/efi/net.c
+@@ -778,9 +778,9 @@ grub_efi_net_parse_address (const char *address,
+ 	}
+     }
+ 
+-  return grub_error (GRUB_ERR_NET_BAD_ADDRESS,
+-		   N_("unrecognised network address `%s'"),
+-		   address);
++  grub_dprintf ("efinet", "unrecognised network address '%s'\n", address);
++
++  return GRUB_ERR_NET_BAD_ADDRESS;
+ }
+ 
+ static grub_efi_net_interface_t *
+@@ -795,10 +795,7 @@ match_route (const char *server)
+   err = grub_efi_net_parse_address (server, &ip4, &ip6, &is_ip6, 0);
+ 
+   if (err)
+-    {
+-      grub_print_error ();
+       return NULL;
+-    }
+ 
+   if (is_ip6)
+     {
+@@ -1233,8 +1230,15 @@ grub_net_open_real (const char *name __attribute__ ((unused)))
+   /*FIXME: Use DNS translate name to address */
+   net_interface = match_route (server);
+ 
++  if (!net_interface && net_default_interface)
++    {
++      net_interface = net_default_interface;
++      grub_dprintf ("efinet", "interface lookup failed, using default '%s'\n",
++                    net_interface->name);
++    }
++
+   /*XXX: should we check device with default gateway ? */
+-  if (!net_interface && !(net_interface = net_default_interface))
++  if (!net_interface)
+     {
+       grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' no route found"),
+ 		  name);
diff --git a/SOURCES/0150-kern-term-Also-accept-F8-as-a-user-interrupt-key.patch b/SOURCES/0150-kern-term-Also-accept-F8-as-a-user-interrupt-key.patch
new file mode 100644
index 0000000..3dd525a
--- /dev/null
+++ b/SOURCES/0150-kern-term-Also-accept-F8-as-a-user-interrupt-key.patch
@@ -0,0 +1,30 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Wed, 22 Apr 2020 12:41:52 +0200
+Subject: [PATCH] kern/term: Also accept F8 as a user interrupt key
+
+Make F8, which used to be the hotkey to show the Windows boot menu during
+boot for a long long time, also interrupt sleeps / stop the menu countdown.
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/kern/term.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/kern/term.c b/grub-core/kern/term.c
+index 14d59649832..4d61f4e9790 100644
+--- a/grub-core/kern/term.c
++++ b/grub-core/kern/term.c
+@@ -144,9 +144,10 @@ grub_key_is_interrupt (int key)
+   /*
+    * ESC sometimes is the BIOS setup hotkey and may be hard to discover, also
+    * check F4, which was chosen because is not used as a hotkey to enter the
+-   * BIOS setup by any vendor.
++   * BIOS setup by any vendor. Also, F8 which was the key to get the Windows
++   * bootmenu for a long time.
+    */
+-  if (key == GRUB_TERM_ESC || key == GRUB_TERM_KEY_F4)
++  if (key == GRUB_TERM_ESC || key == GRUB_TERM_KEY_F4 || key == GRUB_TERM_KEY_F8)
+     return 1;
+ 
+   /*
diff --git a/SOURCES/0151-efi-Set-image-base-address-before-jumping-to-the-PE-.patch b/SOURCES/0151-efi-Set-image-base-address-before-jumping-to-the-PE-.patch
new file mode 100644
index 0000000..336bcf1
--- /dev/null
+++ b/SOURCES/0151-efi-Set-image-base-address-before-jumping-to-the-PE-.patch
@@ -0,0 +1,62 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Thu, 23 Apr 2020 15:06:46 +0200
+Subject: [PATCH] efi: Set image base address before jumping to the PE/COFF
+ entry point
+
+Upstream GRUB uses the EFI LoadImage() and StartImage() to boot the Linux
+kernel. But our custom EFI loader that supports Secure Boot instead uses
+the EFI handover protocol (for x86) or jumping directly to the PE/COFF
+entry point (for aarch64).
+
+This is done to allow the bootloader to verify the images using the shim
+lock protocol to avoid booting untrusted binaries.
+
+Since the bootloader loads the kernel from the boot media instead of using
+LoadImage(), it is responsible to set the Loaded Image base address before
+booting the kernel.
+
+Otherwise the kernel EFI stub will complain that it was not set correctly
+and print the following warning message:
+
+EFI stub: ERROR: FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value
+
+Resolves: rhbz#1814690
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/loader/efi/linux.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c
+index 0622dfa48d4..e8b9ecb17f6 100644
+--- a/grub-core/loader/efi/linux.c
++++ b/grub-core/loader/efi/linux.c
+@@ -72,6 +72,7 @@ grub_err_t
+ grub_efi_linux_boot (void *kernel_addr, grub_off_t handover_offset,
+ 		     void *kernel_params)
+ {
++  grub_efi_loaded_image_t *loaded_image = NULL;
+   handover_func hf;
+   int offset = 0;
+ 
+@@ -79,6 +80,19 @@ grub_efi_linux_boot (void *kernel_addr, grub_off_t handover_offset,
+   offset = 512;
+ #endif
+ 
++  /*
++   * Since the EFI loader is not calling the LoadImage() and StartImage()
++   * services for loading the kernel and booting respectively, it has to
++   * set the Loaded Image base address.
++   */
++  loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle);
++  if (loaded_image)
++    loaded_image->image_base = kernel_addr;
++  else
++    grub_dprintf ("linux", "Loaded Image base address could not be set\n");
++
++  grub_dprintf ("linux", "kernel_addr: %p handover_offset: %p params: %p\n",
++		kernel_addr, (void *)(grub_efi_uintn_t)handover_offset, kernel_params);
+   hf = (handover_func)((char *)kernel_addr + handover_offset + offset);
+   hf (grub_efi_image_handle, grub_efi_system_table, kernel_params);
+ 
diff --git a/SOURCES/0152-tpm-Don-t-propagate-TPM-measurement-errors-to-the-ve.patch b/SOURCES/0152-tpm-Don-t-propagate-TPM-measurement-errors-to-the-ve.patch
new file mode 100644
index 0000000..747773d
--- /dev/null
+++ b/SOURCES/0152-tpm-Don-t-propagate-TPM-measurement-errors-to-the-ve.patch
@@ -0,0 +1,62 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Sat, 16 May 2020 11:33:18 +0200
+Subject: [PATCH] tpm: Don't propagate TPM measurement errors to the verifiers
+ layer
+
+Currently if the EFI firmware fails to do a TPM measurement for a file,
+the error will be propagated to the verifiers framework and so opening
+the file will not succeed.
+
+This mean that buggy firmwares will prevent the system to boot since the
+loader won't be able to open any file. But failing to do TPM measurements
+shouldn't be a fatal error and the system should still be able to boot.
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/commands/tpm.c | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+diff --git a/grub-core/commands/tpm.c b/grub-core/commands/tpm.c
+index 2052c36eaba..e287d042e6b 100644
+--- a/grub-core/commands/tpm.c
++++ b/grub-core/commands/tpm.c
+@@ -42,7 +42,8 @@ grub_tpm_verify_init (grub_file_t io,
+ static grub_err_t
+ grub_tpm_verify_write (void *context, void *buf, grub_size_t size)
+ {
+-  return grub_tpm_measure (buf, size, GRUB_BINARY_PCR, context);
++  grub_tpm_measure (buf, size, GRUB_BINARY_PCR, context);
++  return GRUB_ERR_NONE;
+ }
+ 
+ static grub_err_t
+@@ -50,7 +51,6 @@ grub_tpm_verify_string (char *str, enum grub_verify_string_type type)
+ {
+   const char *prefix = NULL;
+   char *description;
+-  grub_err_t status;
+ 
+   switch (type)
+     {
+@@ -66,15 +66,15 @@ grub_tpm_verify_string (char *str, enum grub_verify_string_type type)
+     }
+   description = grub_malloc (grub_strlen (str) + grub_strlen (prefix) + 1);
+   if (!description)
+-    return grub_errno;
++    return GRUB_ERR_NONE;
+   grub_memcpy (description, prefix, grub_strlen (prefix));
+   grub_memcpy (description + grub_strlen (prefix), str,
+ 	       grub_strlen (str) + 1);
+-  status =
+-    grub_tpm_measure ((unsigned char *) str, grub_strlen (str),
+-		      GRUB_STRING_PCR, description);
++
++  grub_tpm_measure ((unsigned char *) str, grub_strlen (str), GRUB_STRING_PCR,
++                    description);
+   grub_free (description);
+-  return status;
++  return GRUB_ERR_NONE;
+ }
+ 
+ struct grub_file_verifier grub_tpm_verifier = {
diff --git a/SOURCES/0153-x86-efi-Reduce-maximum-bounce-buffer-size-to-16-MiB.patch b/SOURCES/0153-x86-efi-Reduce-maximum-bounce-buffer-size-to-16-MiB.patch
new file mode 100644
index 0000000..825d0f7
--- /dev/null
+++ b/SOURCES/0153-x86-efi-Reduce-maximum-bounce-buffer-size-to-16-MiB.patch
@@ -0,0 +1,40 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Tue, 26 May 2020 16:59:28 +0200
+Subject: [PATCH] x86-efi: Reduce maximum bounce buffer size to 16 MiB
+
+The EFI linux loader allocates a bounce buffer to copy the initrd since in
+some machines doing DMA on addresses above 4GB is not possible during EFI.
+
+But the verifiers framework also allocates a buffer to copy the initrd in
+its grub_file_open() handler. It does this since the data to verify has to
+be passed as a single chunk to modules that use the verifiers framework.
+
+If the initrd image size is big there may not be enough memory in the heap
+to allocate two buffers of that size. This causes an allocation failure in
+the verifiers framework and leads to the initrd not being read.
+
+To prevent these allocation failures, let's reduce the maximum size of the
+bounce buffer used in the EFI loader. Since the data read can be copied to
+the actual initrd address in multilple chunks.
+
+Resolves: rhbz#1838633
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/loader/i386/efi/linux.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index 6bc18d5aef5..15d40d6e35b 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -144,7 +144,7 @@ grub_linuxefi_unload (void)
+   return GRUB_ERR_NONE;
+ }
+ 
+-#define BOUNCE_BUFFER_MAX 0x10000000ull
++#define BOUNCE_BUFFER_MAX 0x1000000ull
+ 
+ static grub_ssize_t
+ read(grub_file_t file, grub_uint8_t *bufp, grub_size_t len)
diff --git a/SOURCES/0154-http-Prepend-prefix-when-the-HTTP-path-is-relative-a.patch b/SOURCES/0154-http-Prepend-prefix-when-the-HTTP-path-is-relative-a.patch
new file mode 100644
index 0000000..97d2e06
--- /dev/null
+++ b/SOURCES/0154-http-Prepend-prefix-when-the-HTTP-path-is-relative-a.patch
@@ -0,0 +1,47 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Tue, 2 Jun 2020 13:25:01 +0200
+Subject: [PATCH] http: Prepend prefix when the HTTP path is relative as done
+ in efi/http
+
+There are two different HTTP drivers that can be used when requesting an
+HTTP resource: the efi/http that uses the EFI_HTTP_PROTOCOL and the http
+that uses GRUB's HTTP and TCP/IP implementation.
+
+The efi/http driver appends a prefix that is defined in the variable
+http_path, but the http driver doesn't.
+
+So using this driver and attempting to fetch a resource using a relative
+path fails.
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/net/http.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/net/http.c b/grub-core/net/http.c
+index b52b558d631..7f878b56157 100644
+--- a/grub-core/net/http.c
++++ b/grub-core/net/http.c
+@@ -501,13 +501,20 @@ http_open (struct grub_file *file, const char *filename)
+ {
+   grub_err_t err;
+   struct http_data *data;
++  const char *http_path;
+ 
+   data = grub_zalloc (sizeof (*data));
+   if (!data)
+     return grub_errno;
+   file->size = GRUB_FILE_SIZE_UNKNOWN;
+ 
+-  data->filename = grub_strdup (filename);
++  /* If path is relative, prepend http_path */
++  http_path = grub_env_get ("http_path");
++  if (http_path && filename[0] != '/')
++    data->filename = grub_xasprintf ("%s/%s", http_path, filename);
++  else
++    data->filename = grub_strdup (filename);
++
+   if (!data->filename)
+     {
+       grub_free (data);
diff --git a/SOURCES/0155-Fix-a-missing-return-in-efi-export-env-and-efi-load-.patch b/SOURCES/0155-Fix-a-missing-return-in-efi-export-env-and-efi-load-.patch
new file mode 100644
index 0000000..1657d7a
--- /dev/null
+++ b/SOURCES/0155-Fix-a-missing-return-in-efi-export-env-and-efi-load-.patch
@@ -0,0 +1,27 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Wed, 16 Jan 2019 13:21:46 -0500
+Subject: [PATCH] Fix a missing return in efi-export-env and efi-load-env
+ commands
+
+Somewhere along the way this got mis-merged to include a return without
+a value.  Fix it up.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/commands/efi/env.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/grub-core/commands/efi/env.c b/grub-core/commands/efi/env.c
+index cbd13e03e81..977edb6b065 100644
+--- a/grub-core/commands/efi/env.c
++++ b/grub-core/commands/efi/env.c
+@@ -149,6 +149,8 @@ grub_efi_load_env(grub_command_t cmd __attribute__ ((unused)),
+ 
+   grub_envblk_iterate (envblk, NULL, set_var);
+   grub_free (envblk_s.buf);
++
++  return GRUB_ERR_NONE;
+ }
+ 
+ static grub_command_t export_cmd, loadenv_cmd;
diff --git a/SOURCES/0156-efi-dhcp-fix-some-allocation-error-checking.patch b/SOURCES/0156-efi-dhcp-fix-some-allocation-error-checking.patch
new file mode 100644
index 0000000..7549733
--- /dev/null
+++ b/SOURCES/0156-efi-dhcp-fix-some-allocation-error-checking.patch
@@ -0,0 +1,37 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Sun, 19 Jul 2020 17:11:06 -0400
+Subject: [PATCH] efi+dhcp: fix some allocation error checking.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/net/efi/dhcp.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/net/efi/dhcp.c b/grub-core/net/efi/dhcp.c
+index dbef63d8c08..e5c79b748b0 100644
+--- a/grub-core/net/efi/dhcp.c
++++ b/grub-core/net/efi/dhcp.c
+@@ -80,7 +80,7 @@ grub_efi_dhcp4_parse_dns (grub_efi_dhcp4_protocol_t *dhcp4, grub_efi_dhcp4_packe
+   if (status != GRUB_EFI_BUFFER_TOO_SMALL)
+     return NULL;
+ 
+-  option_list = grub_malloc (option_count * sizeof(*option_list));
++  option_list = grub_calloc (option_count, sizeof(*option_list));
+   if (!option_list)
+     return NULL;
+ 
+@@ -360,8 +360,11 @@ grub_cmd_efi_bootp6 (struct grub_command *cmd __attribute__ ((unused)),
+ 
+ 	if (status == GRUB_EFI_BUFFER_TOO_SMALL && count)
+ 	  {
+-	    options = grub_malloc (count * sizeof(*options));
+-	    status = efi_call_4 (dev->dhcp6->parse, dev->dhcp6, mode.ia->reply_packet, &count, options);
++	    options = grub_calloc (count, sizeof(*options));
++	    if (options)
++	      status = efi_call_4 (dev->dhcp6->parse, dev->dhcp6, mode.ia->reply_packet, &count, options);
++	    else
++	      status = GRUB_EFI_OUT_OF_RESOURCES;
+ 	  }
+ 
+ 	if (status != GRUB_EFI_SUCCESS)
diff --git a/SOURCES/0157-efi-http-fix-some-allocation-error-checking.patch b/SOURCES/0157-efi-http-fix-some-allocation-error-checking.patch
new file mode 100644
index 0000000..4dffab9
--- /dev/null
+++ b/SOURCES/0157-efi-http-fix-some-allocation-error-checking.patch
@@ -0,0 +1,39 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Sun, 19 Jul 2020 17:14:15 -0400
+Subject: [PATCH] efi+http: fix some allocation error checking.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/net/efi/http.c | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+diff --git a/grub-core/net/efi/http.c b/grub-core/net/efi/http.c
+index fc8cb25ae0a..26647a50fa4 100644
+--- a/grub-core/net/efi/http.c
++++ b/grub-core/net/efi/http.c
+@@ -412,8 +412,8 @@ grub_efihttp_open (struct grub_efi_net_device *dev,
+ 		  int type)
+ {
+   grub_err_t err;
+-  grub_off_t size;
+-  char *buf;
++  grub_off_t size = 0;
++  char *buf = NULL;
+   char *file_name = NULL;
+   const char *http_path;
+ 
+@@ -441,8 +441,11 @@ grub_efihttp_open (struct grub_efi_net_device *dev,
+       return err;
+     }
+ 
+-  buf = grub_malloc (size);
+-  efihttp_read (dev, buf, size);
++  if (size)
++    {
++      buf = grub_malloc (size);
++      efihttp_read (dev, buf, size);
++    }
+ 
+   file->size = size;
+   file->data = buf;
diff --git a/SOURCES/0158-efi-ip-46-_config.c-fix-some-potential-allocation-ov.patch b/SOURCES/0158-efi-ip-46-_config.c-fix-some-potential-allocation-ov.patch
new file mode 100644
index 0000000..30c21a5
--- /dev/null
+++ b/SOURCES/0158-efi-ip-46-_config.c-fix-some-potential-allocation-ov.patch
@@ -0,0 +1,127 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Sun, 19 Jul 2020 17:27:00 -0400
+Subject: [PATCH] efi/ip[46]_config.c: fix some potential allocation overflows
+
+In theory all of this data comes from the firmware stack and it should
+be safe, but it's better to be paranoid.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/net/efi/ip4_config.c | 25 ++++++++++++++++++-------
+ grub-core/net/efi/ip6_config.c | 13 ++++++++++---
+ 2 files changed, 28 insertions(+), 10 deletions(-)
+
+diff --git a/grub-core/net/efi/ip4_config.c b/grub-core/net/efi/ip4_config.c
+index 313c818b184..9725e928f7e 100644
+--- a/grub-core/net/efi/ip4_config.c
++++ b/grub-core/net/efi/ip4_config.c
+@@ -4,15 +4,20 @@
+ #include <grub/misc.h>
+ #include <grub/net/efi.h>
+ #include <grub/charset.h>
++#include <grub/safemath.h>
+ 
+ char *
+ grub_efi_hw_address_to_string (grub_efi_uint32_t hw_address_size, grub_efi_mac_address_t hw_address)
+ {
+   char *hw_addr, *p;
+-  int sz, s;
+-  int i;
++  grub_size_t sz, s, i;
+ 
+-  sz = (int)hw_address_size * (sizeof ("XX:") - 1) + 1;
++  if (grub_mul (hw_address_size, sizeof ("XX:") - 1, &sz) ||
++      grub_add (sz, 1, &sz))
++    {
++      grub_errno = GRUB_ERR_OUT_OF_RANGE;
++      return NULL;
++    }
+ 
+   hw_addr = grub_malloc (sz);
+   if (!hw_addr)
+@@ -20,7 +25,7 @@ grub_efi_hw_address_to_string (grub_efi_uint32_t hw_address_size, grub_efi_mac_a
+ 
+   p = hw_addr;
+   s = sz;
+-  for (i = 0; i < (int)hw_address_size; i++)
++  for (i = 0; i < hw_address_size; i++)
+     {
+       grub_snprintf (p, sz, "%02x:", hw_address[i]);
+       p +=  sizeof ("XX:") - 1;
+@@ -238,14 +243,20 @@ grub_efi_ip4_interface_route_table (struct grub_efi_net_device *dev)
+ {
+   grub_efi_ip4_config2_interface_info_t *interface_info;
+   char **ret;
+-  int i, id;
++  int id;
++  grub_size_t i, nmemb;
+ 
+   interface_info = efi_ip4_config_interface_info (dev->ip4_config);
+   if (!interface_info)
+     return NULL;
+ 
+-  ret = grub_malloc (sizeof (*ret) * (interface_info->route_table_size + 1));
++  if (grub_add (interface_info->route_table_size, 1, &nmemb))
++    {
++      grub_errno = GRUB_ERR_OUT_OF_RANGE;
++      return NULL;
++    }
+ 
++  ret = grub_calloc (nmemb, sizeof (*ret));
+   if (!ret)
+     {
+       grub_free (interface_info);
+@@ -253,7 +264,7 @@ grub_efi_ip4_interface_route_table (struct grub_efi_net_device *dev)
+     }
+ 
+   id = 0;
+-  for (i = 0; i < (int)interface_info->route_table_size; i++)
++  for (i = 0; i < interface_info->route_table_size; i++)
+     {
+       char *subnet, *gateway, *mask;
+       grub_uint32_t u32_subnet, u32_gateway;
+diff --git a/grub-core/net/efi/ip6_config.c b/grub-core/net/efi/ip6_config.c
+index 017c4d05bc7..a46f6f9b685 100644
+--- a/grub-core/net/efi/ip6_config.c
++++ b/grub-core/net/efi/ip6_config.c
+@@ -3,6 +3,7 @@
+ #include <grub/misc.h>
+ #include <grub/net/efi.h>
+ #include <grub/charset.h>
++#include <grub/safemath.h>
+ 
+ char *
+ grub_efi_ip6_address_to_string (grub_efi_pxe_ipv6_address_t *address)
+@@ -228,14 +229,20 @@ grub_efi_ip6_interface_route_table (struct grub_efi_net_device *dev)
+ {
+   grub_efi_ip6_config_interface_info_t *interface_info;
+   char **ret;
+-  int i, id;
++  int id;
++  grub_size_t i, nmemb;
+ 
+   interface_info = efi_ip6_config_interface_info (dev->ip6_config);
+   if (!interface_info)
+     return NULL;
+ 
+-  ret = grub_malloc (sizeof (*ret) * (interface_info->route_count + 1));
++  if (grub_add (interface_info->route_count, 1, &nmemb))
++    {
++      grub_errno = GRUB_ERR_OUT_OF_RANGE;
++      return NULL;
++    }
+ 
++  ret = grub_calloc (nmemb, sizeof (*ret));
+   if (!ret)
+     {
+       grub_free (interface_info);
+@@ -243,7 +250,7 @@ grub_efi_ip6_interface_route_table (struct grub_efi_net_device *dev)
+     }
+ 
+   id = 0;
+-  for (i = 0; i < (int)interface_info->route_count ; i++)
++  for (i = 0; i < interface_info->route_count ; i++)
+     {
+       char *gateway, *destination;
+       grub_uint64_t u64_gateway[2];
diff --git a/SOURCES/0159-efilinux-Fix-integer-overflows-in-grub_cmd_initrd.patch b/SOURCES/0159-efilinux-Fix-integer-overflows-in-grub_cmd_initrd.patch
new file mode 100644
index 0000000..95f7e20
--- /dev/null
+++ b/SOURCES/0159-efilinux-Fix-integer-overflows-in-grub_cmd_initrd.patch
@@ -0,0 +1,49 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Colin Watson <cjwatson@debian.org>
+Date: Fri, 24 Jul 2020 17:18:09 +0100
+Subject: [PATCH] efilinux: Fix integer overflows in grub_cmd_initrd
+
+These could be triggered by an extremely large number of arguments to
+the initrd command on 32-bit architectures, or a crafted filesystem with
+very large files on any architecture.
+
+Signed-off-by: Colin Watson <cjwatson@debian.org>
+---
+ grub-core/loader/i386/efi/linux.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index 15d40d6e35b..f992ceeef20 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -28,6 +28,8 @@
+ #include <grub/efi/efi.h>
+ #include <grub/efi/linux.h>
+ #include <grub/cpu/efi/memory.h>
++#include <grub/tpm.h>
++#include <grub/safemath.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -206,7 +208,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+       goto fail;
+     }
+ 
+-  files = grub_zalloc (argc * sizeof (files[0]));
++  files = grub_calloc (argc, sizeof (files[0]));
+   if (!files)
+     goto fail;
+ 
+@@ -216,7 +218,11 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+       if (! files[i])
+         goto fail;
+       nfiles++;
+-      size += ALIGN_UP (grub_file_size (files[i]), 4);
++      if (grub_add (size, ALIGN_UP (grub_file_size (files[i]), 4), &size))
++	{
++	  grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
++	  goto fail;
++	}
+     }
+ 
+   initrd_mem = kernel_alloc(size, N_("can't allocate initrd"));
diff --git a/SOURCES/0160-linuxefi-fail-kernel-validation-without-shim-protoco.patch b/SOURCES/0160-linuxefi-fail-kernel-validation-without-shim-protoco.patch
new file mode 100644
index 0000000..20fc786
--- /dev/null
+++ b/SOURCES/0160-linuxefi-fail-kernel-validation-without-shim-protoco.patch
@@ -0,0 +1,130 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Dimitri John Ledkov <xnox@ubuntu.com>
+Date: Wed, 22 Jul 2020 11:31:43 +0100
+Subject: [PATCH] linuxefi: fail kernel validation without shim protocol.
+
+If certificates that signed grub are installed into db, grub can be
+booted directly. It will then boot any kernel without signature
+validation. The booted kernel will think it was booted in secureboot
+mode and will implement lockdown, yet it could have been tampered.
+
+This version of the patch skips calling verification, when booted
+without secureboot. And is indented with gnu ident.
+
+CVE-2020-15705
+
+Reported-by: Mathieu Trudel-Lapierre <cyphermox@ubuntu.com>
+Signed-off-by: Dimitri John Ledkov <xnox@ubuntu.com>
+---
+ grub-core/loader/arm64/linux.c     | 13 +++++++++----
+ grub-core/loader/efi/chainloader.c |  1 +
+ grub-core/loader/efi/linux.c       |  1 +
+ grub-core/loader/i386/efi/linux.c  | 17 +++++++++++------
+ 4 files changed, 22 insertions(+), 10 deletions(-)
+
+diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
+index 70a0075ec5e..47f8cf0d84b 100644
+--- a/grub-core/loader/arm64/linux.c
++++ b/grub-core/loader/arm64/linux.c
+@@ -34,6 +34,7 @@
+ #include <grub/i18n.h>
+ #include <grub/lib/cmdline.h>
+ #include <grub/verify.h>
++#include <grub/efi/sb.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -363,11 +364,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 
+   grub_dprintf ("linux", "kernel @ %p\n", kernel_addr);
+ 
+-  rc = grub_linuxefi_secure_validate (kernel_addr, kernel_size);
+-  if (rc < 0)
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
+     {
+-      grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), argv[0]);
+-      goto fail;
++      rc = grub_linuxefi_secure_validate (kernel_addr, kernel_size);
++      if (rc <= 0)
++	{
++	  grub_error (GRUB_ERR_INVALID_COMMAND,
++		      N_("%s has invalid signature"), argv[0]);
++	  goto fail;
++	}
+     }
+ 
+   pe = (void *)((unsigned long)kernel_addr + lh.hdr_offset);
+diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
+index ac8dfd40c61..d41e8ea14a8 100644
+--- a/grub-core/loader/efi/chainloader.c
++++ b/grub-core/loader/efi/chainloader.c
+@@ -1084,6 +1084,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+ 
+       return 0;
+     }
++  // -1 fall-through to fail
+ 
+ fail:
+   if (dev)
+diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c
+index e8b9ecb17f6..9260731c107 100644
+--- a/grub-core/loader/efi/linux.c
++++ b/grub-core/loader/efi/linux.c
+@@ -33,6 +33,7 @@ struct grub_efi_shim_lock
+ };
+ typedef struct grub_efi_shim_lock grub_efi_shim_lock_t;
+ 
++// Returns 1 on success, -1 on error, 0 when not available
+ int
+ grub_linuxefi_secure_validate (void *data, grub_uint32_t size)
+ {
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index f992ceeef20..3cf0f9b330b 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -30,6 +30,7 @@
+ #include <grub/cpu/efi/memory.h>
+ #include <grub/tpm.h>
+ #include <grub/safemath.h>
++#include <grub/efi/sb.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -101,7 +102,7 @@ kernel_alloc(grub_efi_uintn_t size, const char * const errmsg)
+ 
+       pages = BYTES_TO_PAGES(size);
+       grub_dprintf ("linux", "Trying to allocate %lu pages from %p\n",
+-		    pages, (void *)max);
++		    (unsigned long)pages, (void *)(unsigned long)max);
+ 
+       prev_max = max;
+       addr = grub_efi_allocate_pages_real (max, pages,
+@@ -307,12 +308,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+       goto fail;
+     }
+ 
+-  rc = grub_linuxefi_secure_validate (kernel, filelen);
+-  if (rc < 0)
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
+     {
+-      grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"),
+-		  argv[0]);
+-      goto fail;
++      rc = grub_linuxefi_secure_validate (kernel, filelen);
++      if (rc <= 0)
++	{
++	  grub_error (GRUB_ERR_INVALID_COMMAND,
++		      N_("%s has invalid signature"), argv[0]);
++	  goto fail;
++	}
+     }
+ 
+   lh = (struct linux_i386_kernel_header *)kernel;
+@@ -386,6 +390,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 
+   setup_header_end_offset = *((grub_uint8_t *)kernel + 0x201);
+   grub_dprintf ("linux", "copying %lu bytes from %p to %p\n",
++		(unsigned long)
+ 		MIN((grub_size_t)0x202+setup_header_end_offset,
+ 		    sizeof (*params)) - 0x1f1,
+ 		(grub_uint8_t *)kernel + 0x1f1,
diff --git a/SOURCES/0161-Fix-const-char-pointers-in-grub-core-net-bootp.c.patch b/SOURCES/0161-Fix-const-char-pointers-in-grub-core-net-bootp.c.patch
new file mode 100644
index 0000000..9b0db5f
--- /dev/null
+++ b/SOURCES/0161-Fix-const-char-pointers-in-grub-core-net-bootp.c.patch
@@ -0,0 +1,37 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 20 Jul 2020 12:24:02 -0400
+Subject: [PATCH] Fix const char ** pointers in grub-core/net/bootp.c
+
+This will need to get folded back in the right place on the next rebase,
+but it's before "Make grub_strtol() "end" pointers have safer const
+qualifiers" currently, so for now I'm leaving it here instead of merging
+it back with the original patch.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/net/bootp.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c
+index 8fb8918ae7e..7baf3540c81 100644
+--- a/grub-core/net/bootp.c
++++ b/grub-core/net/bootp.c
+@@ -329,7 +329,7 @@ grub_net_configure_by_dhcp_ack (const char *name,
+   struct grub_net_network_level_interface *inter;
+   int mask = -1;
+   char server_ip[sizeof ("xxx.xxx.xxx.xxx")];
+-  const grub_uint8_t *opt;
++  const char *opt;
+   grub_uint8_t opt_len, overload = 0;
+   const char *boot_file = 0, *server_name = 0;
+   grub_size_t boot_file_len, server_name_len;
+@@ -505,7 +505,7 @@ grub_net_configure_by_dhcp_ack (const char *name,
+   if (opt && opt_len)
+     {
+       grub_env_set_net_property (name, "vendor_class_identifier", (const char *) opt, opt_len);
+-      if (opt && grub_strcmp (opt, "HTTPClient") == 0)
++      if (opt && grub_strcmp ((char *)opt, "HTTPClient") == 0)
+         {
+           char *proto, *ip, *pa;
+ 
diff --git a/SOURCES/0162-Fix-const-char-pointers-in-grub-core-net-efi-ip4_con.patch b/SOURCES/0162-Fix-const-char-pointers-in-grub-core-net-efi-ip4_con.patch
new file mode 100644
index 0000000..6c16e9e
--- /dev/null
+++ b/SOURCES/0162-Fix-const-char-pointers-in-grub-core-net-efi-ip4_con.patch
@@ -0,0 +1,38 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 20 Jul 2020 12:24:02 -0400
+Subject: [PATCH] Fix const char ** pointers in grub-core/net/efi/ip4_config.c
+
+This will need to get folded back in the right place on the next rebase,
+but it's before "Make grub_strtol() "end" pointers have safer const
+qualifiers" currently, so for now I'm leaving it here instead of merging
+it back with the original patch.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/net/efi/ip4_config.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/net/efi/ip4_config.c b/grub-core/net/efi/ip4_config.c
+index 9725e928f7e..cb880fc3e8f 100644
+--- a/grub-core/net/efi/ip4_config.c
++++ b/grub-core/net/efi/ip4_config.c
+@@ -61,7 +61,8 @@ int
+ grub_efi_string_to_ip4_address (const char *val, grub_efi_ipv4_address_t *address, const char **rest)
+ {
+   grub_uint32_t newip = 0;
+-  int i, ncolon = 0;
++  grub_size_t i;
++  int ncolon = 0;
+   const char *ptr = val;
+ 
+   /* Check that is not an IPv6 address */
+@@ -78,7 +79,7 @@ grub_efi_string_to_ip4_address (const char *val, grub_efi_ipv4_address_t *addres
+   for (i = 0; i < 4; i++)
+     {
+       unsigned long t;
+-      t = grub_strtoul (ptr, (char **) &ptr, 0);
++      t = grub_strtoul (ptr, &ptr, 0);
+       if (grub_errno)
+ 	{
+ 	  grub_errno = GRUB_ERR_NONE;
diff --git a/SOURCES/0163-Fix-const-char-pointers-in-grub-core-net-efi-ip6_con.patch b/SOURCES/0163-Fix-const-char-pointers-in-grub-core-net-efi-ip6_con.patch
new file mode 100644
index 0000000..7c29683
--- /dev/null
+++ b/SOURCES/0163-Fix-const-char-pointers-in-grub-core-net-efi-ip6_con.patch
@@ -0,0 +1,28 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 20 Jul 2020 12:24:02 -0400
+Subject: [PATCH] Fix const char ** pointers in grub-core/net/efi/ip6_config.c
+
+This will need to get folded back in the right place on the next rebase,
+but it's before "Make grub_strtol() "end" pointers have safer const
+qualifiers" currently, so for now I'm leaving it here instead of merging
+it back with the original patch.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/net/efi/ip6_config.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/net/efi/ip6_config.c b/grub-core/net/efi/ip6_config.c
+index a46f6f9b685..1c5415d7185 100644
+--- a/grub-core/net/efi/ip6_config.c
++++ b/grub-core/net/efi/ip6_config.c
+@@ -85,7 +85,7 @@ grub_efi_string_to_ip6_address (const char *val, grub_efi_ipv6_address_t *addres
+ 	  ptr++;
+ 	  continue;
+ 	}
+-      t = grub_strtoul (ptr, (char **) &ptr, 16);
++      t = grub_strtoul (ptr, &ptr, 16);
+       if (grub_errno)
+ 	{
+ 	  grub_errno = GRUB_ERR_NONE;
diff --git a/SOURCES/0164-Fix-const-char-pointers-in-grub-core-net-efi-net.c.patch b/SOURCES/0164-Fix-const-char-pointers-in-grub-core-net-efi-net.c.patch
new file mode 100644
index 0000000..0fe90c8
--- /dev/null
+++ b/SOURCES/0164-Fix-const-char-pointers-in-grub-core-net-efi-net.c.patch
@@ -0,0 +1,37 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 20 Jul 2020 12:24:02 -0400
+Subject: [PATCH] Fix const char ** pointers in grub-core/net/efi/net.c
+
+This will need to get folded back in the right place on the next rebase,
+but it's before "Make grub_strtol() "end" pointers have safer const
+qualifiers" currently, so for now I'm leaving it here instead of merging
+it back with the original patch.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/net/efi/net.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/net/efi/net.c b/grub-core/net/efi/net.c
+index a3f0535d43c..78e5442fc52 100644
+--- a/grub-core/net/efi/net.c
++++ b/grub-core/net/efi/net.c
+@@ -729,7 +729,7 @@ grub_efi_net_parse_address (const char *address,
+ 	{
+ 	  grub_uint32_t subnet_mask_size;
+ 
+-	  subnet_mask_size = grub_strtoul (rest + 1, (char **) &rest, 0);
++	  subnet_mask_size = grub_strtoul (rest + 1, &rest, 0);
+ 
+ 	  if (!grub_errno && subnet_mask_size <= 32 && *rest == 0)
+ 	    {
+@@ -758,7 +758,7 @@ grub_efi_net_parse_address (const char *address,
+ 	{
+ 	  grub_efi_uint8_t prefix_length;
+ 
+-	  prefix_length = grub_strtoul (rest + 1, (char **) &rest, 0);
++	  prefix_length = grub_strtoul (rest + 1, &rest, 0);
+ 	  if (!grub_errno && prefix_length <= 128 && *rest == 0)
+ 	    {
+ 	      ip6->prefix_length = prefix_length;
diff --git a/SOURCES/0165-Fix-const-char-pointers-in-grub-core-net-efi-pxe.c.patch b/SOURCES/0165-Fix-const-char-pointers-in-grub-core-net-efi-pxe.c.patch
new file mode 100644
index 0000000..59f29e4
--- /dev/null
+++ b/SOURCES/0165-Fix-const-char-pointers-in-grub-core-net-efi-pxe.c.patch
@@ -0,0 +1,46 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 20 Jul 2020 12:24:02 -0400
+Subject: [PATCH] Fix const char ** pointers in grub-core/net/efi/pxe.c
+
+This will need to get folded back in the right place on the next rebase,
+but it's before "Make grub_strtol() "end" pointers have safer const
+qualifiers" currently, so for now I'm leaving it here instead of merging
+it back with the original patch.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/net/efi/pxe.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/net/efi/pxe.c b/grub-core/net/efi/pxe.c
+index 531949cba5c..73e2bb01c1b 100644
+--- a/grub-core/net/efi/pxe.c
++++ b/grub-core/net/efi/pxe.c
+@@ -187,7 +187,7 @@ parse_ip6 (const char *val, grub_uint64_t *ip, const char **rest)
+ 	  ptr++;
+ 	  continue;
+ 	}
+-      t = grub_strtoul (ptr, (char **) &ptr, 16);
++      t = grub_strtoul (ptr, &ptr, 16);
+       if (grub_errno)
+ 	{
+ 	  grub_errno = GRUB_ERR_NONE;
+@@ -225,7 +225,7 @@ pxe_open (struct grub_efi_net_device *dev,
+ 	  int type __attribute__((unused)))
+ {
+   int i;
+-  char *p;
++  const char *p;
+   grub_efi_status_t status;
+   grub_efi_pxe_ip_address_t server_ip;
+   grub_efi_uint64_t file_size = 0;
+@@ -313,7 +313,7 @@ pxe_read (struct grub_efi_net_device *dev,
+ 	  grub_size_t len)
+ {
+   int i;
+-  char *p;
++  const char *p;
+   grub_efi_status_t status;
+   grub_efi_pxe_t *pxe = (prefer_ip6) ? dev->ip6_pxe : dev->ip4_pxe;
+   grub_efi_uint64_t bufsz = len;
diff --git a/SOURCES/0166-Add-systemd-integration-scripts-to-make-systemctl-re.patch b/SOURCES/0166-Add-systemd-integration-scripts-to-make-systemctl-re.patch
new file mode 100644
index 0000000..130dc77
--- /dev/null
+++ b/SOURCES/0166-Add-systemd-integration-scripts-to-make-systemctl-re.patch
@@ -0,0 +1,190 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Wed, 22 Jul 2020 14:03:42 +0200
+Subject: [PATCH] Add systemd integration scripts to make "systemctl reboot
+ --boot-loader-menu=xxx" work with grub
+
+This commit adds a number of scripts / config files to make
+"systemctl reboot --boot-loader-menu=xxx" work with grub:
+
+1. /lib/systemd/system/systemd-logind.service.d/10-grub.conf
+This sets SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU in the env. for logind,
+indicating that the boot-loader which is used supports this feature, see:
+https://github.com/systemd/systemd/blob/master/docs/ENVIRONMENT.md
+
+2. /lib/systemd/system/grub-systemd-integration.service
+   /lib/systemd/system/reboot.target.wants/grub-systemd-integration.service ->
+     ../grub-systemd-integration.service
+   /usr/libexec/grub/grub-systemd-integration.sh
+
+The symlink in the .wants dir causes the added service file to be started
+by systemd just before rebooting the system.
+If /run/systemd/reboot-to-boot-loader-menu exist then the service will run
+the grub-systemd-integration.sh script.
+This script sets the new menu_show_once_timeout grubenv variable to the
+requested timeout in seconds.
+
+3. /etc/grub.d/14_menu_show_once
+
+This new grub-mkconfig snippet adds the necessary code to the generated
+grub.conf to honor the new menu_show_once_timeout variable, and to
+automatically clear it after consuming it.
+
+Note the service and libexec script use grub-systemd-integration as name
+because in the future they may be used to add further integration with
+systemctl reboot --foo options, e.g. support for --boot-loader-entry=NAME.
+
+A few notes about upstreaming this patch from the rhboot grub2 fork:
+1. I have deliberately put the grub.conf bits for this in a new / separate
+   grub-mkconfig snippet generator for easy upstreaming
+2. Even though the commit message mentions the .wants symlink for the .service
+   I have been unable to come up with a clean way to do this at "make install"
+   time, this should be fixed before upstreaming.
+
+Downstream notes:
+1. Since make install does not add the .wants symlink, this needs to be done
+   in grub2.spec %install
+2. This is keeping support for the "old" Fedora specific menu_show_once env
+   variable, which has a hardcoded timeout of 60 sec in 12_menu_auto_hide in
+   place for now. This can be dropped (eventually) in a follow-up patch once
+   GNOME has been converted to use the systemd dbus API equivalent of
+   "systemctl reboot --boot-loader-menu=xxx".
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ Makefile.util.def                                | 27 ++++++++++++++++++++++++
+ conf/Makefile.common                             |  6 ++++++
+ util/grub.d/14_menu_show_once.in                 | 13 ++++++++++++
+ util/systemd/10-grub-logind-service.conf.in      |  2 ++
+ util/systemd/grub-systemd-integration.service.in |  8 +++++++
+ util/systemd/systemd-integration.sh.in           |  6 ++++++
+ 6 files changed, 62 insertions(+)
+ create mode 100755 util/grub.d/14_menu_show_once.in
+ create mode 100644 util/systemd/10-grub-logind-service.conf.in
+ create mode 100644 util/systemd/grub-systemd-integration.service.in
+ create mode 100644 util/systemd/systemd-integration.sh.in
+
+diff --git a/Makefile.util.def b/Makefile.util.def
+index 11ab2d6fad1..e1242f54022 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -470,6 +470,12 @@ script = {
+   installdir = grubconf;
+ };
+ 
++script = {
++  name = '14_menu_show_once';
++  common = util/grub.d/14_menu_show_once.in;
++  installdir = grubconf;
++};
++
+ script = {
+   name = '01_users';
+   common = util/grub.d/01_users.in;
+@@ -569,6 +575,27 @@ script = {
+   installdir = grubconf;
+ };
+ 
++script = {
++  name = 'grub-systemd-integration.service';
++  common = util/systemd/grub-systemd-integration.service.in;
++  installdir = systemdunit;
++  condition = COND_HOST_LINUX;
++};
++
++script = {
++  name = 'systemd-integration.sh';
++  common = util/systemd/systemd-integration.sh.in;
++  installdir = grublibexec;
++  condition = COND_HOST_LINUX;
++};
++
++script = {
++  name = '10-grub-logind-service.conf';
++  common = util/systemd/10-grub-logind-service.conf.in;
++  installdir = systemd_logind_service_d;
++  condition = COND_HOST_LINUX;
++};
++
+ program = {
+   mansection = 1;
+   name = grub-mkrescue;
+diff --git a/conf/Makefile.common b/conf/Makefile.common
+index 0647c53b916..9fe5863b2d9 100644
+--- a/conf/Makefile.common
++++ b/conf/Makefile.common
+@@ -63,8 +63,11 @@ CCASFLAGS_LIBRARY = $(UTILS_CCASFLAGS)
+ # Other variables
+ 
+ grubconfdir = $(sysconfdir)/grub.d
++grublibexecdir = $(libexecdir)/$(grubdirname)
+ platformdir = $(pkglibdir)/$(target_cpu)-$(platform)
+ starfielddir = $(pkgdatadir)/themes/starfield
++systemdunitdir = ${prefix}/lib/systemd/system
++systemd_logind_service_ddir = $(systemdunitdir)/systemd-logind.service.d
+ 
+ CFLAGS_GNULIB = -Wno-undef -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code -Werror=trampolines -fno-trampolines
+ CPPFLAGS_GNULIB = -I$(top_builddir)/grub-core/lib/gnulib -I$(top_srcdir)/grub-core/lib/gnulib
+@@ -121,6 +124,9 @@ noinst_LIBRARIES =
+ dist_noinst_DATA =
+ platform_SCRIPTS =
+ platform_PROGRAMS =
++grublibexec_SCRIPTS =
++systemdunit_SCRIPTS =
++systemd_logind_service_d_SCRIPTS =
+ 
+ TESTS =
+ EXTRA_DIST =
+diff --git a/util/grub.d/14_menu_show_once.in b/util/grub.d/14_menu_show_once.in
+new file mode 100755
+index 00000000000..1cd7f36142b
+--- /dev/null
++++ b/util/grub.d/14_menu_show_once.in
+@@ -0,0 +1,13 @@
++#! /bin/sh
++# Force the menu to be shown once, with a timeout of ${menu_show_once_timeout}
++# if requested by ${menu_show_once_timeout} being set in the env.
++cat << EOF
++if [ x\$feature_timeout_style = xy ]; then
++  if [ "\${menu_show_once_timeout}" ]; then
++    set timeout_style=menu
++    set timeout="\${menu_show_once_timeout}"
++    unset menu_show_once_timeout
++    save_env menu_show_once_timeout
++  fi
++fi
++EOF
+diff --git a/util/systemd/10-grub-logind-service.conf.in b/util/systemd/10-grub-logind-service.conf.in
+new file mode 100644
+index 00000000000..f2d4ac00732
+--- /dev/null
++++ b/util/systemd/10-grub-logind-service.conf.in
+@@ -0,0 +1,2 @@
++[Service]
++Environment=SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU=true
+diff --git a/util/systemd/grub-systemd-integration.service.in b/util/systemd/grub-systemd-integration.service.in
+new file mode 100644
+index 00000000000..c81fb594ce1
+--- /dev/null
++++ b/util/systemd/grub-systemd-integration.service.in
+@@ -0,0 +1,8 @@
++[Unit]
++Description=Grub2 systemctl reboot --boot-loader-menu=... support
++Before=umount.target systemd-reboot.service
++DefaultDependencies=no
++ConditionPathExists=/run/systemd/reboot-to-boot-loader-menu
++
++[Service]
++ExecStart=@libexecdir@/@grubdirname@/systemd-integration.sh
+diff --git a/util/systemd/systemd-integration.sh.in b/util/systemd/systemd-integration.sh.in
+new file mode 100644
+index 00000000000..dc1218597bc
+--- /dev/null
++++ b/util/systemd/systemd-integration.sh.in
+@@ -0,0 +1,6 @@
++#!/bin/sh
++
++TIMEOUT_USEC=$(cat /run/systemd/reboot-to-boot-loader-menu)
++TIMEOUT=$(((TIMEOUT_USEC + 500000) / 1000000))
++
++@grub_editenv@ - set menu_show_once_timeout=$TIMEOUT
diff --git a/SOURCES/0167-systemd-integration.sh-Also-set-old-menu_show_once-g.patch b/SOURCES/0167-systemd-integration.sh-Also-set-old-menu_show_once-g.patch
new file mode 100644
index 0000000..9021de5
--- /dev/null
+++ b/SOURCES/0167-systemd-integration.sh-Also-set-old-menu_show_once-g.patch
@@ -0,0 +1,32 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Thu, 23 Jul 2020 09:27:36 +0200
+Subject: [PATCH] systemd-integration.sh: Also set old menu_show_once grubenv
+ var
+
+Downstream RH / Fedora patch for compatibility with old, not (yet)
+regenerated grub.cfg files which miss the menu_show_once_timeout check.
+This older grubenv variable leads to a fixed timeout of 60 seconds.
+
+Note that the new menu_show_once_timeout will overrule these 60 seconds
+if both are set and the grub.cfg does have the menu_show_once_timeout
+check.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ util/systemd/systemd-integration.sh.in | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/util/systemd/systemd-integration.sh.in b/util/systemd/systemd-integration.sh.in
+index dc1218597bc..a4c071c5b0c 100644
+--- a/util/systemd/systemd-integration.sh.in
++++ b/util/systemd/systemd-integration.sh.in
+@@ -4,3 +4,8 @@ TIMEOUT_USEC=$(cat /run/systemd/reboot-to-boot-loader-menu)
+ TIMEOUT=$(((TIMEOUT_USEC + 500000) / 1000000))
+ 
+ @grub_editenv@ - set menu_show_once_timeout=$TIMEOUT
++
++# Downstream RH / Fedora patch for compatibility with old, not (yet)
++# regenerated grub.cfg files which miss the menu_show_once_timeout check
++# this older grubenv variable leads to a fixed timeout of 60 seconds
++@grub_editenv@ - set menu_show_once=1
diff --git a/SOURCES/0168-at_keyboard-use-set-1-when-keyboard-is-in-Translate-.patch b/SOURCES/0168-at_keyboard-use-set-1-when-keyboard-is-in-Translate-.patch
new file mode 100644
index 0000000..c338840
--- /dev/null
+++ b/SOURCES/0168-at_keyboard-use-set-1-when-keyboard-is-in-Translate-.patch
@@ -0,0 +1,121 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
+Date: Thu, 3 Dec 2020 09:13:24 +0100
+Subject: [PATCH] at_keyboard: use set 1 when keyboard is in Translate mode
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+When keyboard controller acts in Translate mode (0x40 mask), then use
+set 1 since translation is done.
+Otherwise use the mode queried from the controller (usually set 2).
+
+Added "atkeyb" debugging messages in at_keyboard module as well.
+
+Resolves: rhbz#1897587
+
+Tested on:
+- Asus N53SN (set 1 used)
+- Dell Precision (set 1 used)
+- HP Elitebook (set 2 used)
+- HP G5430 (set 1 used, keyboard in XT mode!)
+- Lenovo P71 & Lenovo T460s (set 2 used)
+- QEMU/KVM (set 1 used)
+
+Signed-off-by: Renaud Métrich <rmetrich@redhat.com>
+---
+ grub-core/term/at_keyboard.c | 29 ++++++++++++++++++++++++-----
+ include/grub/at_keyboard.h   |  4 ++++
+ 2 files changed, 28 insertions(+), 5 deletions(-)
+
+diff --git a/grub-core/term/at_keyboard.c b/grub-core/term/at_keyboard.c
+index 597111077bd..26014382608 100644
+--- a/grub-core/term/at_keyboard.c
++++ b/grub-core/term/at_keyboard.c
+@@ -135,20 +135,28 @@ query_mode (void)
+   int e;
+ 
+   e = write_mode (0);
+-  if (!e)
++  if (!e) {
++    grub_dprintf("atkeyb", "query_mode: write_mode(0) failed\n");
+     return 0;
++  }
+ 
+   do {
+     keyboard_controller_wait_until_ready ();
+     ret = grub_inb (KEYBOARD_REG_DATA);
+   } while (ret == GRUB_AT_ACK);
+   /* QEMU translates the set even in no-translate mode.  */
+-  if (ret == 0x43 || ret == 1)
++  if (ret == 0x43 || ret == 1) {
++    grub_dprintf("atkeyb", "query_mode: returning 1 (ret=0x%x)\n", ret);
+     return 1;
+-  if (ret == 0x41 || ret == 2)
++  }
++  if (ret == 0x41 || ret == 2) {
++    grub_dprintf("atkeyb", "query_mode: returning 2 (ret=0x%x)\n", ret);
+     return 2;
+-  if (ret == 0x3f || ret == 3)
++  }
++  if (ret == 0x3f || ret == 3) {
++    grub_dprintf("atkeyb", "query_mode: returning 3 (ret=0x%x)\n", ret);
+     return 3;
++  }
+   return 0;
+ }
+ 
+@@ -165,7 +173,13 @@ set_scancodes (void)
+     }
+ 
+ #if !USE_SCANCODE_SET
+-  ps2_state.current_set = 1;
++  if ((grub_keyboard_controller_orig & KEYBOARD_AT_TRANSLATE) == KEYBOARD_AT_TRANSLATE) {
++    grub_dprintf ("atkeyb", "queried set is %d but keyboard in Translate mode, so actually in set 1\n", grub_keyboard_orig_set);
++    ps2_state.current_set = 1;
++  } else {
++    grub_dprintf ("atkeyb", "using queried set %d\n", grub_keyboard_orig_set);
++    ps2_state.current_set = grub_keyboard_orig_set;
++  }
+   return;
+ #else
+ 
+@@ -266,6 +280,7 @@ grub_keyboard_controller_init (void)
+   grub_keyboard_orig_set = 2;
+ #else
+   grub_keyboard_controller_orig = grub_keyboard_controller_read ();
++  grub_dprintf ("atkeyb", "grub_keyboard_controller_orig = 0x%x\n", grub_keyboard_controller_orig);
+   grub_keyboard_orig_set = query_mode ();
+ #endif
+   set_scancodes ();
+@@ -275,11 +290,15 @@ grub_keyboard_controller_init (void)
+ static grub_err_t
+ grub_keyboard_controller_fini (struct grub_term_input *term __attribute__ ((unused)))
+ {
++/* In !USE_SCANCODE_SET mode, we didn't change anything, so nothing to restore */
++#if USE_SCANCODE_SET
+   if (ps2_state.current_set == 0)
+     return GRUB_ERR_NONE;
++  grub_dprintf ("atkeyb", "restoring set %d, controller 0x%x\n", grub_keyboard_orig_set, grub_keyboard_controller_orig);
+   if (grub_keyboard_orig_set)
+     write_mode (grub_keyboard_orig_set);
+   grub_keyboard_controller_write (grub_keyboard_controller_orig);
++#endif
+   return GRUB_ERR_NONE;
+ }
+ 
+diff --git a/include/grub/at_keyboard.h b/include/grub/at_keyboard.h
+index bcb4d9ba78f..9414dc1b996 100644
+--- a/include/grub/at_keyboard.h
++++ b/include/grub/at_keyboard.h
+@@ -19,6 +19,10 @@
+ #ifndef GRUB_AT_KEYBOARD_HEADER
+ #define GRUB_AT_KEYBOARD_HEADER	1
+ 
++/*
++ * Refer to https://wiki.osdev.org/%228042%22_PS/2_Controller for details.
++ */
++
+ /* Used for sending commands to the controller.  */
+ #define KEYBOARD_COMMAND_ISREADY(x)	!((x) & 0x02)
+ #define KEYBOARD_COMMAND_READ		0x20
diff --git a/SOURCES/0169-grub-install-disable-support-for-EFI-platforms.patch b/SOURCES/0169-grub-install-disable-support-for-EFI-platforms.patch
new file mode 100644
index 0000000..41003e2
--- /dev/null
+++ b/SOURCES/0169-grub-install-disable-support-for-EFI-platforms.patch
@@ -0,0 +1,120 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jan Hlavac <jhlavac@redhat.com>
+Date: Fri, 20 Nov 2020 23:51:47 +0100
+Subject: [PATCH] grub-install: disable support for EFI platforms
+
+For each platform, GRUB is shipped as a kernel image and a set of
+modules. These files are then used by the grub-install utility to
+install GRUB on a specific device. However, in order to support UEFI
+Secure Boot, the resulting EFI binary must be signed by a recognized
+private key. For this reason, for EFI platforms, most distributions also
+ship prebuilt EFI binaries signed by a distribution-specific private
+key. In this case, however, the grub-install utility should not be used
+because it would overwrite the signed EFI binary.
+
+The current fix is suboptimal because it preserves all EFI-related code.
+A better solution could be to modularize the code and provide a
+build-time option.
+
+Resolves: rhbz#1737444
+
+Signed-off-by: Jan Hlavac <jhlavac@redhat.com>
+---
+ util/grub-install.c | 37 ++++++++++++++++---------------------
+ docs/grub.texi      |  7 +++++++
+ util/grub-install.8 |  4 +++-
+ 3 files changed, 26 insertions(+), 22 deletions(-)
+
+diff --git a/util/grub-install.c b/util/grub-install.c
+index a2bec7446cb..5babc7af551 100644
+--- a/util/grub-install.c
++++ b/util/grub-install.c
+@@ -899,6 +899,22 @@ main (int argc, char *argv[])
+ 
+   platform = grub_install_get_target (grub_install_source_directory);
+ 
++  switch (platform)
++    {
++    case GRUB_INSTALL_PLATFORM_ARM_EFI:
++    case GRUB_INSTALL_PLATFORM_ARM64_EFI:
++    case GRUB_INSTALL_PLATFORM_I386_EFI:
++    case GRUB_INSTALL_PLATFORM_IA64_EFI:
++    case GRUB_INSTALL_PLATFORM_X86_64_EFI:
++      is_efi = 1;
++      grub_util_error (_("this utility cannot be used for EFI platforms"
++                         " because it does not support UEFI Secure Boot"));
++      break;
++    default:
++      is_efi = 0;
++      break;
++    }
++
+   {
+     char *platname = grub_install_get_platform_name (platform);
+     fprintf (stderr, _("Installing for %s platform.\n"), platname);
+@@ -1011,28 +1027,7 @@ main (int argc, char *argv[])
+   grub_hostfs_init ();
+   grub_host_init ();
+ 
+-  switch (platform)
+-    {
+-    case GRUB_INSTALL_PLATFORM_I386_EFI:
+-    case GRUB_INSTALL_PLATFORM_X86_64_EFI:
+-    case GRUB_INSTALL_PLATFORM_ARM_EFI:
+-    case GRUB_INSTALL_PLATFORM_ARM64_EFI:
+-    case GRUB_INSTALL_PLATFORM_RISCV32_EFI:
+-    case GRUB_INSTALL_PLATFORM_RISCV64_EFI:
+-    case GRUB_INSTALL_PLATFORM_IA64_EFI:
+-      is_efi = 1;
+-      break;
+-    default:
+-      is_efi = 0;
+-      break;
+-
+-      /* pacify warning.  */
+-    case GRUB_INSTALL_PLATFORM_MAX:
+-      break;
+-    }
+-
+   /* Find the EFI System Partition.  */
+-
+   if (is_efi)
+     {
+       grub_fs_t fs;
+diff --git a/docs/grub.texi b/docs/grub.texi
+index 04ed6ac1f07..4870faaa00a 100644
+--- a/docs/grub.texi
++++ b/docs/grub.texi
+@@ -6509,6 +6509,13 @@ grub2-install @var{install_device}
+ The device name @var{install_device} is an OS device name or a GRUB
+ device name.
+ 
++In order to support UEFI Secure Boot, the resulting GRUB EFI binary must
++be signed by a recognized private key. For this reason, for EFI
++platforms, most distributions also ship prebuilt GRUB EFI binaries
++signed by a distribution-specific private key. In this case, however,
++@command{grub2-install} should not be used because it would overwrite
++the signed EFI binary.
++
+ @command{grub2-install} accepts the following options:
+ 
+ @table @option
+diff --git a/util/grub-install.8 b/util/grub-install.8
+index 1db89e94b3b..811d441b16c 100644
+--- a/util/grub-install.8
++++ b/util/grub-install.8
+@@ -1,4 +1,4 @@
+-.TH GRUB-INSTALL 1 "Wed Feb 26 2014"
++.TH GRUB-INSTALL 1 "Fri Nov 20 2020"
+ .SH NAME
+ \fBgrub-install\fR \(em Install GRUB on a device.
+ 
+@@ -31,6 +31,8 @@
+ .SH DESCRIPTION
+ \fBgrub-install\fR installs GRUB onto a device.  This includes copying GRUB images into the target directory (generally \fI/boot/grub\fR), and on some platforms may also include installing GRUB onto a boot sector.
+ 
++In order to support UEFI Secure Boot, the resulting GRUB EFI binary must be signed by a recognized private key. For this reason, for EFI platforms, most distributions also ship prebuilt GRUB EFI binaries signed by a distribution-specific private key. In this case, however, the \fBgrub-install\fR utility should not be used because it would overwrite the signed EFI binary.
++
+ .SH OPTIONS
+ .TP
+ \fB--modules\fR=\fIMODULES\fR\!
diff --git a/SOURCES/0170-New-with-debug-timestamps-configure-flag-to-prepend-.patch b/SOURCES/0170-New-with-debug-timestamps-configure-flag-to-prepend-.patch
new file mode 100644
index 0000000..3eb0d00
--- /dev/null
+++ b/SOURCES/0170-New-with-debug-timestamps-configure-flag-to-prepend-.patch
@@ -0,0 +1,112 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
+Date: Sat, 23 Nov 2019 14:57:41 +0100
+Subject: [PATCH] New --with-debug-timestamps configure flag to prepend debug
+ traces with absolute and relative timestamp
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Renaud Métrich <rmetrich@redhat.com>
+---
+ configure.ac          | 18 ++++++++++++++++++
+ grub-core/kern/misc.c | 20 ++++++++++++++++++++
+ config.h.in           |  1 +
+ 3 files changed, 39 insertions(+)
+
+diff --git a/configure.ac b/configure.ac
+index 907477a585c..d5d2a28b4ef 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1613,6 +1613,17 @@ else
+ fi
+ AC_SUBST([BOOT_TIME_STATS])
+ 
++AC_ARG_WITH([debug-timestamps],
++	   AS_HELP_STRING([--with-debug-timestamps],
++                          [prepend debug traces with absolute and relative timestamps]))
++
++if test x$with_debug_timestamps = xyes; then
++  DEBUG_WITH_TIMESTAMPS=1
++else
++  DEBUG_WITH_TIMESTAMPS=0
++fi
++AC_SUBST([DEBUG_WITH_TIMESTAMPS])
++
+ AC_ARG_ENABLE([grub-emu-sdl],
+ 	      [AS_HELP_STRING([--enable-grub-emu-sdl],
+                              [build and install the `grub-emu' debugging utility with SDL support (default=guessed)])])
+@@ -2200,6 +2211,7 @@ AM_CONDITIONAL([COND_APPLE_LINKER], [test x$TARGET_APPLE_LINKER = x1])
+ AM_CONDITIONAL([COND_ENABLE_EFIEMU], [test x$enable_efiemu = xyes])
+ AM_CONDITIONAL([COND_ENABLE_CACHE_STATS], [test x$DISK_CACHE_STATS = x1])
+ AM_CONDITIONAL([COND_ENABLE_BOOT_TIME_STATS], [test x$BOOT_TIME_STATS = x1])
++AM_CONDITIONAL([COND_DEBUG_WITH_TIMESTAMPS], [test x$DEBUG_WITH_TIMESTAMPS = x1])
+ 
+ AM_CONDITIONAL([COND_HAVE_CXX], [test x$HAVE_CXX = xyes])
+ 
+@@ -2295,6 +2307,12 @@ else
+ echo With boot time statistics: No
+ fi
+ 
++if [ x"$with_debug_timestamps" = xyes ]; then
++echo Debug traces with timestamps: Yes
++else
++echo Debug traces with timestamps: No
++fi
++
+ if [ x"$efiemu_excuse" = x ]; then
+ echo efiemu runtime: Yes
+ else
+diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
+index 578bf51a5fc..9f54b6b7d2d 100644
+--- a/grub-core/kern/misc.c
++++ b/grub-core/kern/misc.c
+@@ -25,6 +25,9 @@
+ #include <grub/env.h>
+ #include <grub/i18n.h>
+ #include <grub/backtrace.h>
++#if DEBUG_WITH_TIMESTAMPS
++#include <grub/time.h>
++#endif
+ 
+ union printf_arg
+ {
+@@ -192,9 +195,26 @@ grub_real_dprintf (const char *file, const int line, const char *condition,
+ 		   const char *fmt, ...)
+ {
+   va_list args;
++#if DEBUG_WITH_TIMESTAMPS
++  static long unsigned int last_time = 0;
++  static int last_had_cr = 1;
++#endif
+ 
+   if (grub_debug_enabled (condition))
+     {
++#if DEBUG_WITH_TIMESTAMPS
++      /* Don't print timestamp if last printed message isn't terminated yet */
++      if (last_had_cr) {
++        long unsigned int tmabs = (long unsigned int) grub_get_time_ms();
++        long unsigned int tmrel = tmabs - last_time;
++        last_time = tmabs;
++        grub_printf ("%3lu.%03lus +%2lu.%03lus ", tmabs / 1000, tmabs % 1000, tmrel / 1000, tmrel % 1000);
++      }
++      if (fmt[grub_strlen(fmt)-1] == '\n')
++        last_had_cr = 1;
++      else
++        last_had_cr = 0;
++#endif
+       grub_printf ("%s:%d: ", file, line);
+       va_start (args, fmt);
+       grub_vprintf (fmt, args);
+diff --git a/config.h.in b/config.h.in
+index c7e316f0f1f..c80e3e0aba3 100644
+--- a/config.h.in
++++ b/config.h.in
+@@ -12,6 +12,7 @@
+ /* Define to 1 to enable disk cache statistics.  */
+ #define DISK_CACHE_STATS @DISK_CACHE_STATS@
+ #define BOOT_TIME_STATS @BOOT_TIME_STATS@
++#define DEBUG_WITH_TIMESTAMPS @DEBUG_WITH_TIMESTAMPS@
+ 
+ /* We don't need those.  */
+ #define MINILZO_CFG_SKIP_LZO_PTR 1
diff --git a/SOURCES/0171-Added-debug-statements-to-grub_disk_open-and-grub_di.patch b/SOURCES/0171-Added-debug-statements-to-grub_disk_open-and-grub_di.patch
new file mode 100644
index 0000000..cbb1a38
--- /dev/null
+++ b/SOURCES/0171-Added-debug-statements-to-grub_disk_open-and-grub_di.patch
@@ -0,0 +1,47 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
+Date: Sat, 23 Nov 2019 15:22:16 +0100
+Subject: [PATCH] Added debug statements to grub_disk_open() and
+ grub_disk_close() on success
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Renaud Métrich <rmetrich@redhat.com>
+---
+ grub-core/kern/disk.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/kern/disk.c b/grub-core/kern/disk.c
+index e1b0e073e09..05a28ab1429 100644
+--- a/grub-core/kern/disk.c
++++ b/grub-core/kern/disk.c
+@@ -285,6 +285,8 @@ grub_disk_open (const char *name)
+       return 0;
+     }
+ 
++  grub_dprintf ("disk", "Opening `%s' succeeded.\n", name);
++
+   return disk;
+ }
+ 
+@@ -292,7 +294,7 @@ void
+ grub_disk_close (grub_disk_t disk)
+ {
+   grub_partition_t part;
+-  grub_dprintf ("disk", "Closing `%s'.\n", disk->name);
++  grub_dprintf ("disk", "Closing `%s'...\n", disk->name);
+ 
+   if (disk->dev && disk->dev->disk_close)
+     (disk->dev->disk_close) (disk);
+@@ -306,8 +308,10 @@ grub_disk_close (grub_disk_t disk)
+       grub_free (disk->partition);
+       disk->partition = part;
+     }
++  grub_dprintf ("disk", "Closing `%s' succeeded.\n", disk->name);
+   grub_free ((void *) disk->name);
+   grub_free (disk);
++
+ }
+ 
+ /* Small read (less than cache size and not pass across cache unit boundaries).
diff --git a/SOURCES/0172-Introduce-function-grub_debug_is_enabled-void-return.patch b/SOURCES/0172-Introduce-function-grub_debug_is_enabled-void-return.patch
new file mode 100644
index 0000000..4e4718b
--- /dev/null
+++ b/SOURCES/0172-Introduce-function-grub_debug_is_enabled-void-return.patch
@@ -0,0 +1,51 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
+Date: Mon, 25 Nov 2019 09:29:53 +0100
+Subject: [PATCH] Introduce function grub_debug_is_enabled(void) returning 1 if
+ 'debug' is in the environment and not empty
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Renaud Métrich <rmetrich@redhat.com>
+---
+ grub-core/kern/misc.c | 13 +++++++++++++
+ include/grub/misc.h   |  1 +
+ 2 files changed, 14 insertions(+)
+
+diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
+index 9f54b6b7d2d..a186ad3dd41 100644
+--- a/grub-core/kern/misc.c
++++ b/grub-core/kern/misc.c
+@@ -163,6 +163,19 @@ int grub_err_printf (const char *fmt, ...)
+ __attribute__ ((alias("grub_printf")));
+ #endif
+ 
++/* Return 1 if 'debug' is set and not empty */
++int
++grub_debug_is_enabled (void)
++{
++  const char *debug;
++
++  debug = grub_env_get ("debug");
++  if (!debug || debug[0] == '\0')
++    return 0;
++
++  return 1;
++}
++
+ int
+ grub_debug_enabled (const char * condition)
+ {
+diff --git a/include/grub/misc.h b/include/grub/misc.h
+index 3adc4036e3b..6c4aa85ac50 100644
+--- a/include/grub/misc.h
++++ b/include/grub/misc.h
+@@ -340,6 +340,7 @@ grub_puts (const char *s)
+ }
+ 
+ int EXPORT_FUNC(grub_puts_) (const char *s);
++int EXPORT_FUNC(grub_debug_is_enabled) (void);
+ int EXPORT_FUNC(grub_debug_enabled) (const char *condition);
+ void EXPORT_FUNC(grub_real_dprintf) (const char *file,
+                                      const int line,
diff --git a/SOURCES/0173-Don-t-clear-screen-when-debugging-is-enabled.patch b/SOURCES/0173-Don-t-clear-screen-when-debugging-is-enabled.patch
new file mode 100644
index 0000000..d2a5988
--- /dev/null
+++ b/SOURCES/0173-Don-t-clear-screen-when-debugging-is-enabled.patch
@@ -0,0 +1,27 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
+Date: Sat, 23 Nov 2019 16:23:54 +0100
+Subject: [PATCH] Don't clear screen when debugging is enabled
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Renaud Métrich <rmetrich@redhat.com>
+---
+ grub-core/normal/main.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
+index e349303c29b..155bf366da2 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -210,7 +210,8 @@ void
+ grub_normal_init_page (struct grub_term_output *term,
+ 		       int y __attribute__((__unused__)))
+ {
+-  grub_term_cls (term);
++  if (! grub_debug_is_enabled ())
++    grub_term_cls (term);
+ 
+ #if 0
+   grub_ssize_t msg_len;
diff --git a/SOURCES/0174-grub_file_-instrumentation-new-file-debug-tag.patch b/SOURCES/0174-grub_file_-instrumentation-new-file-debug-tag.patch
new file mode 100644
index 0000000..0cc6b8c
--- /dev/null
+++ b/SOURCES/0174-grub_file_-instrumentation-new-file-debug-tag.patch
@@ -0,0 +1,71 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
+Date: Fri, 29 Nov 2019 11:02:00 +0100
+Subject: [PATCH] grub_file_* instrumentation (new 'file' debug tag)
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Renaud Métrich <rmetrich@redhat.com>
+---
+ grub-core/kern/file.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c
+index 58454458c47..e19aea3e514 100644
+--- a/grub-core/kern/file.c
++++ b/grub-core/kern/file.c
+@@ -66,6 +66,8 @@ grub_file_open (const char *name, enum grub_file_type type)
+   const char *file_name;
+   grub_file_filter_id_t filter;
+ 
++  grub_dprintf ("file", "Opening `%s' ...\n", name);
++
+   device_name = grub_file_get_device_name (name);
+   if (grub_errno)
+     goto fail;
+@@ -128,6 +130,8 @@ grub_file_open (const char *name, enum grub_file_type type)
+   if (!file)
+     grub_file_close (last_file);
+ 
++  grub_dprintf ("file", "Opening `%s' succeeded.\n", name);
++
+   return file;
+ 
+  fail:
+@@ -138,6 +142,8 @@ grub_file_open (const char *name, enum grub_file_type type)
+ 
+   grub_free (file);
+ 
++  grub_dprintf ("file", "Opening `%s' failed.\n", name);
++
+   return 0;
+ }
+ 
+@@ -169,6 +175,7 @@ grub_file_read (grub_file_t file, void *buf, grub_size_t len)
+ 
+   if (len == 0)
+     return 0;
++
+   read_hook = file->read_hook;
+   read_hook_data = file->read_hook_data;
+   if (!file->read_hook)
+@@ -189,11 +196,18 @@ grub_file_read (grub_file_t file, void *buf, grub_size_t len)
+ grub_err_t
+ grub_file_close (grub_file_t file)
+ {
++  grub_dprintf ("file", "Closing `%s' ...\n", file->name);
+   if (file->fs->fs_close)
+     (file->fs->fs_close) (file);
+ 
+   if (file->device)
+     grub_device_close (file->device);
++
++  if (grub_errno == GRUB_ERR_NONE)
++    grub_dprintf ("file", "Closing `%s' succeeded.\n", file->name);
++  else
++    grub_dprintf ("file", "Closing `%s' failed with %d.\n", file->name, grub_errno);
++
+   grub_free (file->name);
+   grub_free (file);
+   return grub_errno;
diff --git a/SOURCES/0175-ieee1275-Avoiding-many-unecessary-open-close.patch b/SOURCES/0175-ieee1275-Avoiding-many-unecessary-open-close.patch
new file mode 100644
index 0000000..6a0ad75
--- /dev/null
+++ b/SOURCES/0175-ieee1275-Avoiding-many-unecessary-open-close.patch
@@ -0,0 +1,136 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Diego Domingos <diegodo@br.ibm.com>
+Date: Mon, 14 Dec 2020 17:42:45 +0100
+Subject: [PATCH] ieee1275: Avoiding many unecessary open/close
+
+Signed-off-by: Diego Domingos <diegodo@br.ibm.com>
+---
+ grub-core/disk/ieee1275/ofdisk.c | 64 ++++++++++++++++++++++------------------
+ 1 file changed, 35 insertions(+), 29 deletions(-)
+
+diff --git a/grub-core/disk/ieee1275/ofdisk.c b/grub-core/disk/ieee1275/ofdisk.c
+index 03674cb477e..ea7f78ac7d8 100644
+--- a/grub-core/disk/ieee1275/ofdisk.c
++++ b/grub-core/disk/ieee1275/ofdisk.c
+@@ -44,7 +44,7 @@ struct ofdisk_hash_ent
+ };
+ 
+ static grub_err_t
+-grub_ofdisk_get_block_size (const char *device, grub_uint32_t *block_size,
++grub_ofdisk_get_block_size (grub_uint32_t *block_size,
+ 			    struct ofdisk_hash_ent *op);
+ 
+ #define OFDISK_HASH_SZ	8
+@@ -461,6 +461,7 @@ grub_ofdisk_open (const char *name, grub_disk_t disk)
+   grub_ssize_t actual;
+   grub_uint32_t block_size = 0;
+   grub_err_t err;
++  struct ofdisk_hash_ent *op;
+ 
+   if (grub_strncmp (name, "ieee1275/", sizeof ("ieee1275/") - 1) != 0)
+       return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+@@ -471,6 +472,35 @@ grub_ofdisk_open (const char *name, grub_disk_t disk)
+ 
+   grub_dprintf ("disk", "Opening `%s'.\n", devpath);
+ 
++  op = ofdisk_hash_find (devpath);
++  if (!op)
++    op = ofdisk_hash_add (devpath, NULL);
++  if (!op)
++    {
++      grub_free (devpath);
++      return grub_errno;
++    }
++
++  /* Check if the call to open is the same to the last disk already opened */
++  if (last_devpath && !grub_strcmp(op->open_path,last_devpath))
++  {
++      goto finish;
++  }
++
++ /* If not, we need to close the previous disk and open the new one */
++  else {
++    if (last_ihandle){
++        grub_ieee1275_close (last_ihandle);
++    }
++    last_ihandle = 0;
++    last_devpath = NULL;
++
++    grub_ieee1275_open (op->open_path, &last_ihandle);
++    if (! last_ihandle)
++      return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device");
++    last_devpath = op->open_path;
++  }
++
+   if (grub_ieee1275_finddevice (devpath, &dev))
+     {
+       grub_free (devpath);
+@@ -491,25 +521,18 @@ grub_ofdisk_open (const char *name, grub_disk_t disk)
+       return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a block device");
+     }
+ 
++
++  finish:
+   /* XXX: There is no property to read the number of blocks.  There
+      should be a property `#blocks', but it is not there.  Perhaps it
+      is possible to use seek for this.  */
+   disk->total_sectors = GRUB_DISK_SIZE_UNKNOWN;
+ 
+   {
+-    struct ofdisk_hash_ent *op;
+-    op = ofdisk_hash_find (devpath);
+-    if (!op)
+-      op = ofdisk_hash_add (devpath, NULL);
+-    if (!op)
+-      {
+-        grub_free (devpath);
+-        return grub_errno;
+-      }
+     disk->id = (unsigned long) op;
+     disk->data = op->open_path;
+ 
+-    err = grub_ofdisk_get_block_size (devpath, &block_size, op);
++    err = grub_ofdisk_get_block_size (&block_size, op);
+     if (err)
+       {
+         grub_free (devpath);
+@@ -532,13 +555,6 @@ grub_ofdisk_open (const char *name, grub_disk_t disk)
+ static void
+ grub_ofdisk_close (grub_disk_t disk)
+ {
+-  if (disk->data == last_devpath)
+-    {
+-      if (last_ihandle)
+-	grub_ieee1275_close (last_ihandle);
+-      last_ihandle = 0;
+-      last_devpath = NULL;
+-    }
+   disk->data = 0;
+ }
+ 
+@@ -685,7 +701,7 @@ grub_ofdisk_init (void)
+ }
+ 
+ static grub_err_t
+-grub_ofdisk_get_block_size (const char *device, grub_uint32_t *block_size,
++grub_ofdisk_get_block_size (grub_uint32_t *block_size,
+ 			    struct ofdisk_hash_ent *op)
+ {
+   struct size_args_ieee1275
+@@ -698,16 +714,6 @@ grub_ofdisk_get_block_size (const char *device, grub_uint32_t *block_size,
+       grub_ieee1275_cell_t size2;
+     } args_ieee1275;
+ 
+-  if (last_ihandle)
+-    grub_ieee1275_close (last_ihandle);
+-
+-  last_ihandle = 0;
+-  last_devpath = NULL;
+-
+-  grub_ieee1275_open (device, &last_ihandle);
+-  if (! last_ihandle)
+-    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device");
+-
+   *block_size = 0;
+ 
+   if (op->block_size_fails >= 2)
diff --git a/SOURCES/0176-ieee1275-powerpc-implements-fibre-channel-discovery-.patch b/SOURCES/0176-ieee1275-powerpc-implements-fibre-channel-discovery-.patch
new file mode 100644
index 0000000..9fa02bb
--- /dev/null
+++ b/SOURCES/0176-ieee1275-powerpc-implements-fibre-channel-discovery-.patch
@@ -0,0 +1,90 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Diego Domingos <diegodo@br.ibm.com>
+Date: Mon, 14 Dec 2020 17:45:28 +0100
+Subject: [PATCH] ieee1275/powerpc: implements fibre channel discovery for
+ ofpathname
+
+grub-ofpathname doesn't work with fibre channel because there is no
+function currently implemented for it.
+This patch enables it by prividing a function that looks for the port
+name, building the entire path for OF devices.
+
+Signed-off-by: Diego Domingos <diegodo@br.ibm.com>
+---
+ grub-core/osdep/linux/ofpath.c | 49 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 49 insertions(+)
+
+diff --git a/grub-core/osdep/linux/ofpath.c b/grub-core/osdep/linux/ofpath.c
+index a6153d35954..0f5d54e9f2d 100644
+--- a/grub-core/osdep/linux/ofpath.c
++++ b/grub-core/osdep/linux/ofpath.c
+@@ -350,6 +350,38 @@ of_path_of_ide(const char *sys_devname __attribute__((unused)), const char *devi
+   return ret;
+ }
+ 
++
++static void
++of_fc_port_name(const char *path, const char *subpath, char *port_name)
++{
++  char *bname, *basepath, *p;
++  int fd;
++
++  bname = xmalloc(sizeof(char)*150);
++  basepath = xmalloc(strlen(path));
++
++  /* Generate the path to get port name information from the drive */
++  strncpy(basepath,path,subpath-path);
++  basepath[subpath-path-1] = '\0';
++  p = get_basename(basepath);
++  snprintf(bname,sizeof(char)*150,"%s/fc_transport/%s/port_name",basepath,p);
++
++  /* Read the information from the port name */
++  fd = open (bname, O_RDONLY);
++  if (fd < 0)
++    grub_util_error (_("cannot open `%s': %s"), bname, strerror (errno));
++
++  if (read(fd,port_name,sizeof(char)*19) < 0)
++    grub_util_error (_("cannot read `%s': %s"), bname, strerror (errno));
++
++  sscanf(port_name,"0x%s",port_name);
++  
++  close(fd);
++
++  free(bname);
++  free(basepath);
++}
++
+ #ifdef __sparc__
+ static char *
+ of_path_of_nvme(const char *sys_devname __attribute__((unused)),
+@@ -577,6 +609,16 @@ of_path_of_scsi(const char *sys_devname __attribute__((unused)), const char *dev
+   digit_string = trailing_digits (device);
+   if (strncmp (of_path, "/vdevice/", sizeof ("/vdevice/") - 1) == 0)
+     {
++      if(strstr(of_path,"vfc-client"))
++      {
++	char * port_name = xmalloc(sizeof(char)*17);
++	of_fc_port_name(sysfs_path, p, port_name);
++	
++	snprintf(disk,sizeof(disk),"/%s@%s", disk_name, port_name);
++	free(port_name);
++      }
++      else
++      {
+       unsigned long id = 0x8000 | (tgt << 8) | (bus << 5) | lun;
+       if (*digit_string == '\0')
+ 	{
+@@ -590,6 +632,13 @@ of_path_of_scsi(const char *sys_devname __attribute__((unused)), const char *dev
+ 	  snprintf(disk, sizeof (disk),
+ 		   "/%s@%04lx000000000000:%c", disk_name, id, 'a' + (part - 1));
+ 	}
++	}
++    } else if (strstr(of_path,"fibre-channel")||(strstr(of_path,"vfc-client"))){
++	char * port_name = xmalloc(sizeof(char)*17);
++	of_fc_port_name(sysfs_path, p, port_name);
++	
++	snprintf(disk,sizeof(disk),"/%s@%s", disk_name, port_name);
++	free(port_name);
+     }
+   else
+     {
diff --git a/SOURCES/0177-ieee1275-powerpc-enables-device-mapper-discovery.patch b/SOURCES/0177-ieee1275-powerpc-enables-device-mapper-discovery.patch
new file mode 100644
index 0000000..6f1c21b
--- /dev/null
+++ b/SOURCES/0177-ieee1275-powerpc-enables-device-mapper-discovery.patch
@@ -0,0 +1,106 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Diego Domingos <diegodo@br.ibm.com>
+Date: Mon, 14 Dec 2020 17:47:16 +0100
+Subject: [PATCH] ieee1275/powerpc: enables device mapper discovery
+
+this patch enables the device mapper discovery on ofpath.c. Currently,
+when we are dealing with a device like /dev/dm-* the ofpath returns null
+since there is no function implemented to handle this case.
+
+This patch implements a function that will look into /sys/block/dm-*
+devices and search recursively inside slaves directory to find the root
+disk.
+
+Signed-off-by: Diego Domingos <diegodo@br.ibm.com>
+---
+ grub-core/osdep/linux/ofpath.c | 64 +++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 63 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/osdep/linux/ofpath.c b/grub-core/osdep/linux/ofpath.c
+index 0f5d54e9f2d..cc849d9c94c 100644
+--- a/grub-core/osdep/linux/ofpath.c
++++ b/grub-core/osdep/linux/ofpath.c
+@@ -37,6 +37,7 @@
+ #include <fcntl.h>
+ #include <errno.h>
+ #include <ctype.h>
++#include <dirent.h>
+ 
+ #ifdef __sparc__
+ typedef enum
+@@ -755,13 +756,74 @@ strip_trailing_digits (const char *p)
+   return new;
+ }
+ 
++static char *
++get_slave_from_dm(const char * device){
++  char *curr_device, *tmp;
++  char *directory;
++  char *ret = NULL;
++
++  directory = grub_strdup (device);
++  tmp = get_basename(directory);
++  curr_device = grub_strdup (tmp);
++  *tmp = '\0';
++
++  /* Recursively check for slaves devices so we can find the root device */
++  while ((curr_device[0] == 'd') && (curr_device[1] == 'm') && (curr_device[2] == '-')){
++    DIR *dp;
++    struct dirent *ep;
++    char* device_path;
++
++    device_path = grub_xasprintf ("/sys/block/%s/slaves", curr_device);
++    dp = opendir(device_path);
++    free(device_path);
++
++    if (dp != NULL)
++    {
++      ep = readdir (dp);
++      while (ep != NULL){
++
++	/* avoid some system directories */
++        if (!strcmp(ep->d_name,"."))
++            goto next_dir;
++        if (!strcmp(ep->d_name,".."))
++            goto next_dir;
++
++	free (curr_device);
++	free (ret);
++	curr_device = grub_strdup (ep->d_name);
++	ret = grub_xasprintf ("%s%s", directory, curr_device);
++	break;
++
++        next_dir:
++         ep = readdir (dp);
++         continue;
++      }
++      closedir (dp);
++    }
++    else
++      grub_util_warn (_("cannot open directory `%s'"), device_path);
++  }
++
++  free (directory);
++  free (curr_device);
++
++  return ret;
++}
++
+ char *
+ grub_util_devname_to_ofpath (const char *sys_devname)
+ {
+-  char *name_buf, *device, *devnode, *devicenode, *ofpath;
++  char *name_buf, *device, *devnode, *devicenode, *ofpath, *realname;
+ 
+   name_buf = xrealpath (sys_devname);
+ 
++  realname = get_slave_from_dm (name_buf);
++  if (realname)
++    {
++      free (name_buf);
++      name_buf = realname;
++    }
++
+   device = get_basename (name_buf);
+   devnode = strip_trailing_digits (name_buf);
+   devicenode = strip_trailing_digits (device);
diff --git a/SOURCES/0178-Add-at_keyboard_fallback_set-var-to-force-the-set-ma.patch b/SOURCES/0178-Add-at_keyboard_fallback_set-var-to-force-the-set-ma.patch
new file mode 100644
index 0000000..968a9c1
--- /dev/null
+++ b/SOURCES/0178-Add-at_keyboard_fallback_set-var-to-force-the-set-ma.patch
@@ -0,0 +1,239 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
+Date: Fri, 18 Dec 2020 15:39:26 +0100
+Subject: [PATCH] Add 'at_keyboard_fallback_set' var to force the set manually
+
+This seems required with HP DL380p Gen 8 systems.
+Indeed, with this system, we can see the following sequence:
+
+1. controller is queried to get current configuration (returns 0x30 which is quite standard)
+2. controller is queried to get the current keyboard set in used, using code 0xf0 (first part)
+3. controller answers with 0xfa which means "ACK" (== ok)
+4. then we send "0" to tell "we want to know which set your are supporting"
+5. controller answers with 0xfa ("ACK")
+6. controller should then give us 1, 2, 3 or 0x43, 0x41, 0x3f, but here it gives us 0xfe which means "NACK"
+
+Since there seems no way to determine the current set, and in fact the
+controller expects set2 to be used, we need to rely on an environment
+variable.
+Everything has been tested on this system: using 0xFE (resend command),
+making sure we wait for ACK in the 2 steps "write_mode", etc.
+
+Below is litterature I used to come up with "there is no other
+solution":
+- https://wiki.osdev.org/%228042%22_PS/2_Controller
+- http://www-ug.eecg.toronto.edu/msl/nios_devices/datasheets/PS2%20Keyboard%20Protocol.htm
+- http://www.s100computers.com/My%20System%20Pages/MSDOS%20Board/PC%20Keyboard.pdf
+---
+ grub-core/term/at_keyboard.c | 121 ++++++++++++++++++++++++++++++++++---------
+ 1 file changed, 96 insertions(+), 25 deletions(-)
+
+diff --git a/grub-core/term/at_keyboard.c b/grub-core/term/at_keyboard.c
+index 26014382608..dac0f946fe6 100644
+--- a/grub-core/term/at_keyboard.c
++++ b/grub-core/term/at_keyboard.c
+@@ -31,6 +31,7 @@ GRUB_MOD_LICENSE ("GPLv3+");
+ static grub_uint8_t grub_keyboard_controller_orig;
+ static grub_uint8_t grub_keyboard_orig_set;
+ struct grub_ps2_state ps2_state;
++static int fallback_set;
+ 
+ static int ping_sent;
+ 
+@@ -76,6 +77,8 @@ at_command (grub_uint8_t data)
+ 	break;
+       return 0;
+     }
++  if (i == GRUB_AT_TRIES)
++    grub_dprintf ("atkeyb", "at_command() timed out! (stopped after %d tries)\n", i);
+   return (i != GRUB_AT_TRIES);
+ }
+ 
+@@ -105,6 +108,21 @@ grub_keyboard_controller_read (void)
+ 
+ #endif
+ 
++static int
++resend_last_result (void)
++{
++  grub_uint8_t ret;
++  keyboard_controller_wait_until_ready ();
++  grub_dprintf ("atkeyb", "resend_last_result: sending 0xfe\n");
++  grub_outb (0xfe, KEYBOARD_REG_DATA);
++  ret = wait_ack ();
++  grub_dprintf ("atkeyb", "resend_last_result: wait_ack() returned 0x%x\n", ret);
++  keyboard_controller_wait_until_ready ();
++  ret = grub_inb (KEYBOARD_REG_DATA);
++  grub_dprintf ("atkeyb", "resend_last_result: read 0x%x from controller\n", ret);
++  return ret;
++}
++
+ static int
+ write_mode (int mode)
+ {
+@@ -113,11 +131,14 @@ write_mode (int mode)
+     {
+       grub_uint8_t ack;
+       keyboard_controller_wait_until_ready ();
++      grub_dprintf ("atkeyb", "write_mode: sending 0xf0\n");
+       grub_outb (0xf0, KEYBOARD_REG_DATA);
+       keyboard_controller_wait_until_ready ();
++      grub_dprintf ("atkeyb", "write_mode: sending mode %d\n", mode);
+       grub_outb (mode, KEYBOARD_REG_DATA);
+       keyboard_controller_wait_until_ready ();
+       ack = wait_ack ();
++      grub_dprintf ("atkeyb", "write_mode: wait_ack() returned 0x%x\n", ack);
+       if (ack == GRUB_AT_NACK)
+ 	continue;
+       if (ack == GRUB_AT_ACK)
+@@ -125,6 +146,9 @@ write_mode (int mode)
+       return 0;
+     }
+ 
++  if (i == GRUB_AT_TRIES)
++    grub_dprintf ("atkeyb", "write_mode() timed out! (stopped after %d tries)\n", i);
++
+   return (i != GRUB_AT_TRIES);
+ }
+ 
+@@ -132,31 +156,66 @@ static int
+ query_mode (void)
+ {
+   grub_uint8_t ret;
++  grub_uint64_t endtime;
++  unsigned i;
+   int e;
++  char *envvar;
+ 
+-  e = write_mode (0);
+-  if (!e) {
+-    grub_dprintf("atkeyb", "query_mode: write_mode(0) failed\n");
+-    return 0;
+-  }
++  for (i = 0; i < GRUB_AT_TRIES; i++) {
++    grub_dprintf ("atkeyb", "query_mode: sending command to controller\n");
++    e = write_mode (0);
++    if (!e) {
++      grub_dprintf ("atkeyb", "query_mode: write_mode(0) failed\n");
++      return 0;
++    }
+ 
+-  do {
+-    keyboard_controller_wait_until_ready ();
+-    ret = grub_inb (KEYBOARD_REG_DATA);
+-  } while (ret == GRUB_AT_ACK);
+-  /* QEMU translates the set even in no-translate mode.  */
+-  if (ret == 0x43 || ret == 1) {
+-    grub_dprintf("atkeyb", "query_mode: returning 1 (ret=0x%x)\n", ret);
+-    return 1;
+-  }
+-  if (ret == 0x41 || ret == 2) {
+-    grub_dprintf("atkeyb", "query_mode: returning 2 (ret=0x%x)\n", ret);
+-    return 2;
++    endtime = grub_get_time_ms () + 20;
++    do {
++      keyboard_controller_wait_until_ready ();
++      ret = grub_inb (KEYBOARD_REG_DATA);
++      grub_dprintf ("atkeyb", "query_mode/loop: read 0x%x from controller\n", ret);
++    } while ((ret == GRUB_AT_ACK || ret == GRUB_AT_NACK) && grub_get_time_ms () < endtime);
++    if (ret == 0xfe) {
++      grub_dprintf ("atkeyb", "query_mode: asking controller to resend last result\n");
++      ret = resend_last_result();
++      grub_dprintf ("atkeyb", "query_mode: read 0x%x from controller\n", ret);
++    }
++    /* QEMU translates the set even in no-translate mode.  */
++    if (ret == 0x43 || ret == 1) {
++      grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 1\n", ret);
++      return 1;
++    }
++    if (ret == 0x41 || ret == 2) {
++      grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 2\n", ret);
++      return 2;
++    }
++    if (ret == 0x3f || ret == 3) {
++      grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 3\n", ret);
++      return 3;
++    }
++    grub_dprintf ("atkeyb", "query_mode: controller returned unexpected value 0x%x, retrying\n", ret);
+   }
+-  if (ret == 0x3f || ret == 3) {
+-    grub_dprintf("atkeyb", "query_mode: returning 3 (ret=0x%x)\n", ret);
+-    return 3;
++
++  /*
++   * Falling here means we tried querying and the controller returned something
++   * we don't understand, try to use 'at_keyboard_fallback_set' if it exists,
++   * otherwise return 0.
++   */
++  envvar = grub_env_get ("at_keyboard_fallback_set");
++  if (envvar) {
++    fallback_set = grub_strtoul (envvar, 0, 10);
++    if ((grub_errno) || (fallback_set < 1) || (fallback_set > 3)) {
++      grub_dprintf ("atkeyb", "WARNING: ignoring unexpected value '%s' for '%s' variable\n",
++		    envvar, "at_keyboard_fallback_set");
++      fallback_set = 0;
++    } else {
++      grub_dprintf ("atkeyb", "query_mode: '%s' specified in environment, returning %d\n",
++		    "at_keyboard_fallback_set", fallback_set);
++    }
++    return fallback_set;
+   }
++  grub_dprintf ("atkeyb", "WARNING: no '%s' specified in environment, returning 0\n",
++		"at_keyboard_fallback_set");
+   return 0;
+ }
+ 
+@@ -165,14 +224,25 @@ set_scancodes (void)
+ {
+   /* You must have visited computer museum. Keyboard without scancode set
+      knowledge. Assume XT. */
+-  if (!grub_keyboard_orig_set)
+-    {
+-      grub_dprintf ("atkeyb", "No sets support assumed\n");
+-      ps2_state.current_set = 1;
++  if (!grub_keyboard_orig_set) {
++    if (fallback_set) {
++      grub_dprintf ("atkeyb", "No sets support assumed but set forced to %d\n", fallback_set);
++      ps2_state.current_set = fallback_set;
+       return;
+     }
++    grub_dprintf ("atkeyb", "No sets support assumed, forcing to set 1\n");
++    ps2_state.current_set = 1;
++    return;
++  }
+ 
+ #if !USE_SCANCODE_SET
++  if (fallback_set) {
++    grub_dprintf ("atkeyb", "queried set is %d but set forced to %d\n",
++		  grub_keyboard_orig_set, fallback_set);
++    ps2_state.current_set = fallback_set;
++    return;
++  }
++
+   if ((grub_keyboard_controller_orig & KEYBOARD_AT_TRANSLATE) == KEYBOARD_AT_TRANSLATE) {
+     grub_dprintf ("atkeyb", "queried set is %d but keyboard in Translate mode, so actually in set 1\n", grub_keyboard_orig_set);
+     ps2_state.current_set = 1;
+@@ -261,6 +331,7 @@ grub_at_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused)))
+ static void
+ grub_keyboard_controller_init (void)
+ {
++  grub_dprintf ("atkeyb", "initializing the controller\n");
+   ps2_state.at_keyboard_status = 0;
+   /* Drain input buffer. */
+   while (1)
+@@ -282,6 +353,7 @@ grub_keyboard_controller_init (void)
+   grub_keyboard_controller_orig = grub_keyboard_controller_read ();
+   grub_dprintf ("atkeyb", "grub_keyboard_controller_orig = 0x%x\n", grub_keyboard_controller_orig);
+   grub_keyboard_orig_set = query_mode ();
++  grub_dprintf ("atkeyb", "grub_keyboard_orig_set = %d\n", grub_keyboard_orig_set);
+ #endif
+   set_scancodes ();
+   keyboard_controller_led (ps2_state.led_status);
+@@ -329,7 +401,6 @@ grub_at_restore_hw (void)
+   return GRUB_ERR_NONE;
+ }
+ 
+-
+ static struct grub_term_input grub_at_keyboard_term =
+   {
+     .name = "at_keyboard",
diff --git a/SOURCES/0179-Add-suport-for-signing-grub-with-an-appended-signatu.patch b/SOURCES/0179-Add-suport-for-signing-grub-with-an-appended-signatu.patch
new file mode 100644
index 0000000..a863298
--- /dev/null
+++ b/SOURCES/0179-Add-suport-for-signing-grub-with-an-appended-signatu.patch
@@ -0,0 +1,317 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Rashmica Gupta <rashmica.g@gmail.com>
+Date: Thu, 11 Jun 2020 11:26:23 +1000
+Subject: [PATCH] Add suport for signing grub with an appended signature
+
+Add infrastructure to allow firmware to verify the integrity of grub
+by use of a Linux-kernel-module-style appended signature. We initially
+target powerpc-ieee1275, but the code should be extensible to other
+platforms.
+
+Usually these signatures are appended to a file without modifying the
+ELF file itself. (This is what the 'sign-file' tool does, for example.)
+The verifier loads the signed file from the file system and looks at the
+end of the file for the appended signature. However, on powerpc-ieee1275
+platforms, the bootloader is often stored directly in the PReP partition
+as raw bytes without a file-system. This makes determining the location
+of an appended signature more difficult.
+
+To address this, we add a new ELF note.
+
+The name field of shall be the string "Appended-Signature", zero-padded
+to 4 byte alignment. The type field shall be 0x41536967 (the ASCII values
+for the string "ASig"). It must be the final section in the ELF binary.
+
+The description shall contain the appended signature structure as defined
+by the Linux kernel. The description will also be padded to be a multiple
+of 4 bytes. The padding shall be added before the appended signature
+structure (not at the end) so that the final bytes of a signed ELF file
+are the appended signature magic.
+
+A subsequent patch documents how to create a grub core.img validly signed
+under this scheme.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Signed-off-by: Rashmica Gupta <rashmica.g@gmail.com>
+
+---
+
+You can experiment with this code with a patched version of SLOF
+that verifies these signatures. You can find one at:
+   https://github.com/daxtens/SLOF
+
+I will be proposing this for inclusion in a future Power Architecture
+Platform Reference (PAPR).
+---
+ util/grub-install-common.c  | 18 ++++++++++++++++--
+ util/grub-mkimage.c         | 16 ++++++++++++++--
+ util/grub-mkimagexx.c       | 39 ++++++++++++++++++++++++++++++++++++++-
+ util/mkimage.c              | 13 +++++++------
+ include/grub/util/install.h |  8 ++++++--
+ include/grub/util/mkimage.h |  4 ++--
+ 6 files changed, 83 insertions(+), 15 deletions(-)
+
+diff --git a/util/grub-install-common.c b/util/grub-install-common.c
+index 4e212e690c5..aab2a941f85 100644
+--- a/util/grub-install-common.c
++++ b/util/grub-install-common.c
+@@ -461,10 +461,12 @@ static size_t npubkeys;
+ static char *sbat;
+ static int disable_shim_lock;
+ static grub_compression_t compression;
++static size_t appsig_size;
+ 
+ int
+ grub_install_parse (int key, char *arg)
+ {
++  const char *end;
+   switch (key)
+     {
+     case 'C':
+@@ -562,6 +564,12 @@ grub_install_parse (int key, char *arg)
+       grub_util_error (_("Unrecognized compression `%s'"), arg);
+     case GRUB_INSTALL_OPTIONS_GRUB_MKIMAGE:
+       return 1;
++    case GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE:
++      grub_errno = 0;
++      appsig_size = grub_strtol(arg, &end, 10);
++      if (grub_errno)
++        return 0;
++      return 1;
+     default:
+       return 0;
+     }
+@@ -665,7 +673,13 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
+ 		  dir, prefix,
+ 		  outname, dtb ? : "", sbat ? : "", mkimage_target,
+ 		  compnames[compression], note ? "--note" : "",
+-		  disable_shim_lock ? "--disable-shim-lock" : "", s);
++		  disable_shim_lock ? "--disable-shim-lock" : "",
++		  "--format '%s' --compression '%s' "
++		  "--appended-signature-size %zu %s %s\n",
++		  dir, prefix,
++		  outname, dtb ? : "", mkimage_target,
++		  compnames[compression], appsig_size,
++		  note ? "--note" : "", s);
+   free (s);
+ 
+   tgt = grub_install_get_image_target (mkimage_target);
+@@ -675,7 +689,7 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
+   grub_install_generate_image (dir, prefix, fp, outname,
+ 			       modules.entries, memdisk_path,
+ 			       pubkeys, npubkeys, config_path, tgt,
+-			       note, compression, dtb, sbat,
++			       note, appsig_size, compression, dtb, sbat,
+ 			       disable_shim_lock);
+   while (dc--)
+     grub_install_pop_module ();
+diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c
+index c0d55993702..26d1ecbf74e 100644
+--- a/util/grub-mkimage.c
++++ b/util/grub-mkimage.c
+@@ -84,6 +84,7 @@ static struct argp_option options[] = {
+   {"sbat", 's', N_("FILE"), 0, N_("SBAT metadata"), 0},
+   {"disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, N_("disable shim_lock verifier"), 0},
+   {"verbose",     'v', 0,      0, N_("print verbose messages."), 0},
++  {"appended-signature-size", 's', N_("SIZE"), 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), 0},
+   { 0, 0, 0, 0, 0, 0 }
+ };
+ 
+@@ -128,6 +129,7 @@ struct arguments
+   char *sbat;
+   int note;
+   int disable_shim_lock;
++  size_t appsig_size;
+   const struct grub_install_image_target_desc *image_target;
+   grub_compression_t comp;
+ };
+@@ -138,6 +140,7 @@ argp_parser (int key, char *arg, struct argp_state *state)
+   /* Get the input argument from argp_parse, which we
+      know is a pointer to our arguments structure. */
+   struct arguments *arguments = state->input;
++  const char* end;
+ 
+   switch (key)
+     {
+@@ -170,6 +173,13 @@ argp_parser (int key, char *arg, struct argp_state *state)
+       arguments->note = 1;
+       break;
+ 
++    case 'S':
++      grub_errno = 0;
++      arguments->appsig_size = grub_strtol(arg, &end, 10);
++      if (grub_errno)
++        return 0;
++      break;
++
+     case 'm':
+       if (arguments->memdisk)
+ 	free (arguments->memdisk);
+@@ -324,8 +334,10 @@ main (int argc, char *argv[])
+ 			       arguments.memdisk, arguments.pubkeys,
+ 			       arguments.npubkeys, arguments.config,
+ 			       arguments.image_target, arguments.note,
+-			       arguments.comp, arguments.dtb,
+-			       arguments.sbat, arguments.disable_shim_lock);
++
++			       arguments.comp, arguments.appsig_size,
++			       arguments.dtb, arguments.sbat,
++			       arguments.disable_shim_lock);
+ 
+   if (grub_util_file_sync (fp) < 0)
+     grub_util_error (_("cannot sync `%s': %s"), arguments.output ? : "stdout",
+diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c
+index d78fa3e5330..393119486d3 100644
+--- a/util/grub-mkimagexx.c
++++ b/util/grub-mkimagexx.c
+@@ -84,6 +84,15 @@ struct grub_ieee1275_note
+   struct grub_ieee1275_note_desc descriptor;
+ };
+ 
++#define GRUB_APPENDED_SIGNATURE_NOTE_NAME "Appended-Signature"
++#define GRUB_APPENDED_SIGNATURE_NOTE_TYPE 0x41536967 /* "ASig" */
++
++struct grub_appended_signature_note
++{
++  Elf32_Nhdr header;
++  char name[ALIGN_UP(sizeof (GRUB_APPENDED_SIGNATURE_NOTE_NAME), 4)];
++};
++
+ #define GRUB_XEN_NOTE_NAME "Xen"
+ 
+ struct fixup_block_list
+@@ -207,7 +216,7 @@ grub_arm_reloc_jump24 (grub_uint32_t *target, Elf32_Addr sym_addr)
+ 
+ void
+ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc *image_target,
+-				    int note, char **core_img, size_t *core_size,
++				    int note, size_t appsig_size, char **core_img, size_t *core_size,
+ 				    Elf_Addr target_addr,
+ 				    struct grub_mkimage_layout *layout)
+ {
+@@ -221,6 +230,12 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc
+   int shnum = 4;
+   int string_size = sizeof (".text") + sizeof ("mods") + 1;
+ 
++  if (appsig_size)
++    {
++      phnum++;
++      footer_size += ALIGN_UP(sizeof (struct grub_appended_signature_note) + appsig_size, 4);
++    }
++
+   if (image_target->id != IMAGE_LOONGSON_ELF)
+     phnum += 2;
+ 
+@@ -484,6 +499,28 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc
+       phdr->p_offset = grub_host_to_target32 (header_size + program_size);
+     }
+ 
++  if (appsig_size) {
++    int note_size = ALIGN_UP(sizeof (struct grub_appended_signature_note) + appsig_size, 4);
++    struct grub_appended_signature_note *note_ptr = (struct grub_appended_signature_note *)
++      (elf_img + program_size + header_size + (note ? sizeof (struct grub_ieee1275_note) : 0));
++
++    note_ptr->header.n_namesz = grub_host_to_target32 (sizeof (GRUB_APPENDED_SIGNATURE_NOTE_NAME));
++    /* needs to sit at the end, so we round this up and sign some zero padding */
++    note_ptr->header.n_descsz = grub_host_to_target32 (ALIGN_UP(appsig_size, 4));
++    note_ptr->header.n_type = grub_host_to_target32 (GRUB_APPENDED_SIGNATURE_NOTE_TYPE);
++    strcpy (note_ptr->name, GRUB_APPENDED_SIGNATURE_NOTE_NAME);
++
++    phdr++;
++    phdr->p_type = grub_host_to_target32 (PT_NOTE);
++    phdr->p_flags = grub_host_to_target32 (PF_R);
++    phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof);
++    phdr->p_vaddr = 0;
++    phdr->p_paddr = 0;
++    phdr->p_filesz = grub_host_to_target32 (note_size);
++    phdr->p_memsz = 0;
++    phdr->p_offset = grub_host_to_target32 (header_size + program_size + (note ? sizeof (struct grub_ieee1275_note) : 0));
++  }
++
+   {
+     char *str_start = (elf_img + sizeof (*ehdr) + phnum * sizeof (*phdr)
+ 		       + shnum * sizeof (*shdr));
+diff --git a/util/mkimage.c b/util/mkimage.c
+index a26cf76f72f..bab12276010 100644
+--- a/util/mkimage.c
++++ b/util/mkimage.c
+@@ -869,8 +869,9 @@ grub_install_generate_image (const char *dir, const char *prefix,
+ 			     char *memdisk_path, char **pubkey_paths,
+ 			     size_t npubkeys, char *config_path,
+ 			     const struct grub_install_image_target_desc *image_target,
+-			     int note, grub_compression_t comp, const char *dtb_path,
+-			     const char *sbat_path, int disable_shim_lock)
++			     int note, size_t appsig_size, grub_compression_t comp,
++			     const char *dtb_path, const char *sbat_path,
++			     int disable_shim_lock)
+ {
+   char *kernel_img, *core_img;
+   size_t total_module_size, core_size;
+@@ -1773,11 +1774,11 @@ grub_install_generate_image (const char *dir, const char *prefix,
+ 	else
+ 	  target_addr = image_target->link_addr;
+ 	if (image_target->voidp_sizeof == 4)
+-	  grub_mkimage_generate_elf32 (image_target, note, &core_img, &core_size,
+-				       target_addr, &layout);
++	  grub_mkimage_generate_elf32 (image_target, note, appsig_size, &core_img,
++				       &core_size, target_addr, &layout);
+ 	else
+-	  grub_mkimage_generate_elf64 (image_target, note, &core_img, &core_size,
+-				       target_addr, &layout);
++	  grub_mkimage_generate_elf64 (image_target, note, appsig_size, &core_img,
++				       &core_size, target_addr, &layout);
+       }
+       break;
+     }
+diff --git a/include/grub/util/install.h b/include/grub/util/install.h
+index 7df3191f47e..cf4531e02b6 100644
+--- a/include/grub/util/install.h
++++ b/include/grub/util/install.h
+@@ -67,6 +67,9 @@
+       N_("SBAT metadata"), 0 },						\
+   { "disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0,	\
+       N_("disable shim_lock verifier"), 0 },				\
++  { "appended-signature-size", GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE,\
++    "SIZE", 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), \
++    1},                                                                 \
+   { "verbose", 'v', 0, 0,						\
+     N_("print verbose messages."), 1 }
+ 
+@@ -128,7 +131,8 @@ enum grub_install_options {
+   GRUB_INSTALL_OPTIONS_INSTALL_CORE_COMPRESS,
+   GRUB_INSTALL_OPTIONS_DTB,
+   GRUB_INSTALL_OPTIONS_SBAT,
+-  GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK
++  GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK,
++  GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE
+ };
+ 
+ extern char *grub_install_source_directory;
+@@ -188,7 +192,7 @@ grub_install_generate_image (const char *dir, const char *prefix,
+ 			     size_t npubkeys,
+ 			     char *config_path,
+ 			     const struct grub_install_image_target_desc *image_target,
+-			     int note,
++			     int note, size_t appsig_size,
+ 			     grub_compression_t comp, const char *dtb_file,
+ 			     const char *sbat_path, const int disable_shim_lock);
+ 
+diff --git a/include/grub/util/mkimage.h b/include/grub/util/mkimage.h
+index 3819a67441c..6f1da89b9b6 100644
+--- a/include/grub/util/mkimage.h
++++ b/include/grub/util/mkimage.h
+@@ -51,12 +51,12 @@ grub_mkimage_load_image64 (const char *kernel_path,
+ 			   const struct grub_install_image_target_desc *image_target);
+ void
+ grub_mkimage_generate_elf32 (const struct grub_install_image_target_desc *image_target,
+-			     int note, char **core_img, size_t *core_size,
++			     int note, size_t appsig_size, char **core_img, size_t *core_size,
+ 			     Elf32_Addr target_addr,
+ 			     struct grub_mkimage_layout *layout);
+ void
+ grub_mkimage_generate_elf64 (const struct grub_install_image_target_desc *image_target,
+-			     int note, char **core_img, size_t *core_size,
++			     int note, size_t appsig_size, char **core_img, size_t *core_size,
+ 			     Elf64_Addr target_addr,
+ 			     struct grub_mkimage_layout *layout);
+ 
diff --git a/SOURCES/0180-docs-grub-Document-signing-grub-under-UEFI.patch b/SOURCES/0180-docs-grub-Document-signing-grub-under-UEFI.patch
new file mode 100644
index 0000000..9b9b19a
--- /dev/null
+++ b/SOURCES/0180-docs-grub-Document-signing-grub-under-UEFI.patch
@@ -0,0 +1,61 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Sat, 15 Aug 2020 02:00:57 +1000
+Subject: [PATCH] docs/grub: Document signing grub under UEFI
+
+Before adding information about how grub is signed with an appended
+signature scheme, it's worth adding some information about how it
+can currently be signed for UEFI.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ docs/grub.texi | 22 +++++++++++++++++++++-
+ 1 file changed, 21 insertions(+), 1 deletion(-)
+
+diff --git a/docs/grub.texi b/docs/grub.texi
+index 4870faaa00a..365d1d6931b 100644
+--- a/docs/grub.texi
++++ b/docs/grub.texi
+@@ -5817,6 +5817,7 @@ environment variables and commands are listed in the same order.
+ * Secure Boot Advanced Targeting::   Embedded information for generation number based revocation
+ * Measured Boot::                    Measuring boot components
+ * Lockdown::                         Lockdown when booting on a secure setup
++* Signing GRUB itself::              Ensuring the integrity of the GRUB core image
+ @end menu
+ 
+ @node Authentication and authorisation
+@@ -5895,7 +5896,7 @@ commands.
+ 
+ GRUB's @file{core.img} can optionally provide enforcement that all files
+ subsequently read from disk are covered by a valid digital signature.
+-This document does @strong{not} cover how to ensure that your
++This section does @strong{not} cover how to ensure that your
+ platform's firmware (e.g., Coreboot) validates @file{core.img}.
+ 
+ If environment variable @code{check_signatures}
+@@ -6067,6 +6068,25 @@ be restricted and some operations/commands cannot be executed.
+ The @samp{lockdown} variable is set to @samp{y} when the GRUB is locked down.
+ Otherwise it does not exit.
+ 
++@node Signing GRUB itself
++@section Signing GRUB itself
++
++To ensure a complete secure-boot chain, there must be a way for the code that
++loads GRUB to verify the integrity of the core image.
++
++This is ultimately platform-specific and individual platforms can define their
++own mechanisms. However, there are general-purpose mechanisms that can be used
++with GRUB.
++
++@section Signing GRUB for UEFI secure boot
++
++On UEFI platforms, @file{core.img} is a PE binary. Therefore, it can be signed
++with a tool such as @command{pesign} or @command{sbsign}. Refer to the
++suggestions in @pxref{UEFI secure boot and shim} to ensure that the final
++image works under UEFI secure boot and can maintain the secure-boot chain. It
++will also be necessary to enrol the public key used into a relevant firmware
++key database.
++
+ @node Platform limitations
+ @chapter Platform limitations
+ 
diff --git a/SOURCES/0181-docs-grub-Document-signing-grub-with-an-appended-sig.patch b/SOURCES/0181-docs-grub-Document-signing-grub-with-an-appended-sig.patch
new file mode 100644
index 0000000..4d85d93
--- /dev/null
+++ b/SOURCES/0181-docs-grub-Document-signing-grub-with-an-appended-sig.patch
@@ -0,0 +1,67 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Sat, 15 Aug 2020 02:19:36 +1000
+Subject: [PATCH] docs/grub: Document signing grub with an appended signature
+
+Signing grub for firmware that verifies an appended signature is a
+bit fiddly. I don't want people to have to figure it out from scratch
+so document it here.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ docs/grub.texi | 42 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 42 insertions(+)
+
+diff --git a/docs/grub.texi b/docs/grub.texi
+index 365d1d6931b..afbde7c1f7b 100644
+--- a/docs/grub.texi
++++ b/docs/grub.texi
+@@ -6087,6 +6087,48 @@ image works under UEFI secure boot and can maintain the secure-boot chain. It
+ will also be necessary to enrol the public key used into a relevant firmware
+ key database.
+ 
++@section Signing GRUB with an appended signature
++
++The @file{core.elf} itself can be signed with a Linux kernel module-style
++appended signature.
++
++To support IEEE1275 platforms where the boot image is often loaded directly
++from a disk partition rather than from a file system, the @file{core.elf}
++can specify the size and location of the appended signature with an ELF
++note added by @command{grub-install}.
++
++An image can be signed this way using the @command{sign-file} command from
++the Linux kernel:
++
++@example
++@group
++# grub.key is your private key and certificate.der is your public key
++
++# Determine the size of the appended signature. It depends on the signing
++# certificate and the hash algorithm
++touch empty
++sign-file SHA256 grub.key certificate.der empty empty.sig
++SIG_SIZE=`stat -c '%s' empty.sig`
++rm empty empty.sig
++
++# Build a grub image with $SIG_SIZE reserved for the signature
++grub-install --appended-signature-size $SIG_SIZE --modules="..." ...
++
++# Replace the reserved size with a signature:
++# cut off the last $SIG_SIZE bytes with truncate's minus modifier
++truncate -s -$SIG_SIZE /boot/grub/powerpc-ieee1275/core.elf core.elf.unsigned
++# sign the trimmed file with an appended signature, restoring the correct size
++sign-file SHA256 grub.key certificate.der core.elf.unsigned core.elf.signed
++
++# Don't forget to install the signed image as required
++# (e.g. on powerpc-ieee1275, to the PReP partition)
++@end group
++@end example
++
++As with UEFI secure boot, it is necessary to build in the required modules,
++or sign them separately.
++
++
+ @node Platform limitations
+ @chapter Platform limitations
+ 
diff --git a/SOURCES/0182-dl-provide-a-fake-grub_dl_set_persistent-for-the-emu.patch b/SOURCES/0182-dl-provide-a-fake-grub_dl_set_persistent-for-the-emu.patch
new file mode 100644
index 0000000..6e46db0
--- /dev/null
+++ b/SOURCES/0182-dl-provide-a-fake-grub_dl_set_persistent-for-the-emu.patch
@@ -0,0 +1,44 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Thu, 30 Jul 2020 00:13:21 +1000
+Subject: [PATCH] dl: provide a fake grub_dl_set_persistent for the emu target
+
+Trying to start grub-emu with a module that calls grub_dl_set_persistent
+will crash because grub-emu fakes modules and passes NULL to the module
+init function.
+
+Provide an empty function for the emu case.
+
+Fixes: ee7808e2197c (dl: Add support for persistent modules)
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ include/grub/dl.h | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/include/grub/dl.h b/include/grub/dl.h
+index 2f76e6b0437..20d870f2a47 100644
+--- a/include/grub/dl.h
++++ b/include/grub/dl.h
+@@ -245,11 +245,22 @@ grub_dl_get (const char *name)
+   return 0;
+ }
+ 
++#ifdef GRUB_MACHINE_EMU
++/*
++ * Under grub-emu, modules are faked and NULL is passed to GRUB_MOD_INIT.
++ * So we fake this out to avoid a NULL deref.
++ */
++static inline void
++grub_dl_set_persistent (grub_dl_t mod __attribute__((unused)))
++{
++}
++#else
+ static inline void
+ grub_dl_set_persistent (grub_dl_t mod)
+ {
+   mod->persistent = 1;
+ }
++#endif
+ 
+ static inline int
+ grub_dl_is_persistent (grub_dl_t mod)
diff --git a/SOURCES/0183-pgp-factor-out-rsa_pad.patch b/SOURCES/0183-pgp-factor-out-rsa_pad.patch
new file mode 100644
index 0000000..a8154e7
--- /dev/null
+++ b/SOURCES/0183-pgp-factor-out-rsa_pad.patch
@@ -0,0 +1,191 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Thu, 1 Oct 2020 20:23:48 +1000
+Subject: [PATCH] pgp: factor out rsa_pad
+
+rsa_pad does the PKCS#1 v1.5 padding for the RSA signature scheme.
+We want to use it in other RSA signature verification applications.
+
+I considered and rejected putting it in lib/crypto.c. That file doesn't
+currently require any MPI functions, but rsa_pad does. That's not so
+much of a problem for the grub kernel and modules, but crypto.c also
+gets built into all the grub utilities. So - despite the utils not
+using any asymmetric ciphers -  we would need to built the entire MPI
+infrastructure in to them.
+
+A better and simpler solution is just to spin rsa_pad out into its own
+PKCS#1 v1.5 module.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/Makefile.core.def |  8 ++++++
+ grub-core/commands/pgp.c    | 28 ++-------------------
+ grub-core/lib/pkcs1_v15.c   | 59 +++++++++++++++++++++++++++++++++++++++++++++
+ include/grub/pkcs1_v15.h    | 27 +++++++++++++++++++++
+ 4 files changed, 96 insertions(+), 26 deletions(-)
+ create mode 100644 grub-core/lib/pkcs1_v15.c
+ create mode 100644 include/grub/pkcs1_v15.h
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 81fc274148e..97347ae76f9 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -2510,6 +2510,14 @@ module = {
+   cppflags = '$(CPPFLAGS_GCRY)';
+ };
+ 
++module = {
++  name = pkcs1_v15;
++  common = lib/pkcs1_v15.c;
++
++  cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls -Wno-sign-compare';
++  cppflags = '$(CPPFLAGS_GCRY)';
++};
++
+ module = {
+   name = all_video;
+   common = lib/fake_module.c;
+diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c
+index 5daa1e9d00c..2408db4994f 100644
+--- a/grub-core/commands/pgp.c
++++ b/grub-core/commands/pgp.c
+@@ -24,6 +24,7 @@
+ #include <grub/file.h>
+ #include <grub/command.h>
+ #include <grub/crypto.h>
++#include <grub/pkcs1_v15.h>
+ #include <grub/i18n.h>
+ #include <grub/gcrypt/gcrypt.h>
+ #include <grub/pubkey.h>
+@@ -411,32 +412,7 @@ static int
+ rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval,
+ 	 const gcry_md_spec_t *hash, struct grub_public_subkey *sk)
+ {
+-  grub_size_t tlen, emlen, fflen;
+-  grub_uint8_t *em, *emptr;
+-  unsigned nbits = gcry_mpi_get_nbits (sk->mpis[0]);
+-  int ret;
+-  tlen = hash->mdlen + hash->asnlen;
+-  emlen = (nbits + 7) / 8;
+-  if (emlen < tlen + 11)
+-    return 1;
+-
+-  em = grub_malloc (emlen);
+-  if (!em)
+-    return 1;
+-
+-  em[0] = 0x00;
+-  em[1] = 0x01;
+-  fflen = emlen - tlen - 3;
+-  for (emptr = em + 2; emptr < em + 2 + fflen; emptr++)
+-    *emptr = 0xff;
+-  *emptr++ = 0x00;
+-  grub_memcpy (emptr, hash->asnoid, hash->asnlen);
+-  emptr += hash->asnlen;
+-  grub_memcpy (emptr, hval, hash->mdlen);
+-
+-  ret = gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, em, emlen, 0);
+-  grub_free (em);
+-  return ret;
++  return grub_crypto_rsa_pad(hmpi, hval, hash, sk->mpis[0]);
+ }
+ 
+ struct grub_pubkey_context
+diff --git a/grub-core/lib/pkcs1_v15.c b/grub-core/lib/pkcs1_v15.c
+new file mode 100644
+index 00000000000..dbacd563d01
+--- /dev/null
++++ b/grub-core/lib/pkcs1_v15.c
+@@ -0,0 +1,59 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2013  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/dl.h>
++#include <grub/gcrypt/gcrypt.h>
++
++GRUB_MOD_LICENSE ("GPLv3+");
++
++/*
++ * Given a hash value 'hval', of hash specification 'hash', perform
++ * the EMSA-PKCS1-v1_5 padding suitable for a key with modulus 'mod'
++ * (see RFC 8017 s 9.2) and place the result in 'hmpi'.
++ */
++gcry_err_code_t
++grub_crypto_rsa_pad (gcry_mpi_t * hmpi, grub_uint8_t * hval,
++		     const gcry_md_spec_t * hash, gcry_mpi_t mod)
++{
++  grub_size_t tlen, emlen, fflen;
++  grub_uint8_t *em, *emptr;
++  unsigned nbits = gcry_mpi_get_nbits (mod);
++  int ret;
++  tlen = hash->mdlen + hash->asnlen;
++  emlen = (nbits + 7) / 8;
++  if (emlen < tlen + 11)
++    return GPG_ERR_TOO_SHORT;
++
++  em = grub_malloc (emlen);
++  if (!em)
++    return 1;
++
++  em[0] = 0x00;
++  em[1] = 0x01;
++  fflen = emlen - tlen - 3;
++  for (emptr = em + 2; emptr < em + 2 + fflen; emptr++)
++    *emptr = 0xff;
++  *emptr++ = 0x00;
++  grub_memcpy (emptr, hash->asnoid, hash->asnlen);
++  emptr += hash->asnlen;
++  grub_memcpy (emptr, hval, hash->mdlen);
++
++  ret = gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, em, emlen, 0);
++  grub_free (em);
++  return ret;
++}
+diff --git a/include/grub/pkcs1_v15.h b/include/grub/pkcs1_v15.h
+new file mode 100644
+index 00000000000..5c338c84a15
+--- /dev/null
++++ b/include/grub/pkcs1_v15.h
+@@ -0,0 +1,27 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2013  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++/*
++ * Given a hash value 'hval', of hash specification 'hash', perform
++ * the EMSA-PKCS1-v1_5 padding suitable for a key with modulus 'mod'
++ * (See RFC 8017 s 9.2)
++ */
++gcry_err_code_t
++grub_crypto_rsa_pad (gcry_mpi_t * hmpi, grub_uint8_t * hval,
++		     const gcry_md_spec_t * hash, gcry_mpi_t mod);
++
diff --git a/SOURCES/0184-crypto-move-storage-for-grub_crypto_pk_-to-crypto.c.patch b/SOURCES/0184-crypto-move-storage-for-grub_crypto_pk_-to-crypto.c.patch
new file mode 100644
index 0000000..763cdc6
--- /dev/null
+++ b/SOURCES/0184-crypto-move-storage-for-grub_crypto_pk_-to-crypto.c.patch
@@ -0,0 +1,71 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Fri, 2 Oct 2020 10:49:26 +1000
+Subject: [PATCH] crypto: move storage for grub_crypto_pk_* to crypto.c
+
+The way gcry_rsa and friends (the asymmetric ciphers) are loaded for the
+pgp module is a bit quirky.
+
+include/grub/crypto.h contains:
+  extern struct gcry_pk_spec *grub_crypto_pk_rsa;
+
+commands/pgp.c contains the actual storage:
+  struct gcry_pk_spec *grub_crypto_pk_rsa;
+
+And the module itself saves to the storage in pgp.c:
+  GRUB_MOD_INIT(gcry_rsa)
+  {
+    grub_crypto_pk_rsa = &_gcry_pubkey_spec_rsa;
+  }
+
+This is annoying: gcry_rsa now has a dependency on pgp!
+
+We want to be able to bring in gcry_rsa without bringing in PGP,
+so move the storage to crypto.c.
+
+Previously, gcry_rsa depended on pgp and mpi. Now it depends on
+crypto and mpi. As pgp depends on crypto, this doesn't add any new
+module dependencies using the PGP verfier.
+
+[FWIW, the story is different for the symmetric ciphers. cryptodisk
+and friends (zfs encryption etc) use grub_crypto_lookup_cipher_by_name()
+to get a cipher handle. That depends on grub_ciphers being populated
+by people calling grub_cipher_register. import_gcry.py ensures that the
+symmetric ciphers call it.]
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/commands/pgp.c | 4 ----
+ grub-core/lib/crypto.c   | 4 ++++
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c
+index 2408db4994f..355a43844ac 100644
+--- a/grub-core/commands/pgp.c
++++ b/grub-core/commands/pgp.c
+@@ -147,10 +147,6 @@ const char *hashes[] = {
+   [0x0b] = "sha224"
+ };
+ 
+-struct gcry_pk_spec *grub_crypto_pk_dsa;
+-struct gcry_pk_spec *grub_crypto_pk_ecdsa;
+-struct gcry_pk_spec *grub_crypto_pk_rsa;
+-
+ static int
+ dsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval,
+ 	 const gcry_md_spec_t *hash, struct grub_public_subkey *sk);
+diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c
+index ca334d5a40e..c578128a59d 100644
+--- a/grub-core/lib/crypto.c
++++ b/grub-core/lib/crypto.c
+@@ -121,6 +121,10 @@ grub_md_unregister (gcry_md_spec_t *cipher)
+       }
+ }
+ 
++struct gcry_pk_spec *grub_crypto_pk_dsa;
++struct gcry_pk_spec *grub_crypto_pk_ecdsa;
++struct gcry_pk_spec *grub_crypto_pk_rsa;
++
+ void
+ grub_crypto_hash (const gcry_md_spec_t *hash, void *out, const void *in,
+ 		  grub_size_t inlen)
diff --git a/SOURCES/0185-posix_wrap-tweaks-in-preparation-for-libtasn1.patch b/SOURCES/0185-posix_wrap-tweaks-in-preparation-for-libtasn1.patch
new file mode 100644
index 0000000..a09cab1
--- /dev/null
+++ b/SOURCES/0185-posix_wrap-tweaks-in-preparation-for-libtasn1.patch
@@ -0,0 +1,64 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Sat, 2 May 2020 00:27:57 +1000
+Subject: [PATCH] posix_wrap: tweaks in preparation for libtasn1
+
+ - Define SIZEOF_UNSIGNED_LONG_INT, it's the same as
+   SIZEOF_UNSIGNED_LONG.
+
+ - Define WORD_BIT, the size in bits of an int. This is a defined
+   in the Single Unix Specification and in gnulib's limits.h. gnulib
+   assumes it's 32 bits on all our platforms, including 64 bit
+   platforms, so we also use that value.
+
+ - Provide strto[u]l[l] preprocessor macros that resolve to
+   grub_strto[u]l[l]. To avoid gcrypt redefining strtoul, we
+   also define HAVE_STRTOUL here.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/lib/posix_wrap/limits.h    | 1 +
+ grub-core/lib/posix_wrap/stdlib.h    | 8 ++++++++
+ grub-core/lib/posix_wrap/sys/types.h | 1 +
+ 3 files changed, 10 insertions(+)
+
+diff --git a/grub-core/lib/posix_wrap/limits.h b/grub-core/lib/posix_wrap/limits.h
+index 7217138ffd6..591dbf3289d 100644
+--- a/grub-core/lib/posix_wrap/limits.h
++++ b/grub-core/lib/posix_wrap/limits.h
+@@ -37,5 +37,6 @@
+ #define LONG_MAX GRUB_LONG_MAX
+ 
+ #define CHAR_BIT 8
++#define WORD_BIT 32
+ 
+ #endif
+diff --git a/grub-core/lib/posix_wrap/stdlib.h b/grub-core/lib/posix_wrap/stdlib.h
+index 7a8d385e973..4634db09f29 100644
+--- a/grub-core/lib/posix_wrap/stdlib.h
++++ b/grub-core/lib/posix_wrap/stdlib.h
+@@ -58,4 +58,12 @@ abs (int c)
+   return (c >= 0) ? c : -c;
+ }
+ 
++#define strtol grub_strtol
++
++/* for libgcrypt */
++#define HAVE_STRTOUL
++#define strtoul grub_strtoul
++
++#define strtoull grub_strtoull
++
+ #endif
+diff --git a/grub-core/lib/posix_wrap/sys/types.h b/grub-core/lib/posix_wrap/sys/types.h
+index 854eb0122ef..f63412c8da0 100644
+--- a/grub-core/lib/posix_wrap/sys/types.h
++++ b/grub-core/lib/posix_wrap/sys/types.h
+@@ -51,6 +51,7 @@ typedef grub_uint8_t byte;
+ typedef grub_addr_t uintptr_t;
+ 
+ #define SIZEOF_UNSIGNED_LONG GRUB_CPU_SIZEOF_LONG
++#define SIZEOF_UNSIGNED_LONG_INT GRUB_CPU_SIZEOF_LONG
+ #define SIZEOF_UNSIGNED_INT 4
+ #define SIZEOF_UNSIGNED_LONG_LONG 8
+ #define SIZEOF_UNSIGNED_SHORT 2
diff --git a/SOURCES/0186-libtasn1-import-libtasn1-4.16.0.patch b/SOURCES/0186-libtasn1-import-libtasn1-4.16.0.patch
new file mode 100644
index 0000000..89552c8
--- /dev/null
+++ b/SOURCES/0186-libtasn1-import-libtasn1-4.16.0.patch
@@ -0,0 +1,8934 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Wed, 10 Jun 2020 16:31:22 +1000
+Subject: [PATCH] libtasn1: import libtasn1-4.16.0
+
+Import a very trimmed-down set of libtasn1 files:
+
+pushd /tmp
+wget https://ftp.gnu.org/gnu/libtasn1/libtasn1-4.16.0.tar.gz
+popd
+pushd grub-core/lib
+mkdir libtasn1
+cp /tmp/libtasn1-4.16.0/{README.md,LICENSE} libtasn1/
+mkdir libtasn1/lib
+cp /tmp/libtasn1-4.16.0/lib/{coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h}  libtasn1/lib
+cp /tmp/libtasn1-4.16.0/lib/includes/libtasn1.h ../../include/grub/
+git add libtasn1/ ../../include/grub/libtasn1.h
+popd
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/lib/libtasn1/lib/coding.c     | 1415 ++++++++++++++++++
+ grub-core/lib/libtasn1/lib/decoding.c   | 2478 +++++++++++++++++++++++++++++++
+ grub-core/lib/libtasn1/lib/element.c    | 1111 ++++++++++++++
+ grub-core/lib/libtasn1/lib/errors.c     |  100 ++
+ grub-core/lib/libtasn1/lib/gstr.c       |   74 +
+ grub-core/lib/libtasn1/lib/parser_aux.c | 1173 +++++++++++++++
+ grub-core/lib/libtasn1/lib/structure.c  | 1220 +++++++++++++++
+ grub-core/lib/libtasn1/lib/element.h    |   40 +
+ grub-core/lib/libtasn1/lib/gstr.h       |   47 +
+ grub-core/lib/libtasn1/lib/int.h        |  221 +++
+ grub-core/lib/libtasn1/lib/parser_aux.h |  172 +++
+ grub-core/lib/libtasn1/lib/structure.h  |   45 +
+ include/grub/libtasn1.h                 |  588 ++++++++
+ grub-core/lib/libtasn1/LICENSE          |   16 +
+ grub-core/lib/libtasn1/README.md        |   91 ++
+ 15 files changed, 8791 insertions(+)
+ create mode 100644 grub-core/lib/libtasn1/lib/coding.c
+ create mode 100644 grub-core/lib/libtasn1/lib/decoding.c
+ create mode 100644 grub-core/lib/libtasn1/lib/element.c
+ create mode 100644 grub-core/lib/libtasn1/lib/errors.c
+ create mode 100644 grub-core/lib/libtasn1/lib/gstr.c
+ create mode 100644 grub-core/lib/libtasn1/lib/parser_aux.c
+ create mode 100644 grub-core/lib/libtasn1/lib/structure.c
+ create mode 100644 grub-core/lib/libtasn1/lib/element.h
+ create mode 100644 grub-core/lib/libtasn1/lib/gstr.h
+ create mode 100644 grub-core/lib/libtasn1/lib/int.h
+ create mode 100644 grub-core/lib/libtasn1/lib/parser_aux.h
+ create mode 100644 grub-core/lib/libtasn1/lib/structure.h
+ create mode 100644 include/grub/libtasn1.h
+ create mode 100644 grub-core/lib/libtasn1/LICENSE
+ create mode 100644 grub-core/lib/libtasn1/README.md
+
+diff --git a/grub-core/lib/libtasn1/lib/coding.c b/grub-core/lib/libtasn1/lib/coding.c
+new file mode 100644
+index 00000000000..245ea64cf0a
+--- /dev/null
++++ b/grub-core/lib/libtasn1/lib/coding.c
+@@ -0,0 +1,1415 @@
++/*
++ * Copyright (C) 2002-2014 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * The LIBTASN1 library is free software; you can redistribute it
++ * and/or modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301, USA
++ */
++
++
++/*****************************************************/
++/* File: coding.c                                    */
++/* Description: Functions to create a DER coding of  */
++/*   an ASN1 type.                                   */
++/*****************************************************/
++
++#include <int.h>
++#include "parser_aux.h"
++#include <gstr.h>
++#include "element.h"
++#include "minmax.h"
++#include <structure.h>
++
++#define MAX_TAG_LEN 16
++
++/******************************************************/
++/* Function : _asn1_error_description_value_not_found */
++/* Description: creates the ErrorDescription string   */
++/* for the ASN1_VALUE_NOT_FOUND error.                */
++/* Parameters:                                        */
++/*   node: node of the tree where the value is NULL.  */
++/*   ErrorDescription: string returned.               */
++/* Return:                                            */
++/******************************************************/
++static void
++_asn1_error_description_value_not_found (asn1_node node,
++					 char *ErrorDescription)
++{
++
++  if (ErrorDescription == NULL)
++    return;
++
++  Estrcpy (ErrorDescription, ":: value of element '");
++  _asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription),
++			   ASN1_MAX_ERROR_DESCRIPTION_SIZE - 40);
++  Estrcat (ErrorDescription, "' not found");
++
++}
++
++/**
++ * asn1_length_der:
++ * @len: value to convert.
++ * @der: buffer to hold the returned encoding (may be %NULL).
++ * @der_len: number of meaningful bytes of ANS (der[0]..der[der_len-1]).
++ *
++ * Creates the DER encoding of the provided length value.
++ * The @der buffer must have enough room for the output. The maximum
++ * length this function will encode is %ASN1_MAX_LENGTH_SIZE.
++ *
++ * To know the size of the DER encoding use a %NULL value for @der.
++ **/
++void
++asn1_length_der (unsigned long int len, unsigned char *der, int *der_len)
++{
++  int k;
++  unsigned char temp[ASN1_MAX_LENGTH_SIZE];
++#if SIZEOF_UNSIGNED_LONG_INT > 8
++  len &= 0xFFFFFFFFFFFFFFFF;
++#endif
++
++  if (len < 128)
++    {
++      /* short form */
++      if (der != NULL)
++	der[0] = (unsigned char) len;
++      *der_len = 1;
++    }
++  else
++    {
++      /* Long form */
++      k = 0;
++      while (len)
++	{
++	  temp[k++] = len & 0xFF;
++	  len = len >> 8;
++	}
++      *der_len = k + 1;
++      if (der != NULL)
++	{
++	  der[0] = ((unsigned char) k & 0x7F) + 128;
++	  while (k--)
++	    der[*der_len - 1 - k] = temp[k];
++	}
++    }
++}
++
++/******************************************************/
++/* Function : _asn1_tag_der                           */
++/* Description: creates the DER coding for the CLASS  */
++/* and TAG parameters.                                */
++/* It is limited by the ASN1_MAX_TAG_SIZE variable    */
++/* Parameters:                                        */
++/*   class: value to convert.                         */
++/*   tag_value: value to convert.                     */
++/*   ans: string returned.                            */
++/*   ans_len: number of meaningful bytes of ANS       */
++/*            (ans[0]..ans[ans_len-1]).               */
++/* Return:                                            */
++/******************************************************/
++static void
++_asn1_tag_der (unsigned char class, unsigned int tag_value,
++	       unsigned char ans[ASN1_MAX_TAG_SIZE], int *ans_len)
++{
++  int k;
++  unsigned char temp[ASN1_MAX_TAG_SIZE];
++
++  if (tag_value < 31)
++    {
++      /* short form */
++      ans[0] = (class & 0xE0) + ((unsigned char) (tag_value & 0x1F));
++      *ans_len = 1;
++    }
++  else
++    {
++      /* Long form */
++      ans[0] = (class & 0xE0) + 31;
++      k = 0;
++      while (tag_value != 0)
++	{
++	  temp[k++] = tag_value & 0x7F;
++	  tag_value >>= 7;
++
++	  if (k > ASN1_MAX_TAG_SIZE - 1)
++	    break;		/* will not encode larger tags */
++	}
++      *ans_len = k + 1;
++      while (k--)
++	ans[*ans_len - 1 - k] = temp[k] + 128;
++      ans[*ans_len - 1] -= 128;
++    }
++}
++
++/**
++ * asn1_octet_der:
++ * @str: the input data.
++ * @str_len: STR length (str[0]..str[*str_len-1]).
++ * @der: encoded string returned.
++ * @der_len: number of meaningful bytes of DER (der[0]..der[der_len-1]).
++ *
++ * Creates a length-value DER encoding for the input data.
++ * The DER encoding of the input data will be placed in the @der variable.
++ *
++ * Note that the OCTET STRING tag is not included in the output.
++ *
++ * This function does not return any value because it is expected
++ * that @der_len will contain enough bytes to store the string
++ * plus the DER encoding. The DER encoding size can be obtained using
++ * asn1_length_der().
++ **/
++void
++asn1_octet_der (const unsigned char *str, int str_len,
++		unsigned char *der, int *der_len)
++{
++  int len_len;
++
++  if (der == NULL || str_len < 0)
++    return;
++
++  asn1_length_der (str_len, der, &len_len);
++  memcpy (der + len_len, str, str_len);
++  *der_len = str_len + len_len;
++}
++
++
++/**
++ * asn1_encode_simple_der:
++ * @etype: The type of the string to be encoded (ASN1_ETYPE_)
++ * @str: the string data.
++ * @str_len: the string length
++ * @tl: the encoded tag and length
++ * @tl_len: the bytes of the @tl field
++ *
++ * Creates the DER encoding for various simple ASN.1 types like strings etc.
++ * It stores the tag and length in @tl, which should have space for at least
++ * %ASN1_MAX_TL_SIZE bytes. Initially @tl_len should contain the size of @tl.
++ *
++ * The complete DER encoding should consist of the value in @tl appended
++ * with the provided @str.
++ *
++ * Returns: %ASN1_SUCCESS if successful or an error value.
++ **/
++int
++asn1_encode_simple_der (unsigned int etype, const unsigned char *str,
++			unsigned int str_len, unsigned char *tl,
++			unsigned int *tl_len)
++{
++  int tag_len, len_len;
++  unsigned tlen;
++  unsigned char der_tag[ASN1_MAX_TAG_SIZE];
++  unsigned char der_length[ASN1_MAX_LENGTH_SIZE];
++  unsigned char *p;
++
++  if (str == NULL)
++    return ASN1_VALUE_NOT_VALID;
++
++  if (ETYPE_OK (etype) == 0)
++    return ASN1_VALUE_NOT_VALID;
++
++  /* doesn't handle constructed classes */
++  if (ETYPE_CLASS (etype) != ASN1_CLASS_UNIVERSAL)
++    return ASN1_VALUE_NOT_VALID;
++
++  _asn1_tag_der (ETYPE_CLASS (etype), ETYPE_TAG (etype), der_tag, &tag_len);
++
++  asn1_length_der (str_len, der_length, &len_len);
++
++  if (tag_len <= 0 || len_len <= 0)
++    return ASN1_VALUE_NOT_VALID;
++
++  tlen = tag_len + len_len;
++
++  if (*tl_len < tlen)
++    return ASN1_MEM_ERROR;
++
++  p = tl;
++  memcpy (p, der_tag, tag_len);
++  p += tag_len;
++  memcpy (p, der_length, len_len);
++
++  *tl_len = tlen;
++
++  return ASN1_SUCCESS;
++}
++
++/******************************************************/
++/* Function : _asn1_time_der                          */
++/* Description: creates the DER coding for a TIME     */
++/* type (length included).                            */
++/* Parameters:                                        */
++/*   str: TIME null-terminated string.                */
++/*   der: string returned.                            */
++/*   der_len: number of meaningful bytes of DER       */
++/*            (der[0]..der[ans_len-1]). Initially it  */
++/*            if must store the lenght of DER.        */
++/* Return:                                            */
++/*   ASN1_MEM_ERROR when DER isn't big enough         */
++/*   ASN1_SUCCESS otherwise                           */
++/******************************************************/
++static int
++_asn1_time_der (unsigned char *str, int str_len, unsigned char *der,
++		int *der_len)
++{
++  int len_len;
++  int max_len;
++
++  if (der == NULL)
++    return ASN1_VALUE_NOT_VALID;
++
++  max_len = *der_len;
++
++  asn1_length_der (str_len, (max_len > 0) ? der : NULL, &len_len);
++
++  if ((len_len + str_len) <= max_len)
++    memcpy (der + len_len, str, str_len);
++  *der_len = len_len + str_len;
++
++  if ((*der_len) > max_len)
++    return ASN1_MEM_ERROR;
++
++  return ASN1_SUCCESS;
++}
++
++
++/*
++void
++_asn1_get_utctime_der(unsigned char *der,int *der_len,unsigned char *str)
++{
++  int len_len,str_len;
++  char temp[20];
++
++  if(str==NULL) return;
++  str_len=asn1_get_length_der(der,*der_len,&len_len);
++  if (str_len<0) return;
++  memcpy(temp,der+len_len,str_len);
++  *der_len=str_len+len_len;
++  switch(str_len)
++  {
++  case 11:
++    temp[10]=0;
++    strcat(temp,"00+0000");
++    break;
++  case 13:
++    temp[12]=0;
++    strcat(temp,"+0000");
++    break;
++  case 15:
++    temp[15]=0;
++    memmove(temp+12,temp+10,6);
++    temp[10]=temp[11]='0';
++    break;
++  case 17:
++    temp[17]=0;
++    break;
++  default:
++    return;
++  }
++  strcpy(str,temp);
++}
++*/
++
++static
++void encode_val(uint64_t val, unsigned char *der, int max_len, int *der_len)
++{
++  int first, k;
++  unsigned char bit7;
++
++  first = 0;
++  for (k = sizeof(val); k >= 0; k--)
++    {
++      bit7 = (val >> (k * 7)) & 0x7F;
++      if (bit7 || first || !k)
++	{
++	  if (k)
++	    bit7 |= 0x80;
++	  if (max_len > (*der_len))
++	    der[*der_len] = bit7;
++	  (*der_len)++;
++	  first = 1;
++	}
++    }
++}
++
++/******************************************************/
++/* Function : _asn1_object_id_der                     */
++/* Description: creates the DER coding for an         */
++/* OBJECT IDENTIFIER  type (length included).         */
++/* Parameters:                                        */
++/*   str: OBJECT IDENTIFIER null-terminated string.   */
++/*   der: string returned.                            */
++/*   der_len: number of meaningful bytes of DER       */
++/*            (der[0]..der[ans_len-1]). Initially it  */
++/*            must store the length of DER.           */
++/* Return:                                            */
++/*   ASN1_MEM_ERROR when DER isn't big enough         */
++/*   ASN1_SUCCESS if succesful                        */
++/*   or an error value.                               */
++/******************************************************/
++static int
++_asn1_object_id_der (const char *str, unsigned char *der, int *der_len)
++{
++  int len_len, counter, max_len;
++  char *temp, *n_end, *n_start;
++  uint64_t val, val1 = 0;
++  int str_len = _asn1_strlen (str);
++
++  max_len = *der_len;
++  *der_len = 0;
++
++  if (der == NULL && max_len > 0)
++    return ASN1_VALUE_NOT_VALID;
++
++  temp = malloc (str_len + 2);
++  if (temp == NULL)
++    return ASN1_MEM_ALLOC_ERROR;
++
++  memcpy (temp, str, str_len);
++  temp[str_len] = '.';
++  temp[str_len + 1] = 0;
++
++  counter = 0;
++  n_start = temp;
++  while ((n_end = strchr (n_start, '.')))
++    {
++      *n_end = 0;
++      val = _asn1_strtou64 (n_start, NULL, 10);
++      counter++;
++
++      if (counter == 1)
++        {
++	  val1 = val;
++	}
++      else if (counter == 2)
++	{
++	  uint64_t val0;
++
++          if (val1 > 2)
++            {
++              free(temp);
++              return ASN1_VALUE_NOT_VALID;
++            }
++          else if ((val1 == 0 || val1 == 1) && val > 39)
++            {
++              free(temp);
++              return ASN1_VALUE_NOT_VALID;
++            }
++
++	  val0 = 40 * val1 + val;
++	  encode_val(val0, der, max_len, der_len);
++	}
++      else
++	{
++	  encode_val(val, der, max_len, der_len);
++	}
++      n_start = n_end + 1;
++    }
++
++  asn1_length_der (*der_len, NULL, &len_len);
++  if (max_len >= (*der_len + len_len))
++    {
++      memmove (der + len_len, der, *der_len);
++      asn1_length_der (*der_len, der, &len_len);
++    }
++  *der_len += len_len;
++
++  free (temp);
++
++  if (max_len < (*der_len))
++    return ASN1_MEM_ERROR;
++
++  return ASN1_SUCCESS;
++}
++
++/**
++ * asn1_object_id_der:
++ * @str: An object identifier in numeric, dot format.
++ * @der: buffer to hold the returned encoding (may be %NULL).
++ * @der_len: initially the size of @der; will hold the final size.
++ * @flags: must be zero
++ *
++ * Creates the DER encoding of the provided object identifier.
++ *
++ * Returns: %ASN1_SUCCESS if DER encoding was OK, %ASN1_VALUE_NOT_VALID
++ *   if @str is not a valid OID, %ASN1_MEM_ERROR if the @der
++ *   vector isn't big enough and in this case @der_len will contain the
++ *   length needed.
++ **/
++int asn1_object_id_der(const char *str, unsigned char *der, int *der_len, unsigned flags)
++{
++  unsigned char tag_der[MAX_TAG_LEN];
++  int tag_len = 0, r;
++  int max_len = *der_len;
++
++  *der_len = 0;
++
++  _asn1_tag_der (ETYPE_CLASS (ASN1_ETYPE_OBJECT_ID), ETYPE_TAG (ASN1_ETYPE_OBJECT_ID),
++                 tag_der, &tag_len);
++
++  if (max_len > tag_len)
++    {
++      memcpy(der, tag_der, tag_len);
++    }
++  max_len -= tag_len;
++  der += tag_len;
++
++  r = _asn1_object_id_der (str, der, &max_len);
++  if (r == ASN1_MEM_ERROR || r == ASN1_SUCCESS)
++    {
++      *der_len = max_len + tag_len;
++    }
++
++  return r;
++}
++
++static const unsigned char bit_mask[] =
++  { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80 };
++
++/**
++ * asn1_bit_der:
++ * @str: BIT string.
++ * @bit_len: number of meaningful bits in STR.
++ * @der: string returned.
++ * @der_len: number of meaningful bytes of DER
++ *   (der[0]..der[ans_len-1]).
++ *
++ * Creates a length-value DER encoding for the input data
++ * as it would have been for a BIT STRING.
++ * The DER encoded data will be copied in @der.
++ *
++ * Note that the BIT STRING tag is not included in the output.
++ *
++ * This function does not return any value because it is expected
++ * that @der_len will contain enough bytes to store the string
++ * plus the DER encoding. The DER encoding size can be obtained using
++ * asn1_length_der().
++ **/
++void
++asn1_bit_der (const unsigned char *str, int bit_len,
++	      unsigned char *der, int *der_len)
++{
++  int len_len, len_byte, len_pad;
++
++  if (der == NULL)
++    return;
++
++  len_byte = bit_len >> 3;
++  len_pad = 8 - (bit_len & 7);
++  if (len_pad == 8)
++    len_pad = 0;
++  else
++    len_byte++;
++  asn1_length_der (len_byte + 1, der, &len_len);
++  der[len_len] = len_pad;
++
++  if (str)
++    memcpy (der + len_len + 1, str, len_byte);
++  der[len_len + len_byte] &= bit_mask[len_pad];
++  *der_len = len_byte + len_len + 1;
++}
++
++
++/******************************************************/
++/* Function : _asn1_complete_explicit_tag             */
++/* Description: add the length coding to the EXPLICIT */
++/* tags.                                              */
++/* Parameters:                                        */
++/*   node: pointer to the tree element.               */
++/*   der: string with the DER coding of the whole tree*/
++/*   counter: number of meaningful bytes of DER       */
++/*            (der[0]..der[*counter-1]).              */
++/*   max_len: size of der vector                      */
++/* Return:                                            */
++/*   ASN1_MEM_ERROR if der vector isn't big enough,   */
++/*   otherwise ASN1_SUCCESS.                          */
++/******************************************************/
++static int
++_asn1_complete_explicit_tag (asn1_node node, unsigned char *der,
++			     int *counter, int *max_len)
++{
++  asn1_node p;
++  int is_tag_implicit, len2, len3;
++  unsigned char temp[SIZEOF_UNSIGNED_INT];
++
++  if (der == NULL && *max_len > 0)
++    return ASN1_VALUE_NOT_VALID;
++
++  is_tag_implicit = 0;
++
++  if (node->type & CONST_TAG)
++    {
++      p = node->down;
++      if (p == NULL)
++        return ASN1_DER_ERROR;
++      /* When there are nested tags we must complete them reverse to
++         the order they were created. This is because completing a tag
++         modifies all data within it, including the incomplete tags
++         which store buffer positions -- simon@josefsson.org 2002-09-06
++       */
++      while (p->right)
++	p = p->right;
++      while (p && p != node->down->left)
++	{
++	  if (type_field (p->type) == ASN1_ETYPE_TAG)
++	    {
++	      if (p->type & CONST_EXPLICIT)
++		{
++		  len2 = strtol (p->name, NULL, 10);
++		  _asn1_set_name (p, NULL);
++
++		  asn1_length_der (*counter - len2, temp, &len3);
++		  if (len3 <= (*max_len))
++		    {
++		      memmove (der + len2 + len3, der + len2,
++			       *counter - len2);
++		      memcpy (der + len2, temp, len3);
++		    }
++		  *max_len -= len3;
++		  *counter += len3;
++		  is_tag_implicit = 0;
++		}
++	      else
++		{		/* CONST_IMPLICIT */
++		  if (!is_tag_implicit)
++		    {
++		      is_tag_implicit = 1;
++		    }
++		}
++	    }
++	  p = p->left;
++	}
++    }
++
++  if (*max_len < 0)
++    return ASN1_MEM_ERROR;
++
++  return ASN1_SUCCESS;
++}
++
++const tag_and_class_st _asn1_tags[] = {
++  [ASN1_ETYPE_GENERALSTRING] =
++    {ASN1_TAG_GENERALSTRING, ASN1_CLASS_UNIVERSAL, "type:GENERALSTRING"},
++  [ASN1_ETYPE_NUMERIC_STRING] =
++    {ASN1_TAG_NUMERIC_STRING, ASN1_CLASS_UNIVERSAL, "type:NUMERIC_STR"},
++  [ASN1_ETYPE_IA5_STRING] =
++    {ASN1_TAG_IA5_STRING, ASN1_CLASS_UNIVERSAL, "type:IA5_STR"},
++  [ASN1_ETYPE_TELETEX_STRING] =
++    {ASN1_TAG_TELETEX_STRING, ASN1_CLASS_UNIVERSAL, "type:TELETEX_STR"},
++  [ASN1_ETYPE_PRINTABLE_STRING] =
++    {ASN1_TAG_PRINTABLE_STRING, ASN1_CLASS_UNIVERSAL, "type:PRINTABLE_STR"},
++  [ASN1_ETYPE_UNIVERSAL_STRING] =
++    {ASN1_TAG_UNIVERSAL_STRING, ASN1_CLASS_UNIVERSAL, "type:UNIVERSAL_STR"},
++  [ASN1_ETYPE_BMP_STRING] =
++    {ASN1_TAG_BMP_STRING, ASN1_CLASS_UNIVERSAL, "type:BMP_STR"},
++  [ASN1_ETYPE_UTF8_STRING] =
++    {ASN1_TAG_UTF8_STRING, ASN1_CLASS_UNIVERSAL, "type:UTF8_STR"},
++  [ASN1_ETYPE_VISIBLE_STRING] =
++    {ASN1_TAG_VISIBLE_STRING, ASN1_CLASS_UNIVERSAL, "type:VISIBLE_STR"},
++  [ASN1_ETYPE_OCTET_STRING] =
++    {ASN1_TAG_OCTET_STRING, ASN1_CLASS_UNIVERSAL, "type:OCT_STR"},
++  [ASN1_ETYPE_BIT_STRING] =
++    {ASN1_TAG_BIT_STRING, ASN1_CLASS_UNIVERSAL, "type:BIT_STR"},
++  [ASN1_ETYPE_OBJECT_ID] =
++    {ASN1_TAG_OBJECT_ID, ASN1_CLASS_UNIVERSAL, "type:OBJ_ID"},
++  [ASN1_ETYPE_NULL] = {ASN1_TAG_NULL, ASN1_CLASS_UNIVERSAL, "type:NULL"},
++  [ASN1_ETYPE_BOOLEAN] =
++    {ASN1_TAG_BOOLEAN, ASN1_CLASS_UNIVERSAL, "type:BOOLEAN"},
++  [ASN1_ETYPE_INTEGER] =
++    {ASN1_TAG_INTEGER, ASN1_CLASS_UNIVERSAL, "type:INTEGER"},
++  [ASN1_ETYPE_ENUMERATED] =
++    {ASN1_TAG_ENUMERATED, ASN1_CLASS_UNIVERSAL, "type:ENUMERATED"},
++  [ASN1_ETYPE_SEQUENCE] =
++    {ASN1_TAG_SEQUENCE, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED,
++     "type:SEQUENCE"},
++  [ASN1_ETYPE_SEQUENCE_OF] =
++    {ASN1_TAG_SEQUENCE, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED,
++     "type:SEQ_OF"},
++  [ASN1_ETYPE_SET] =
++    {ASN1_TAG_SET, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, "type:SET"},
++  [ASN1_ETYPE_SET_OF] =
++    {ASN1_TAG_SET, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED,
++     "type:SET_OF"},
++  [ASN1_ETYPE_GENERALIZED_TIME] =
++    {ASN1_TAG_GENERALIZEDTime, ASN1_CLASS_UNIVERSAL, "type:GENERALIZED_TIME"},
++  [ASN1_ETYPE_UTC_TIME] =
++    {ASN1_TAG_UTCTime, ASN1_CLASS_UNIVERSAL, "type:UTC_TIME"},
++};
++
++unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]);
++
++/******************************************************/
++/* Function : _asn1_insert_tag_der                    */
++/* Description: creates the DER coding of tags of one */
++/* NODE.                                              */
++/* Parameters:                                        */
++/*   node: pointer to the tree element.               */
++/*   der: string returned                             */
++/*   counter: number of meaningful bytes of DER       */
++/*            (counter[0]..der[*counter-1]).          */
++/*   max_len: size of der vector                      */
++/* Return:                                            */
++/*   ASN1_GENERIC_ERROR if the type is unknown,       */
++/*   ASN1_MEM_ERROR if der vector isn't big enough,   */
++/*   otherwise ASN1_SUCCESS.                          */
++/******************************************************/
++static int
++_asn1_insert_tag_der (asn1_node node, unsigned char *der, int *counter,
++		      int *max_len)
++{
++  asn1_node p;
++  int tag_len, is_tag_implicit;
++  unsigned char class, class_implicit = 0, temp[MAX(SIZEOF_UNSIGNED_INT * 3 + 1, LTOSTR_MAX_SIZE)];
++  unsigned long tag_implicit = 0;
++  unsigned char tag_der[MAX_TAG_LEN];
++
++  is_tag_implicit = 0;
++
++  if (node->type & CONST_TAG)
++    {
++      p = node->down;
++      while (p)
++	{
++	  if (type_field (p->type) == ASN1_ETYPE_TAG)
++	    {
++	      if (p->type & CONST_APPLICATION)
++		class = ASN1_CLASS_APPLICATION;
++	      else if (p->type & CONST_UNIVERSAL)
++		class = ASN1_CLASS_UNIVERSAL;
++	      else if (p->type & CONST_PRIVATE)
++		class = ASN1_CLASS_PRIVATE;
++	      else
++		class = ASN1_CLASS_CONTEXT_SPECIFIC;
++
++	      if (p->type & CONST_EXPLICIT)
++		{
++		  if (is_tag_implicit)
++		    _asn1_tag_der (class_implicit, tag_implicit, tag_der,
++				   &tag_len);
++		  else
++		    _asn1_tag_der (class | ASN1_CLASS_STRUCTURED,
++				   _asn1_strtoul (p->value, NULL, 10),
++				   tag_der, &tag_len);
++
++		  *max_len -= tag_len;
++		  if (der && *max_len >= 0)
++		    memcpy (der + *counter, tag_der, tag_len);
++		  *counter += tag_len;
++
++		  _asn1_ltostr (*counter, (char *) temp);
++		  _asn1_set_name (p, (const char *) temp);
++
++		  is_tag_implicit = 0;
++		}
++	      else
++		{		/* CONST_IMPLICIT */
++		  if (!is_tag_implicit)
++		    {
++		      if ((type_field (node->type) == ASN1_ETYPE_SEQUENCE) ||
++			  (type_field (node->type) == ASN1_ETYPE_SEQUENCE_OF)
++			  || (type_field (node->type) == ASN1_ETYPE_SET)
++			  || (type_field (node->type) == ASN1_ETYPE_SET_OF))
++			class |= ASN1_CLASS_STRUCTURED;
++		      class_implicit = class;
++		      tag_implicit = _asn1_strtoul (p->value, NULL, 10);
++		      is_tag_implicit = 1;
++		    }
++		}
++	    }
++	  p = p->right;
++	}
++    }
++
++  if (is_tag_implicit)
++    {
++      _asn1_tag_der (class_implicit, tag_implicit, tag_der, &tag_len);
++    }
++  else
++    {
++      unsigned type = type_field (node->type);
++      switch (type)
++	{
++	CASE_HANDLED_ETYPES:
++	  _asn1_tag_der (_asn1_tags[type].class, _asn1_tags[type].tag,
++			 tag_der, &tag_len);
++	  break;
++	case ASN1_ETYPE_TAG:
++	case ASN1_ETYPE_CHOICE:
++	case ASN1_ETYPE_ANY:
++	  tag_len = 0;
++	  break;
++	default:
++	  return ASN1_GENERIC_ERROR;
++	}
++    }
++
++  *max_len -= tag_len;
++  if (der && *max_len >= 0)
++    memcpy (der + *counter, tag_der, tag_len);
++  *counter += tag_len;
++
++  if (*max_len < 0)
++    return ASN1_MEM_ERROR;
++
++  return ASN1_SUCCESS;
++}
++
++/******************************************************/
++/* Function : _asn1_ordering_set                      */
++/* Description: puts the elements of a SET type in    */
++/* the correct order according to DER rules.          */
++/* Parameters:                                        */
++/*   der: string with the DER coding.                 */
++/*   node: pointer to the SET element.                */
++/* Return:                                            */
++/*    ASN1_SUCCESS if successful                      */
++/*    or an error value.                              */
++/******************************************************/
++static int
++_asn1_ordering_set (unsigned char *der, int der_len, asn1_node node)
++{
++  struct vet
++  {
++    int end;
++    unsigned long value;
++    struct vet *next, *prev;
++  };
++
++  int counter, len, len2;
++  struct vet *first, *last, *p_vet, *p2_vet;
++  asn1_node p;
++  unsigned char class, *temp;
++  unsigned long tag, t;
++  int err;
++
++  counter = 0;
++
++  if (type_field (node->type) != ASN1_ETYPE_SET)
++    return ASN1_VALUE_NOT_VALID;
++
++  p = node->down;
++  while (p && ((type_field (p->type) == ASN1_ETYPE_TAG) ||
++	 (type_field (p->type) == ASN1_ETYPE_SIZE)))
++    p = p->right;
++
++  if ((p == NULL) || (p->right == NULL))
++    return ASN1_SUCCESS;
++
++  first = last = NULL;
++  while (p)
++    {
++      p_vet = malloc (sizeof (struct vet));
++      if (p_vet == NULL)
++        {
++	  err = ASN1_MEM_ALLOC_ERROR;
++	  goto error;
++	}
++
++      p_vet->next = NULL;
++      p_vet->prev = last;
++      if (first == NULL)
++	first = p_vet;
++      else
++	last->next = p_vet;
++      last = p_vet;
++
++      /* tag value calculation */
++      err = asn1_get_tag_der (der + counter, der_len - counter, &class, &len2,
++			      &tag);
++      if (err != ASN1_SUCCESS)
++	goto error;
++
++      t = ((unsigned int)class) << 24;
++      p_vet->value = t | tag;
++      counter += len2;
++
++      /* extraction and length */
++      len2 = asn1_get_length_der (der + counter, der_len - counter, &len);
++      if (len2 < 0)
++	{
++	  err = ASN1_DER_ERROR;
++	  goto error;
++	}
++      counter += len + len2;
++
++      p_vet->end = counter;
++      p = p->right;
++    }
++
++  p_vet = first;
++
++  while (p_vet)
++    {
++      p2_vet = p_vet->next;
++      counter = 0;
++      while (p2_vet)
++	{
++	  if (p_vet->value > p2_vet->value)
++	    {
++	      /* change position */
++	      temp = malloc (p_vet->end - counter);
++	      if (temp == NULL)
++		{
++		  err = ASN1_MEM_ALLOC_ERROR;
++		  goto error;
++		}
++
++	      memcpy (temp, der + counter, p_vet->end - counter);
++	      memcpy (der + counter, der + p_vet->end,
++		      p2_vet->end - p_vet->end);
++	      memcpy (der + counter + p2_vet->end - p_vet->end, temp,
++		      p_vet->end - counter);
++	      free (temp);
++
++	      tag = p_vet->value;
++	      p_vet->value = p2_vet->value;
++	      p2_vet->value = tag;
++
++	      p_vet->end = counter + (p2_vet->end - p_vet->end);
++	    }
++	  counter = p_vet->end;
++
++	  p2_vet = p2_vet->next;
++	  p_vet = p_vet->next;
++	}
++
++      if (p_vet != first)
++	p_vet->prev->next = NULL;
++      else
++	first = NULL;
++      free (p_vet);
++      p_vet = first;
++    }
++  return ASN1_SUCCESS;
++
++error:
++  while (first != NULL)
++    {
++      p_vet = first;
++      first = first->next;
++      free(p_vet);
++    }
++  return err;
++}
++
++struct vet
++{
++  unsigned char *ptr;
++  int size;
++};
++
++static int setof_compar(const void *_e1, const void *_e2)
++{
++  unsigned length;
++  const struct vet *e1 = _e1, *e2 = _e2;
++  int rval;
++
++  /* The encodings of the component values of a set-of value shall
++   * appear in ascending order, the encodings being compared
++   * as octet strings with the shorter components being
++   * padded at their trailing end with 0-octets.
++   * The padding octets are for comparison purposes and
++   * do not appear in the encodings.
++   */
++  length = MIN(e1->size, e2->size);
++
++  rval = memcmp(e1->ptr, e2->ptr, length);
++  if (rval == 0 && e1->size != e2->size)
++    {
++      if (e1->size > e2->size)
++        rval = 1;
++      else if (e2->size > e1->size)
++        rval = -1;
++    }
++
++  return rval;
++}
++
++/******************************************************/
++/* Function : _asn1_ordering_set_of                   */
++/* Description: puts the elements of a SET OF type in */
++/* the correct order according to DER rules.          */
++/* Parameters:                                        */
++/*   der: string with the DER coding.                 */
++/*   node: pointer to the SET OF element.             */
++/* Return:                                            */
++/*    ASN1_SUCCESS if successful                      */
++/*    or an error value.                              */
++/******************************************************/
++static int
++_asn1_ordering_set_of (unsigned char *der, int der_len, asn1_node node)
++{
++  int counter, len, len2;
++  struct vet *list = NULL, *tlist;
++  unsigned list_size = 0;
++  struct vet *p_vet;
++  asn1_node p;
++  unsigned char class;
++  unsigned i;
++  unsigned char *out = NULL;
++  int err;
++
++  if (der == NULL)
++    return ASN1_VALUE_NOT_VALID;
++
++  counter = 0;
++
++  if (type_field (node->type) != ASN1_ETYPE_SET_OF)
++    return ASN1_VALUE_NOT_VALID;
++
++  p = node->down;
++  while (p && ((type_field (p->type) == ASN1_ETYPE_TAG) ||
++	 (type_field (p->type) == ASN1_ETYPE_SIZE)))
++    p = p->right;
++  if (p == NULL)
++    return ASN1_VALUE_NOT_VALID;
++  p = p->right;
++
++  if ((p == NULL) || (p->right == NULL))
++    return ASN1_SUCCESS;
++
++  while (p)
++    {
++      list_size++;
++      tlist = realloc (list, list_size*sizeof(struct vet));
++      if (tlist == NULL)
++	{
++	  err = ASN1_MEM_ALLOC_ERROR;
++	  goto error;
++	}
++      list = tlist;
++      p_vet = &list[list_size-1];
++
++      p_vet->ptr = der+counter;
++      p_vet->size = 0;
++
++      /* extraction of tag and length */
++      if (der_len - counter > 0)
++	{
++	  err = asn1_get_tag_der (der + counter, der_len - counter, &class,
++	                          &len, NULL);
++	  if (err != ASN1_SUCCESS)
++	    goto error;
++	  counter += len;
++          p_vet->size += len;
++
++	  len2 = asn1_get_length_der (der + counter, der_len - counter, &len);
++	  if (len2 < 0)
++	    {
++	      err = ASN1_DER_ERROR;
++	      goto error;
++	    }
++	  counter += len + len2;
++          p_vet->size += len + len2;
++
++	}
++      else
++	{
++	  err = ASN1_DER_ERROR;
++	  goto error;
++	}
++      p = p->right;
++    }
++
++  if (counter > der_len)
++    {
++      err = ASN1_DER_ERROR;
++      goto error;
++    }
++
++  qsort(list, list_size, sizeof(struct vet), setof_compar);
++
++  out = malloc(der_len);
++  if (out == NULL)
++    {
++      err = ASN1_MEM_ERROR;
++      goto error;
++    }
++
++  /* the sum of p_vet->size == der_len */
++  counter = 0;
++  for (i = 0; i < list_size; i++)
++    {
++      p_vet = &list[i];
++      memcpy(out+counter, p_vet->ptr, p_vet->size);
++      counter += p_vet->size;
++    }
++  memcpy(der, out, der_len);
++  free(out);
++
++  err = ASN1_SUCCESS;
++
++error:
++  free(list);
++  return err;
++}
++
++/**
++ * asn1_der_coding:
++ * @element: pointer to an ASN1 element
++ * @name: the name of the structure you want to encode (it must be
++ *   inside *POINTER).
++ * @ider: vector that will contain the DER encoding. DER must be a
++ *   pointer to memory cells already allocated.
++ * @len: number of bytes of *@ider: @ider[0]..@ider[len-1], Initialy
++ *   holds the sizeof of der vector.
++ * @ErrorDescription: return the error description or an empty
++ *   string if success.
++ *
++ * Creates the DER encoding for the NAME structure (inside *POINTER
++ * structure).
++ *
++ * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
++ *   if @name is not a valid element, %ASN1_VALUE_NOT_FOUND if there
++ *   is an element without a value, %ASN1_MEM_ERROR if the @ider
++ *   vector isn't big enough and in this case @len will contain the
++ *   length needed.
++ **/
++int
++asn1_der_coding (asn1_node_const element, const char *name, void *ider, int *len,
++		 char *ErrorDescription)
++{
++  asn1_node node, p, p2;
++  unsigned char temp[MAX(LTOSTR_MAX_SIZE, SIZEOF_UNSIGNED_LONG_INT * 3 + 1)];
++  int counter, counter_old, len2, len3, move, max_len, max_len_old;
++  int err;
++  unsigned char *der = ider;
++
++  if (ErrorDescription)
++    ErrorDescription[0] = 0;
++
++  node = asn1_find_node (element, name);
++  if (node == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  /* Node is now a locally allocated variable.
++   * That is because in some point we modify the
++   * structure, and I don't know why! --nmav
++   */
++  node = _asn1_copy_structure3 (node);
++  if (node == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  max_len = *len;
++
++  if (der == NULL && max_len > 0)
++    return ASN1_VALUE_NOT_VALID;
++
++  counter = 0;
++  move = DOWN;
++  p = node;
++
++  while (1)
++    {
++
++      counter_old = counter;
++      max_len_old = max_len;
++      if (move != UP)
++	{
++          p->start = counter;
++	  err = _asn1_insert_tag_der (p, der, &counter, &max_len);
++	  if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR)
++	    goto error;
++	}
++      switch (type_field (p->type))
++	{
++	case ASN1_ETYPE_NULL:
++	  max_len--;
++	  if (der != NULL && max_len >= 0)
++	    der[counter] = 0;
++	  counter++;
++	  move = RIGHT;
++	  break;
++	case ASN1_ETYPE_BOOLEAN:
++	  if ((p->type & CONST_DEFAULT) && (p->value == NULL))
++	    {
++	      counter = counter_old;
++	      max_len = max_len_old;
++	    }
++	  else
++	    {
++	      if (p->value == NULL)
++		{
++		  _asn1_error_description_value_not_found (p,
++							   ErrorDescription);
++		  err = ASN1_VALUE_NOT_FOUND;
++		  goto error;
++		}
++	      max_len -= 2;
++	      if (der != NULL && max_len >= 0)
++		{
++		  der[counter++] = 1;
++		  if (p->value[0] == 'F')
++		    der[counter++] = 0;
++		  else
++		    der[counter++] = 0xFF;
++		}
++	      else
++		counter += 2;
++	    }
++	  move = RIGHT;
++	  break;
++	case ASN1_ETYPE_INTEGER:
++	case ASN1_ETYPE_ENUMERATED:
++	  if ((p->type & CONST_DEFAULT) && (p->value == NULL))
++	    {
++	      counter = counter_old;
++	      max_len = max_len_old;
++	    }
++	  else
++	    {
++	      if (p->value == NULL)
++		{
++		  _asn1_error_description_value_not_found (p,
++							   ErrorDescription);
++		  err = ASN1_VALUE_NOT_FOUND;
++		  goto error;
++		}
++	      len2 = asn1_get_length_der (p->value, p->value_len, &len3);
++	      if (len2 < 0)
++		{
++		  err = ASN1_DER_ERROR;
++		  goto error;
++		}
++	      max_len -= len2 + len3;
++	      if (der != NULL && max_len >= 0)
++		memcpy (der + counter, p->value, len3 + len2);
++	      counter += len3 + len2;
++	    }
++	  move = RIGHT;
++	  break;
++	case ASN1_ETYPE_OBJECT_ID:
++	  if ((p->type & CONST_DEFAULT) && (p->value == NULL))
++	    {
++	      counter = counter_old;
++	      max_len = max_len_old;
++	    }
++	  else
++	    {
++	      if (p->value == NULL)
++		{
++		  _asn1_error_description_value_not_found (p,
++							   ErrorDescription);
++		  err = ASN1_VALUE_NOT_FOUND;
++		  goto error;
++		}
++	      len2 = max_len;
++	      err = _asn1_object_id_der ((char*)p->value, der + counter, &len2);
++	      if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR)
++		goto error;
++
++	      max_len -= len2;
++	      counter += len2;
++	    }
++	  move = RIGHT;
++	  break;
++	case ASN1_ETYPE_GENERALIZED_TIME:
++	case ASN1_ETYPE_UTC_TIME:
++	  if (p->value == NULL)
++	    {
++	      _asn1_error_description_value_not_found (p, ErrorDescription);
++	      err = ASN1_VALUE_NOT_FOUND;
++	      goto error;
++	    }
++	  len2 = max_len;
++	  err = _asn1_time_der (p->value, p->value_len, der + counter, &len2);
++	  if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR)
++	    goto error;
++
++	  max_len -= len2;
++	  counter += len2;
++	  move = RIGHT;
++	  break;
++	case ASN1_ETYPE_OCTET_STRING:
++	case ASN1_ETYPE_GENERALSTRING:
++	case ASN1_ETYPE_NUMERIC_STRING:
++	case ASN1_ETYPE_IA5_STRING:
++	case ASN1_ETYPE_TELETEX_STRING:
++	case ASN1_ETYPE_PRINTABLE_STRING:
++	case ASN1_ETYPE_UNIVERSAL_STRING:
++	case ASN1_ETYPE_BMP_STRING:
++	case ASN1_ETYPE_UTF8_STRING:
++	case ASN1_ETYPE_VISIBLE_STRING:
++	case ASN1_ETYPE_BIT_STRING:
++	  if (p->value == NULL)
++	    {
++	      _asn1_error_description_value_not_found (p, ErrorDescription);
++	      err = ASN1_VALUE_NOT_FOUND;
++	      goto error;
++	    }
++	  len2 = asn1_get_length_der (p->value, p->value_len, &len3);
++	  if (len2 < 0)
++	    {
++	      err = ASN1_DER_ERROR;
++	      goto error;
++	    }
++	  max_len -= len2 + len3;
++	  if (der != NULL && max_len >= 0)
++	    memcpy (der + counter, p->value, len3 + len2);
++	  counter += len3 + len2;
++	  move = RIGHT;
++	  break;
++	case ASN1_ETYPE_SEQUENCE:
++	case ASN1_ETYPE_SET:
++	  if (move != UP)
++	    {
++	      p->tmp_ival = counter;
++	      if (p->down == NULL)
++		{
++		  move = UP;
++		  continue;
++		}
++	      else
++		{
++		  p2 = p->down;
++		  while (p2 && (type_field (p2->type) == ASN1_ETYPE_TAG))
++		    p2 = p2->right;
++		  if (p2)
++		    {
++		      p = p2;
++		      move = RIGHT;
++		      continue;
++		    }
++		  move = UP;
++		  continue;
++		}
++	    }
++	  else
++	    {			/* move==UP */
++	      len2 = p->tmp_ival;
++	      p->tmp_ival = 0;
++	      if ((type_field (p->type) == ASN1_ETYPE_SET) && (max_len >= 0))
++		{
++		  err = _asn1_ordering_set (der + len2, counter - len2, p);
++		  if (err != ASN1_SUCCESS)
++		    goto error;
++		}
++	      asn1_length_der (counter - len2, temp, &len3);
++	      max_len -= len3;
++	      if (der != NULL && max_len >= 0)
++		{
++		  memmove (der + len2 + len3, der + len2, counter - len2);
++		  memcpy (der + len2, temp, len3);
++		}
++	      counter += len3;
++	      move = RIGHT;
++	    }
++	  break;
++	case ASN1_ETYPE_SEQUENCE_OF:
++	case ASN1_ETYPE_SET_OF:
++	  if (move != UP)
++	    {
++	      p->tmp_ival = counter;
++	      p = p->down;
++	      while ((type_field (p->type) == ASN1_ETYPE_TAG)
++		     || (type_field (p->type) == ASN1_ETYPE_SIZE))
++		p = p->right;
++	      if (p->right)
++		{
++		  p = p->right;
++		  move = RIGHT;
++		  continue;
++		}
++	      else
++		p = _asn1_find_up (p);
++	      move = UP;
++	    }
++	  if (move == UP)
++	    {
++	      len2 = p->tmp_ival;
++	      p->tmp_ival = 0;
++	      if ((type_field (p->type) == ASN1_ETYPE_SET_OF)
++		  && (counter - len2 > 0) && (max_len >= 0))
++		{
++		  err = _asn1_ordering_set_of (der + len2, counter - len2, p);
++		  if (err != ASN1_SUCCESS)
++		    goto error;
++		}
++	      asn1_length_der (counter - len2, temp, &len3);
++	      max_len -= len3;
++	      if (der != NULL && max_len >= 0)
++		{
++		  memmove (der + len2 + len3, der + len2, counter - len2);
++		  memcpy (der + len2, temp, len3);
++		}
++	      counter += len3;
++	      move = RIGHT;
++	    }
++	  break;
++	case ASN1_ETYPE_ANY:
++	  if (p->value == NULL)
++	    {
++	      _asn1_error_description_value_not_found (p, ErrorDescription);
++	      err = ASN1_VALUE_NOT_FOUND;
++	      goto error;
++	    }
++	  len2 = asn1_get_length_der (p->value, p->value_len, &len3);
++	  if (len2 < 0)
++	    {
++	      err = ASN1_DER_ERROR;
++	      goto error;
++	    }
++	  max_len -= len2;
++	  if (der != NULL && max_len >= 0)
++	    memcpy (der + counter, p->value + len3, len2);
++	  counter += len2;
++	  move = RIGHT;
++	  break;
++	default:
++	  move = (move == UP) ? RIGHT : DOWN;
++	  break;
++	}
++
++      if ((move != DOWN) && (counter != counter_old))
++	{
++          p->end = counter - 1;
++	  err = _asn1_complete_explicit_tag (p, der, &counter, &max_len);
++	  if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR)
++	    goto error;
++	}
++
++      if (p == node && move != DOWN)
++	break;
++
++      if (move == DOWN)
++	{
++	  if (p->down)
++	    p = p->down;
++	  else
++	    move = RIGHT;
++	}
++      if (move == RIGHT)
++	{
++	  if (p->right)
++	    p = p->right;
++	  else
++	    move = UP;
++	}
++      if (move == UP)
++	p = _asn1_find_up (p);
++    }
++
++  *len = counter;
++
++  if (max_len < 0)
++    {
++      err = ASN1_MEM_ERROR;
++      goto error;
++    }
++
++  err = ASN1_SUCCESS;
++
++error:
++  asn1_delete_structure (&node);
++  return err;
++}
+diff --git a/grub-core/lib/libtasn1/lib/decoding.c b/grub-core/lib/libtasn1/lib/decoding.c
+new file mode 100644
+index 00000000000..ff04eb778cb
+--- /dev/null
++++ b/grub-core/lib/libtasn1/lib/decoding.c
+@@ -0,0 +1,2478 @@
++/*
++ * Copyright (C) 2002-2016 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * The LIBTASN1 library is free software; you can redistribute it
++ * and/or modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301, USA
++ */
++
++
++/*****************************************************/
++/* File: decoding.c                                  */
++/* Description: Functions to manage DER decoding     */
++/*****************************************************/
++
++#include <int.h>
++#include <parser_aux.h>
++#include <gstr.h>
++#include <structure.h>
++#include <element.h>
++#include <limits.h>
++#include <intprops.h>
++#include <c-ctype.h>
++
++#ifdef DEBUG
++# define warn() fprintf(stderr, "%s: %d\n", __func__, __LINE__)
++#else
++# define warn()
++#endif
++
++#define IS_ERR(len, flags) (len < -1 || ((flags & ASN1_DECODE_FLAG_STRICT_DER) && len < 0))
++
++#define HAVE_TWO(x) (x>=2?1:0)
++
++/* Decoding flags (dflags) used in several decoding functions.
++ *  DECODE_FLAG_HAVE_TAG: The provided buffer includes a tag
++ *  DECODE_FLAG_CONSTRUCTED: The provided buffer is of indefinite encoding (useful
++ *                           when no tags are present).
++ *  DECODE_FLAG_LEVEL1: Internal flag to indicate a level of recursion for BER strings.
++ *  DECODE_FLAG_LEVEL2: Internal flag to indicate two levels of recursion for BER strings.
++ *  DECODE_FLAG_LEVEL3: Internal flag to indicate three levels of recursion for BER strings.
++ *                      This is the maximum levels of recursion possible to prevent stack
++ *                      exhaustion.
++ */
++
++#define DECODE_FLAG_HAVE_TAG 1
++#define DECODE_FLAG_CONSTRUCTED (1<<1)
++#define DECODE_FLAG_LEVEL1 (1<<2)
++#define DECODE_FLAG_LEVEL2 (1<<3)
++#define DECODE_FLAG_LEVEL3 (1<<4)
++
++#define DECR_LEN(l, s) do { \
++	  l -= s; \
++	  if (l < 0) { \
++	    warn(); \
++	    result = ASN1_DER_ERROR; \
++	    goto cleanup; \
++	  } \
++	} while (0)
++
++static int
++_asn1_get_indefinite_length_string (const unsigned char *der, int der_len, int *len);
++
++static int
++_asn1_decode_simple_ber (unsigned int etype, const unsigned char *der,
++			unsigned int _der_len, unsigned char **str,
++			unsigned int *str_len, unsigned int *ber_len,
++			unsigned dflags);
++
++static int
++_asn1_decode_simple_der (unsigned int etype, const unsigned char *der,
++			unsigned int _der_len, const unsigned char **str,
++			unsigned int *str_len, unsigned dflags);
++
++static void
++_asn1_error_description_tag_error (asn1_node node, char *ErrorDescription)
++{
++
++  Estrcpy (ErrorDescription, ":: tag error near element '");
++  _asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription),
++			   ASN1_MAX_ERROR_DESCRIPTION_SIZE - 40);
++  Estrcat (ErrorDescription, "'");
++
++}
++
++/**
++ * asn1_get_length_der:
++ * @der: DER data to decode.
++ * @der_len: Length of DER data to decode.
++ * @len: Output variable containing the length of the DER length field.
++ *
++ * Extract a length field from DER data.
++ *
++ * Returns: Return the decoded length value, or -1 on indefinite
++ *   length, or -2 when the value was too big to fit in a int, or -4
++ *   when the decoded length value plus @len would exceed @der_len.
++ **/
++long
++asn1_get_length_der (const unsigned char *der, int der_len, int *len)
++{
++  unsigned int ans;
++  int k, punt, sum;
++
++  *len = 0;
++  if (der_len <= 0)
++    return 0;
++
++  if (!(der[0] & 128))
++    {
++      /* short form */
++      *len = 1;
++      ans = der[0];
++    }
++  else
++    {
++      /* Long form */
++      k = der[0] & 0x7F;
++      punt = 1;
++      if (k)
++	{ /* definite length method */
++	  ans = 0;
++	  while (punt <= k && punt < der_len)
++	    {
++	      if (INT_MULTIPLY_OVERFLOW (ans, 256))
++		return -2;
++	      ans *= 256;
++
++	      if (INT_ADD_OVERFLOW (ans, ((unsigned) der[punt])))
++		return -2;
++	      ans += der[punt];
++	      punt++;
++	    }
++	}
++      else
++	{			/* indefinite length method */
++	  *len = punt;
++	  return -1;
++	}
++
++      *len = punt;
++    }
++
++  sum = ans;
++  if (ans >= INT_MAX || INT_ADD_OVERFLOW (sum, (*len)))
++    return -2;
++  sum += *len;
++
++  if (sum > der_len)
++    return -4;
++
++  return ans;
++}
++
++/**
++ * asn1_get_tag_der:
++ * @der: DER data to decode.
++ * @der_len: Length of DER data to decode.
++ * @cls: Output variable containing decoded class.
++ * @len: Output variable containing the length of the DER TAG data.
++ * @tag: Output variable containing the decoded tag (may be %NULL).
++ *
++ * Decode the class and TAG from DER code.
++ *
++ * Returns: Returns %ASN1_SUCCESS on success, or an error.
++ **/
++int
++asn1_get_tag_der (const unsigned char *der, int der_len,
++		  unsigned char *cls, int *len, unsigned long *tag)
++{
++  unsigned int ris;
++  int punt;
++
++  if (der == NULL || der_len < 2 || len == NULL)
++    return ASN1_DER_ERROR;
++
++  *cls = der[0] & 0xE0;
++  if ((der[0] & 0x1F) != 0x1F)
++    {
++      /* short form */
++      *len = 1;
++      ris = der[0] & 0x1F;
++    }
++  else
++    {
++      /* Long form */
++      punt = 1;
++      ris = 0;
++      while (punt < der_len && der[punt] & 128)
++	{
++
++	  if (INT_MULTIPLY_OVERFLOW (ris, 128))
++	    return ASN1_DER_ERROR;
++	  ris *= 128;
++
++	  if (INT_ADD_OVERFLOW (ris, ((unsigned) (der[punt] & 0x7F))))
++	    return ASN1_DER_ERROR;
++	  ris += (der[punt] & 0x7F);
++	  punt++;
++	}
++
++      if (punt >= der_len)
++	return ASN1_DER_ERROR;
++
++      if (INT_MULTIPLY_OVERFLOW (ris, 128))
++	return ASN1_DER_ERROR;
++      ris *= 128;
++
++      if (INT_ADD_OVERFLOW (ris, ((unsigned) (der[punt] & 0x7F))))
++	return ASN1_DER_ERROR;
++      ris += (der[punt] & 0x7F);
++      punt++;
++
++      *len = punt;
++    }
++
++  if (tag)
++    *tag = ris;
++  return ASN1_SUCCESS;
++}
++
++/**
++ * asn1_get_length_ber:
++ * @ber: BER data to decode.
++ * @ber_len: Length of BER data to decode.
++ * @len: Output variable containing the length of the BER length field.
++ *
++ * Extract a length field from BER data.  The difference to
++ * asn1_get_length_der() is that this function will return a length
++ * even if the value has indefinite encoding.
++ *
++ * Returns: Return the decoded length value, or negative value when
++ *   the value was too big.
++ *
++ * Since: 2.0
++ **/
++long
++asn1_get_length_ber (const unsigned char *ber, int ber_len, int *len)
++{
++  int ret;
++  long err;
++
++  ret = asn1_get_length_der (ber, ber_len, len);
++
++  if (ret == -1 && ber_len > 1)
++    {				/* indefinite length method */
++      err = _asn1_get_indefinite_length_string (ber + 1, ber_len-1, &ret);
++      if (err != ASN1_SUCCESS)
++	return -3;
++    }
++
++  return ret;
++}
++
++/**
++ * asn1_get_octet_der:
++ * @der: DER data to decode containing the OCTET SEQUENCE.
++ * @der_len: The length of the @der data to decode.
++ * @ret_len: Output variable containing the encoded length of the DER data.
++ * @str: Pre-allocated output buffer to put decoded OCTET SEQUENCE in.
++ * @str_size: Length of pre-allocated output buffer.
++ * @str_len: Output variable containing the length of the contents of the OCTET SEQUENCE.
++ *
++ * Extract an OCTET SEQUENCE from DER data. Note that this function
++ * expects the DER data past the tag field, i.e., the length and
++ * content octets.
++ *
++ * Returns: Returns %ASN1_SUCCESS on success, or an error.
++ **/
++int
++asn1_get_octet_der (const unsigned char *der, int der_len,
++		    int *ret_len, unsigned char *str, int str_size,
++		    int *str_len)
++{
++  int len_len = 0;
++
++  if (der_len <= 0)
++    return ASN1_GENERIC_ERROR;
++
++  *str_len = asn1_get_length_der (der, der_len, &len_len);
++
++  if (*str_len < 0)
++    return ASN1_DER_ERROR;
++
++  *ret_len = *str_len + len_len;
++  if (str_size >= *str_len)
++    {
++      if (*str_len > 0 && str != NULL)
++        memcpy (str, der + len_len, *str_len);
++    }
++  else
++    {
++      return ASN1_MEM_ERROR;
++    }
++
++  return ASN1_SUCCESS;
++}
++
++
++/*-
++ * _asn1_get_time_der:
++ * @type: %ASN1_ETYPE_GENERALIZED_TIME or %ASN1_ETYPE_UTC_TIME
++ * @der: DER data to decode containing the time
++ * @der_len: Length of DER data to decode.
++ * @ret_len: Output variable containing the length of the DER data.
++ * @str: Pre-allocated output buffer to put the textual time in.
++ * @str_size: Length of pre-allocated output buffer.
++ * @flags: Zero or %ASN1_DECODE_FLAG_STRICT_DER
++ *
++ * Performs basic checks in the DER encoded time object and returns its textual form.
++ * The textual form will be in the YYYYMMDD000000Z format for GeneralizedTime
++ * and YYMMDD000000Z for UTCTime.
++ *
++ * Returns: %ASN1_SUCCESS on success, or an error.
++ -*/
++static int
++_asn1_get_time_der (unsigned type, const unsigned char *der, int der_len, int *ret_len,
++		    char *str, int str_size, unsigned flags)
++{
++  int len_len, str_len;
++  unsigned i;
++  unsigned sign_count = 0;
++  unsigned dot_count = 0;
++  const unsigned char *p;
++
++  if (der_len <= 0 || str == NULL)
++    return ASN1_DER_ERROR;
++
++  str_len = asn1_get_length_der (der, der_len, &len_len);
++  if (str_len <= 0 || str_size < str_len)
++    return ASN1_DER_ERROR;
++
++  /* perform some sanity checks on the data */
++  if (str_len < 8)
++    {
++      warn();
++      return ASN1_TIME_ENCODING_ERROR;
++    }
++
++  if ((flags & ASN1_DECODE_FLAG_STRICT_DER) && !(flags & ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME))
++    {
++      p = &der[len_len];
++      for (i=0;i<(unsigned)(str_len-1);i++)
++         {
++           if (c_isdigit(p[i]) == 0)
++             {
++               if (type == ASN1_ETYPE_GENERALIZED_TIME)
++                 {
++                   /* tolerate lax encodings */
++                   if (p[i] == '.' && dot_count == 0)
++                     {
++                       dot_count++;
++                       continue;
++                     }
++
++               /* This is not really valid DER, but there are
++                * structures using that */
++                   if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) &&
++                       (p[i] == '+' || p[i] == '-') && sign_count == 0)
++                     {
++                       sign_count++;
++                       continue;
++                     }
++                 }
++
++               warn();
++               return ASN1_TIME_ENCODING_ERROR;
++             }
++         }
++
++      if (sign_count == 0 && p[str_len-1] != 'Z')
++        {
++          warn();
++          return ASN1_TIME_ENCODING_ERROR;
++        }
++    }
++  memcpy (str, der + len_len, str_len);
++  str[str_len] = 0;
++  *ret_len = str_len + len_len;
++
++  return ASN1_SUCCESS;
++}
++
++/**
++ * asn1_get_object_id_der:
++ * @der: DER data to decode containing the OBJECT IDENTIFIER
++ * @der_len: Length of DER data to decode.
++ * @ret_len: Output variable containing the length of the DER data.
++ * @str: Pre-allocated output buffer to put the textual object id in.
++ * @str_size: Length of pre-allocated output buffer.
++ *
++ * Converts a DER encoded object identifier to its textual form. This
++ * function expects the DER object identifier without the tag.
++ *
++ * Returns: %ASN1_SUCCESS on success, or an error.
++ **/
++int
++asn1_get_object_id_der (const unsigned char *der, int der_len, int *ret_len,
++			char *str, int str_size)
++{
++  int len_len, len, k;
++  int leading, parsed;
++  char temp[LTOSTR_MAX_SIZE];
++  uint64_t val, val1, val0;
++
++  *ret_len = 0;
++  if (str && str_size > 0)
++    str[0] = 0;			/* no oid */
++
++  if (str == NULL || der_len <= 0)
++    return ASN1_GENERIC_ERROR;
++
++  len = asn1_get_length_der (der, der_len, &len_len);
++
++  if (len <= 0 || len + len_len > der_len)
++    return ASN1_DER_ERROR;
++
++  /* leading octet can never be 0x80 */
++  if (der[len_len] == 0x80)
++    return ASN1_DER_ERROR;
++
++  val0 = 0;
++
++  for (k = 0; k < len; k++)
++    {
++      if (INT_LEFT_SHIFT_OVERFLOW (val0, 7))
++	return ASN1_DER_ERROR;
++
++      val0 <<= 7;
++      val0 |= der[len_len + k] & 0x7F;
++      if (!(der[len_len + k] & 0x80))
++	break;
++    }
++  parsed = ++k;
++
++  /* val0 = (X*40) + Y, X={0,1,2}, Y<=39 when X={0,1} */
++  /* X = val, Y = val1 */
++
++  /* check if X == 0  */
++  val = 0;
++  val1 = val0;
++  if (val1 > 39)
++    {
++      val = 1;
++      val1 = val0 - 40;
++      if (val1  > 39)
++        {
++          val = 2;
++          val1 = val0 - 80;
++        }
++    }
++
++  _asn1_str_cpy (str, str_size, _asn1_ltostr (val, temp));
++  _asn1_str_cat (str, str_size, ".");
++  _asn1_str_cat (str, str_size, _asn1_ltostr (val1, temp));
++
++  val = 0;
++  leading = 1;
++  for (k = parsed; k < len; k++)
++    {
++      /* X.690 mandates that the leading byte must never be 0x80
++       */
++      if (leading != 0 && der[len_len + k] == 0x80)
++	return ASN1_DER_ERROR;
++      leading = 0;
++
++      /* check for wrap around */
++      if (INT_LEFT_SHIFT_OVERFLOW (val, 7))
++	return ASN1_DER_ERROR;
++
++      val = val << 7;
++      val |= der[len_len + k] & 0x7F;
++
++      if (!(der[len_len + k] & 0x80))
++	{
++	  _asn1_str_cat (str, str_size, ".");
++	  _asn1_str_cat (str, str_size, _asn1_ltostr (val, temp));
++	  val = 0;
++	  leading = 1;
++	}
++    }
++
++  if (INT_ADD_OVERFLOW (len, len_len))
++    return ASN1_DER_ERROR;
++
++  *ret_len = len + len_len;
++
++  return ASN1_SUCCESS;
++}
++
++/**
++ * asn1_get_bit_der:
++ * @der: DER data to decode containing the BIT SEQUENCE.
++ * @der_len: Length of DER data to decode.
++ * @ret_len: Output variable containing the length of the DER data.
++ * @str: Pre-allocated output buffer to put decoded BIT SEQUENCE in.
++ * @str_size: Length of pre-allocated output buffer.
++ * @bit_len: Output variable containing the size of the BIT SEQUENCE.
++ *
++ * Extract a BIT SEQUENCE from DER data.
++ *
++ * Returns: %ASN1_SUCCESS on success, or an error.
++ **/
++int
++asn1_get_bit_der (const unsigned char *der, int der_len,
++		  int *ret_len, unsigned char *str, int str_size,
++		  int *bit_len)
++{
++  int len_len = 0, len_byte;
++
++  if (der_len <= 0)
++    return ASN1_GENERIC_ERROR;
++
++  len_byte = asn1_get_length_der (der, der_len, &len_len) - 1;
++  if (len_byte < 0)
++    return ASN1_DER_ERROR;
++
++  *ret_len = len_byte + len_len + 1;
++  *bit_len = len_byte * 8 - der[len_len];
++
++  if (*bit_len < 0)
++    return ASN1_DER_ERROR;
++
++  if (str_size >= len_byte)
++    {
++      if (len_byte > 0 && str)
++        memcpy (str, der + len_len + 1, len_byte);
++    }
++  else
++    {
++      return ASN1_MEM_ERROR;
++    }
++
++  return ASN1_SUCCESS;
++}
++
++/* tag_len: the total tag length (explicit+inner)
++ * inner_tag_len: the inner_tag length
++ */
++static int
++_asn1_extract_tag_der (asn1_node node, const unsigned char *der, int der_len,
++		       int *tag_len, int *inner_tag_len, unsigned flags)
++{
++  asn1_node p;
++  int counter, len2, len3, is_tag_implicit;
++  int result;
++  unsigned long tag, tag_implicit = 0;
++  unsigned char class, class2, class_implicit = 0;
++
++  if (der_len <= 0)
++    return ASN1_GENERIC_ERROR;
++
++  counter = is_tag_implicit = 0;
++
++  if (node->type & CONST_TAG)
++    {
++      p = node->down;
++      while (p)
++	{
++	  if (type_field (p->type) == ASN1_ETYPE_TAG)
++	    {
++	      if (p->type & CONST_APPLICATION)
++		class2 = ASN1_CLASS_APPLICATION;
++	      else if (p->type & CONST_UNIVERSAL)
++		class2 = ASN1_CLASS_UNIVERSAL;
++	      else if (p->type & CONST_PRIVATE)
++		class2 = ASN1_CLASS_PRIVATE;
++	      else
++		class2 = ASN1_CLASS_CONTEXT_SPECIFIC;
++
++	      if (p->type & CONST_EXPLICIT)
++		{
++		  if (asn1_get_tag_der
++		      (der + counter, der_len, &class, &len2,
++		       &tag) != ASN1_SUCCESS)
++		    return ASN1_DER_ERROR;
++
++                  DECR_LEN(der_len, len2);
++		  counter += len2;
++
++		  if (flags & ASN1_DECODE_FLAG_STRICT_DER)
++		    len3 =
++		      asn1_get_length_der (der + counter, der_len,
++					 &len2);
++		  else
++		    len3 =
++		      asn1_get_length_ber (der + counter, der_len,
++					 &len2);
++		  if (len3 < 0)
++		    return ASN1_DER_ERROR;
++
++                  DECR_LEN(der_len, len2);
++		  counter += len2;
++
++		  if (!is_tag_implicit)
++		    {
++		      if ((class != (class2 | ASN1_CLASS_STRUCTURED)) ||
++			  (tag != strtoul ((char *) p->value, NULL, 10)))
++			return ASN1_TAG_ERROR;
++		    }
++		  else
++		    {		/* ASN1_TAG_IMPLICIT */
++		      if ((class != class_implicit) || (tag != tag_implicit))
++			return ASN1_TAG_ERROR;
++		    }
++		  is_tag_implicit = 0;
++		}
++	      else
++		{		/* ASN1_TAG_IMPLICIT */
++		  if (!is_tag_implicit)
++		    {
++		      if ((type_field (node->type) == ASN1_ETYPE_SEQUENCE) ||
++			  (type_field (node->type) == ASN1_ETYPE_SEQUENCE_OF)
++			  || (type_field (node->type) == ASN1_ETYPE_SET)
++			  || (type_field (node->type) == ASN1_ETYPE_SET_OF))
++			class2 |= ASN1_CLASS_STRUCTURED;
++		      class_implicit = class2;
++		      tag_implicit = strtoul ((char *) p->value, NULL, 10);
++		      is_tag_implicit = 1;
++		    }
++		}
++	    }
++	  p = p->right;
++	}
++    }
++
++  if (is_tag_implicit)
++    {
++      if (asn1_get_tag_der
++	  (der + counter, der_len, &class, &len2,
++	   &tag) != ASN1_SUCCESS)
++	return ASN1_DER_ERROR;
++
++      DECR_LEN(der_len, len2);
++
++      if ((class != class_implicit) || (tag != tag_implicit))
++	{
++	  if (type_field (node->type) == ASN1_ETYPE_OCTET_STRING)
++	    {
++	      class_implicit |= ASN1_CLASS_STRUCTURED;
++	      if ((class != class_implicit) || (tag != tag_implicit))
++		return ASN1_TAG_ERROR;
++	    }
++	  else
++	    return ASN1_TAG_ERROR;
++	}
++    }
++  else
++    {
++      unsigned type = type_field (node->type);
++      if (type == ASN1_ETYPE_TAG)
++	{
++	  *tag_len = 0;
++	  if (inner_tag_len)
++	    *inner_tag_len = 0;
++	  return ASN1_SUCCESS;
++	}
++
++      if (asn1_get_tag_der
++	  (der + counter, der_len, &class, &len2,
++	   &tag) != ASN1_SUCCESS)
++	return ASN1_DER_ERROR;
++
++      DECR_LEN(der_len, len2);
++
++      switch (type)
++	{
++	case ASN1_ETYPE_NULL:
++	case ASN1_ETYPE_BOOLEAN:
++	case ASN1_ETYPE_INTEGER:
++	case ASN1_ETYPE_ENUMERATED:
++	case ASN1_ETYPE_OBJECT_ID:
++	case ASN1_ETYPE_GENERALSTRING:
++	case ASN1_ETYPE_NUMERIC_STRING:
++	case ASN1_ETYPE_IA5_STRING:
++	case ASN1_ETYPE_TELETEX_STRING:
++	case ASN1_ETYPE_PRINTABLE_STRING:
++	case ASN1_ETYPE_UNIVERSAL_STRING:
++	case ASN1_ETYPE_BMP_STRING:
++	case ASN1_ETYPE_UTF8_STRING:
++	case ASN1_ETYPE_VISIBLE_STRING:
++	case ASN1_ETYPE_BIT_STRING:
++	case ASN1_ETYPE_SEQUENCE:
++	case ASN1_ETYPE_SEQUENCE_OF:
++	case ASN1_ETYPE_SET:
++	case ASN1_ETYPE_SET_OF:
++	case ASN1_ETYPE_GENERALIZED_TIME:
++	case ASN1_ETYPE_UTC_TIME:
++	  if ((class != _asn1_tags[type].class)
++	      || (tag != _asn1_tags[type].tag))
++	    return ASN1_DER_ERROR;
++	  break;
++
++	case ASN1_ETYPE_OCTET_STRING:
++	  /* OCTET STRING is handled differently to allow
++	   * BER encodings (structured class). */
++	  if (((class != ASN1_CLASS_UNIVERSAL)
++	       && (class != (ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED)))
++	      || (tag != ASN1_TAG_OCTET_STRING))
++	    return ASN1_DER_ERROR;
++	  break;
++	case ASN1_ETYPE_ANY:
++	  counter -= len2;
++	  break;
++	case ASN1_ETYPE_CHOICE:
++	  counter -= len2;
++	  break;
++	default:
++	  return ASN1_DER_ERROR;
++	  break;
++	}
++    }
++
++  counter += len2;
++  *tag_len = counter;
++  if (inner_tag_len)
++    *inner_tag_len = len2;
++  return ASN1_SUCCESS;
++
++cleanup:
++  return result;
++}
++
++static int
++extract_tag_der_recursive(asn1_node node, const unsigned char *der, int der_len,
++		       int *ret_len, int *inner_len, unsigned flags)
++{
++asn1_node p;
++int ris = ASN1_DER_ERROR;
++
++  if (type_field (node->type) == ASN1_ETYPE_CHOICE)
++    {
++      p = node->down;
++      while (p)
++        {
++          ris = _asn1_extract_tag_der (p, der, der_len, ret_len, inner_len, flags);
++          if (ris == ASN1_SUCCESS)
++            break;
++          p = p->right;
++	}
++
++      *ret_len = 0;
++      return ris;
++    }
++  else
++    return _asn1_extract_tag_der (node, der, der_len, ret_len, inner_len, flags);
++}
++
++static int
++_asn1_delete_not_used (asn1_node node)
++{
++  asn1_node p, p2;
++
++  if (node == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  p = node;
++  while (p)
++    {
++      if (p->type & CONST_NOT_USED)
++	{
++	  p2 = NULL;
++	  if (p != node)
++	    {
++	      p2 = _asn1_find_left (p);
++	      if (!p2)
++		p2 = _asn1_find_up (p);
++	    }
++	  asn1_delete_structure (&p);
++	  p = p2;
++	}
++
++      if (!p)
++	break;			/* reach node */
++
++      if (p->down)
++	{
++	  p = p->down;
++	}
++      else
++	{
++	  if (p == node)
++	    p = NULL;
++	  else if (p->right)
++	    p = p->right;
++	  else
++	    {
++	      while (1)
++		{
++		  p = _asn1_find_up (p);
++		  if (p == node)
++		    {
++		      p = NULL;
++		      break;
++		    }
++		  if (p->right)
++		    {
++		      p = p->right;
++		      break;
++		    }
++		}
++	    }
++	}
++    }
++  return ASN1_SUCCESS;
++}
++
++static int
++_asn1_get_indefinite_length_string (const unsigned char *der,
++				    int der_len, int *len)
++{
++  int len2, len3, counter, indefinite;
++  int result;
++  unsigned long tag;
++  unsigned char class;
++
++  counter = indefinite = 0;
++
++  while (1)
++    {
++      if (HAVE_TWO(der_len) && (der[counter] == 0) && (der[counter + 1] == 0))
++	{
++	  counter += 2;
++	  DECR_LEN(der_len, 2);
++
++	  indefinite--;
++	  if (indefinite <= 0)
++	    break;
++	  else
++	    continue;
++	}
++
++      if (asn1_get_tag_der
++	  (der + counter, der_len, &class, &len2,
++	   &tag) != ASN1_SUCCESS)
++	return ASN1_DER_ERROR;
++
++      DECR_LEN(der_len, len2);
++      counter += len2;
++
++      len2 = asn1_get_length_der (der + counter, der_len, &len3);
++      if (len2 < -1)
++	return ASN1_DER_ERROR;
++
++      if (len2 == -1)
++	{
++	  indefinite++;
++	  counter += 1;
++          DECR_LEN(der_len, 1);
++	}
++      else
++	{
++	  counter += len2 + len3;
++          DECR_LEN(der_len, len2+len3);
++	}
++    }
++
++  *len = counter;
++  return ASN1_SUCCESS;
++
++cleanup:
++  return result;
++}
++
++static void delete_unneeded_choice_fields(asn1_node p)
++{
++  asn1_node p2;
++
++  while (p->right)
++    {
++      p2 = p->right;
++      asn1_delete_structure (&p2);
++    }
++}
++
++
++/**
++ * asn1_der_decoding2
++ * @element: pointer to an ASN1 structure.
++ * @ider: vector that contains the DER encoding.
++ * @max_ider_len: pointer to an integer giving the information about the
++ *   maximal number of bytes occupied by *@ider. The real size of the DER
++ *   encoding is returned through this pointer.
++ * @flags: flags controlling the behaviour of the function.
++ * @errorDescription: null-terminated string contains details when an
++ *   error occurred.
++ *
++ * Fill the structure *@element with values of a DER encoding string. The
++ * structure must just be created with function asn1_create_element().
++ *
++ * If %ASN1_DECODE_FLAG_ALLOW_PADDING flag is set then the function will ignore
++ * padding after the decoded DER data. Upon a successful return the value of
++ * *@max_ider_len will be set to the number of bytes decoded.
++ *
++ * If %ASN1_DECODE_FLAG_STRICT_DER flag is set then the function will
++ * not decode any BER-encoded elements.
++ *
++ * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
++ *   if @ELEMENT is %NULL, and %ASN1_TAG_ERROR or
++ *   %ASN1_DER_ERROR if the der encoding doesn't match the structure
++ *   name (*@ELEMENT deleted).
++ **/
++int
++asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len,
++		    unsigned int flags, char *errorDescription)
++{
++  asn1_node node, p, p2, p3;
++  char temp[128];
++  int counter, len2, len3, len4, move, ris, tlen;
++  struct node_tail_cache_st tcache = {NULL, NULL};
++  unsigned char class;
++  unsigned long tag;
++  int tag_len;
++  int indefinite, result, total_len = *max_ider_len, ider_len = *max_ider_len;
++  int inner_tag_len;
++  unsigned char *ptmp;
++  const unsigned char *ptag;
++  const unsigned char *der = ider;
++
++  node = *element;
++
++  if (errorDescription != NULL)
++    errorDescription[0] = 0;
++
++  if (node == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  if (node->type & CONST_OPTION)
++    {
++      result = ASN1_GENERIC_ERROR;
++      warn();
++      goto cleanup;
++    }
++
++  counter = 0;
++  move = DOWN;
++  p = node;
++  while (1)
++    {
++      tag_len = 0;
++      inner_tag_len = 0;
++      ris = ASN1_SUCCESS;
++      if (move != UP)
++	{
++	  if (p->type & CONST_SET)
++	    {
++	      p2 = _asn1_find_up (p);
++	      len2 = p2->tmp_ival;
++	      if (len2 == -1)
++		{
++		  if (HAVE_TWO(ider_len) && !der[counter] && !der[counter + 1])
++		    {
++		      p = p2;
++		      move = UP;
++		      counter += 2;
++		      DECR_LEN(ider_len, 2);
++		      continue;
++		    }
++		}
++	      else if (counter == len2)
++		{
++		  p = p2;
++		  move = UP;
++		  continue;
++		}
++	      else if (counter > len2)
++		{
++		  result = ASN1_DER_ERROR;
++                  warn();
++		  goto cleanup;
++		}
++	      p2 = p2->down;
++	      while (p2)
++		{
++		  if ((p2->type & CONST_SET) && (p2->type & CONST_NOT_USED))
++		    {
++		      ris =
++			  extract_tag_der_recursive (p2, der + counter,
++						     ider_len, &len2, NULL, flags);
++		      if (ris == ASN1_SUCCESS)
++			{
++			  p2->type &= ~CONST_NOT_USED;
++			  p = p2;
++			  break;
++			}
++		    }
++		  p2 = p2->right;
++		}
++	      if (p2 == NULL)
++		{
++		  result = ASN1_DER_ERROR;
++                  warn();
++		  goto cleanup;
++		}
++	    }
++
++	  /* the position in the DER structure this starts */
++	  p->start = counter;
++	  p->end = total_len - 1;
++
++	  if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT))
++	    {
++	      p2 = _asn1_find_up (p);
++	      len2 = p2->tmp_ival;
++	      if (counter == len2)
++		{
++		  if (p->right)
++		    {
++		      p2 = p->right;
++		      move = RIGHT;
++		    }
++		  else
++		    move = UP;
++
++		  if (p->type & CONST_OPTION)
++		    asn1_delete_structure (&p);
++
++		  p = p2;
++		  continue;
++		}
++	    }
++
++	  if (type_field (p->type) == ASN1_ETYPE_CHOICE)
++	    {
++	      while (p->down)
++		{
++		  ris =
++		      extract_tag_der_recursive (p->down, der + counter,
++					         ider_len, &len2, NULL, flags);
++
++		  if (ris == ASN1_SUCCESS)
++		    {
++		      delete_unneeded_choice_fields(p->down);
++		      break;
++		    }
++		  else if (ris == ASN1_ERROR_TYPE_ANY)
++		    {
++		      result = ASN1_ERROR_TYPE_ANY;
++                      warn();
++		      goto cleanup;
++		    }
++		  else
++		    {
++		      p2 = p->down;
++		      asn1_delete_structure (&p2);
++		    }
++		}
++
++	      if (p->down == NULL)
++		{
++		  if (!(p->type & CONST_OPTION))
++		    {
++		      result = ASN1_DER_ERROR;
++                      warn();
++		      goto cleanup;
++		    }
++		}
++	      else if (type_field (p->type) != ASN1_ETYPE_CHOICE)
++		p = p->down;
++
++	      p->start = counter;
++	    }
++
++	  if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT))
++	    {
++	      p2 = _asn1_find_up (p);
++	      len2 = p2->tmp_ival;
++
++	      if ((len2 != -1) && (counter > len2))
++		ris = ASN1_TAG_ERROR;
++	    }
++
++	  if (ris == ASN1_SUCCESS)
++	    ris =
++	      extract_tag_der_recursive (p, der + counter, ider_len,
++	                                 &tag_len, &inner_tag_len, flags);
++
++	  if (ris != ASN1_SUCCESS)
++	    {
++	      if (p->type & CONST_OPTION)
++		{
++		  p->type |= CONST_NOT_USED;
++		  move = RIGHT;
++		}
++	      else if (p->type & CONST_DEFAULT)
++		{
++		  _asn1_set_value (p, NULL, 0);
++		  move = RIGHT;
++		}
++	      else
++		{
++		  if (errorDescription != NULL)
++		    _asn1_error_description_tag_error (p, errorDescription);
++
++		  result = ASN1_TAG_ERROR;
++                  warn();
++		  goto cleanup;
++		}
++	    }
++	  else
++	    {
++	      DECR_LEN(ider_len, tag_len);
++	      counter += tag_len;
++	    }
++	}
++
++      if (ris == ASN1_SUCCESS)
++	{
++	  switch (type_field (p->type))
++	    {
++	    case ASN1_ETYPE_NULL:
++	      DECR_LEN(ider_len, 1);
++	      if (der[counter])
++		{
++		  result = ASN1_DER_ERROR;
++                  warn();
++		  goto cleanup;
++		}
++	      counter++;
++	      move = RIGHT;
++	      break;
++	    case ASN1_ETYPE_BOOLEAN:
++	      DECR_LEN(ider_len, 2);
++
++	      if (der[counter++] != 1)
++		{
++		  result = ASN1_DER_ERROR;
++                  warn();
++		  goto cleanup;
++		}
++	      if (der[counter++] == 0)
++		_asn1_set_value (p, "F", 1);
++	      else
++		_asn1_set_value (p, "T", 1);
++	      move = RIGHT;
++	      break;
++	    case ASN1_ETYPE_INTEGER:
++	    case ASN1_ETYPE_ENUMERATED:
++	      len2 =
++		asn1_get_length_der (der + counter, ider_len, &len3);
++	      if (len2 < 0)
++		{
++		  result = ASN1_DER_ERROR;
++                  warn();
++		  goto cleanup;
++		}
++
++	      DECR_LEN(ider_len, len3+len2);
++
++	      _asn1_set_value (p, der + counter, len3 + len2);
++	      counter += len3 + len2;
++	      move = RIGHT;
++	      break;
++	    case ASN1_ETYPE_OBJECT_ID:
++	      result =
++		asn1_get_object_id_der (der + counter, ider_len, &len2,
++					temp, sizeof (temp));
++	      if (result != ASN1_SUCCESS)
++	        {
++                  warn();
++		  goto cleanup;
++		}
++
++	      DECR_LEN(ider_len, len2);
++
++	      tlen = strlen (temp);
++	      if (tlen > 0)
++		_asn1_set_value (p, temp, tlen + 1);
++
++	      counter += len2;
++	      move = RIGHT;
++	      break;
++	    case ASN1_ETYPE_GENERALIZED_TIME:
++	    case ASN1_ETYPE_UTC_TIME:
++	      result =
++		_asn1_get_time_der (type_field (p->type), der + counter, ider_len, &len2, temp,
++				    sizeof (temp) - 1, flags);
++	      if (result != ASN1_SUCCESS)
++	        {
++                  warn();
++                  goto cleanup;
++                }
++
++	      DECR_LEN(ider_len, len2);
++
++	      tlen = strlen (temp);
++	      if (tlen > 0)
++		_asn1_set_value (p, temp, tlen);
++
++	      counter += len2;
++	      move = RIGHT;
++	      break;
++	    case ASN1_ETYPE_OCTET_STRING:
++	      if (counter < inner_tag_len)
++	        {
++		  result = ASN1_DER_ERROR;
++                  warn();
++		  goto cleanup;
++	        }
++
++              ptag = der + counter - inner_tag_len;
++              if ((flags & ASN1_DECODE_FLAG_STRICT_DER) || !(ptag[0] & ASN1_CLASS_STRUCTURED))
++                {
++	          if (ptag[0] & ASN1_CLASS_STRUCTURED)
++		    {
++		      result = ASN1_DER_ERROR;
++                      warn();
++		      goto cleanup;
++		    }
++
++	          len2 =
++		    asn1_get_length_der (der + counter, ider_len, &len3);
++	          if (len2 < 0)
++		    {
++		      result = ASN1_DER_ERROR;
++                      warn();
++		      goto cleanup;
++		    }
++
++	          DECR_LEN(ider_len, len3+len2);
++
++	          _asn1_set_value (p, der + counter, len3 + len2);
++	          counter += len3 + len2;
++                }
++              else
++                {
++                  unsigned dflags = 0, vlen, ber_len;
++
++                  if (ptag[0] & ASN1_CLASS_STRUCTURED)
++                    dflags |= DECODE_FLAG_CONSTRUCTED;
++
++                  result = _asn1_decode_simple_ber(type_field (p->type), der+counter, ider_len, &ptmp, &vlen, &ber_len, dflags);
++                  if (result != ASN1_SUCCESS)
++	            {
++                      warn();
++		      goto cleanup;
++		    }
++
++		  DECR_LEN(ider_len, ber_len);
++
++		  _asn1_set_value_lv (p, ptmp, vlen);
++
++	          counter += ber_len;
++	          free(ptmp);
++                }
++	      move = RIGHT;
++	      break;
++	    case ASN1_ETYPE_GENERALSTRING:
++	    case ASN1_ETYPE_NUMERIC_STRING:
++	    case ASN1_ETYPE_IA5_STRING:
++	    case ASN1_ETYPE_TELETEX_STRING:
++	    case ASN1_ETYPE_PRINTABLE_STRING:
++	    case ASN1_ETYPE_UNIVERSAL_STRING:
++	    case ASN1_ETYPE_BMP_STRING:
++	    case ASN1_ETYPE_UTF8_STRING:
++	    case ASN1_ETYPE_VISIBLE_STRING:
++	    case ASN1_ETYPE_BIT_STRING:
++	      len2 =
++		asn1_get_length_der (der + counter, ider_len, &len3);
++	      if (len2 < 0)
++		{
++		  result = ASN1_DER_ERROR;
++                  warn();
++		  goto cleanup;
++		}
++
++	      DECR_LEN(ider_len, len3+len2);
++
++	      _asn1_set_value (p, der + counter, len3 + len2);
++	      counter += len3 + len2;
++	      move = RIGHT;
++	      break;
++	    case ASN1_ETYPE_SEQUENCE:
++	    case ASN1_ETYPE_SET:
++	      if (move == UP)
++		{
++		  len2 = p->tmp_ival;
++		  p->tmp_ival = 0;
++		  if (len2 == -1)
++		    {		/* indefinite length method */
++		      DECR_LEN(ider_len, 2);
++		      if ((der[counter]) || der[counter + 1])
++		        {
++		          result = ASN1_DER_ERROR;
++                          warn();
++		          goto cleanup;
++			}
++		      counter += 2;
++		    }
++		  else
++		    {		/* definite length method */
++		      if (len2 != counter)
++			{
++			  result = ASN1_DER_ERROR;
++                          warn();
++			  goto cleanup;
++			}
++		    }
++		  move = RIGHT;
++		}
++	      else
++		{		/* move==DOWN || move==RIGHT */
++		  len3 =
++		    asn1_get_length_der (der + counter, ider_len, &len2);
++                  if (IS_ERR(len3, flags))
++		    {
++		      result = ASN1_DER_ERROR;
++                      warn();
++		      goto cleanup;
++		    }
++
++	          DECR_LEN(ider_len, len2);
++		  counter += len2;
++
++		  if (len3 > 0)
++		    {
++		      p->tmp_ival = counter + len3;
++		      move = DOWN;
++		    }
++		  else if (len3 == 0)
++		    {
++		      p2 = p->down;
++		      while (p2)
++			{
++			  if (type_field (p2->type) != ASN1_ETYPE_TAG)
++			    {
++			      p3 = p2->right;
++			      asn1_delete_structure (&p2);
++			      p2 = p3;
++			    }
++			  else
++			    p2 = p2->right;
++			}
++		      move = RIGHT;
++		    }
++		  else
++		    {		/* indefinite length method */
++		      p->tmp_ival = -1;
++		      move = DOWN;
++		    }
++		}
++	      break;
++	    case ASN1_ETYPE_SEQUENCE_OF:
++	    case ASN1_ETYPE_SET_OF:
++	      if (move == UP)
++		{
++		  len2 = p->tmp_ival;
++		  if (len2 == -1)
++		    {		/* indefinite length method */
++		      if (!HAVE_TWO(ider_len) || ((der[counter]) || der[counter + 1]))
++			{
++			  result = _asn1_append_sequence_set (p, &tcache);
++			  if (result != 0)
++			    {
++                              warn();
++		              goto cleanup;
++		            }
++			  p = tcache.tail;
++			  move = RIGHT;
++			  continue;
++			}
++
++		      p->tmp_ival = 0;
++		      tcache.tail = NULL; /* finished decoding this structure */
++		      tcache.head = NULL;
++		      DECR_LEN(ider_len, 2);
++		      counter += 2;
++		    }
++		  else
++		    {		/* definite length method */
++		      if (len2 > counter)
++			{
++			  result = _asn1_append_sequence_set (p, &tcache);
++			  if (result != 0)
++			    {
++                              warn();
++		              goto cleanup;
++		            }
++			  p = tcache.tail;
++			  move = RIGHT;
++			  continue;
++			}
++
++		      p->tmp_ival = 0;
++		      tcache.tail = NULL; /* finished decoding this structure */
++		      tcache.head = NULL;
++
++		      if (len2 != counter)
++			{
++			  result = ASN1_DER_ERROR;
++                          warn();
++			  goto cleanup;
++			}
++		    }
++		}
++	      else
++		{		/* move==DOWN || move==RIGHT */
++		  len3 =
++		    asn1_get_length_der (der + counter, ider_len, &len2);
++                  if (IS_ERR(len3, flags))
++		    {
++		      result = ASN1_DER_ERROR;
++                      warn();
++		      goto cleanup;
++		    }
++
++		  DECR_LEN(ider_len, len2);
++		  counter += len2;
++		  if (len3)
++		    {
++		      if (len3 > 0)
++			{	/* definite length method */
++		          p->tmp_ival = counter + len3;
++			}
++		      else
++			{	/* indefinite length method */
++		          p->tmp_ival = -1;
++			}
++
++		      p2 = p->down;
++                      if (p2 == NULL)
++		        {
++		          result = ASN1_DER_ERROR;
++                          warn();
++		          goto cleanup;
++		        }
++
++		      while ((type_field (p2->type) == ASN1_ETYPE_TAG)
++			     || (type_field (p2->type) == ASN1_ETYPE_SIZE))
++			p2 = p2->right;
++		      if (p2->right == NULL)
++		        {
++			  result = _asn1_append_sequence_set (p, &tcache);
++			  if (result != 0)
++			    {
++                              warn();
++		              goto cleanup;
++		            }
++			}
++		      p = p2;
++		    }
++		}
++	      move = RIGHT;
++	      break;
++	    case ASN1_ETYPE_ANY:
++	      /* Check indefinite lenth method in an EXPLICIT TAG */
++
++	      if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) && (p->type & CONST_TAG) &&
++	          tag_len == 2 && (der[counter - 1] == 0x80))
++		indefinite = 1;
++	      else
++	        indefinite = 0;
++
++	      if (asn1_get_tag_der
++		  (der + counter, ider_len, &class, &len2,
++		   &tag) != ASN1_SUCCESS)
++		{
++		  result = ASN1_DER_ERROR;
++                  warn();
++		  goto cleanup;
++		}
++
++	      DECR_LEN(ider_len, len2);
++
++	      len4 =
++		asn1_get_length_der (der + counter + len2,
++				     ider_len, &len3);
++              if (IS_ERR(len4, flags))
++		{
++		  result = ASN1_DER_ERROR;
++                  warn();
++		  goto cleanup;
++		}
++	      if (len4 != -1) /* definite */
++		{
++		  len2 += len4;
++
++	          DECR_LEN(ider_len, len4+len3);
++		  _asn1_set_value_lv (p, der + counter, len2 + len3);
++		  counter += len2 + len3;
++		}
++	      else /* == -1 */
++		{		/* indefinite length */
++		  ider_len += len2; /* undo DECR_LEN */
++
++		  if (counter == 0)
++		    {
++		      result = ASN1_DER_ERROR;
++                      warn();
++		      goto cleanup;
++		    }
++
++		  result =
++		    _asn1_get_indefinite_length_string (der + counter, ider_len, &len2);
++		  if (result != ASN1_SUCCESS)
++		    {
++                      warn();
++                      goto cleanup;
++                    }
++
++	          DECR_LEN(ider_len, len2);
++		  _asn1_set_value_lv (p, der + counter, len2);
++		  counter += len2;
++
++		}
++
++	        /* Check if a couple of 0x00 are present due to an EXPLICIT TAG with
++	           an indefinite length method. */
++	        if (indefinite)
++		  {
++	            DECR_LEN(ider_len, 2);
++		    if (!der[counter] && !der[counter + 1])
++		      {
++		        counter += 2;
++		      }
++		    else
++		      {
++		        result = ASN1_DER_ERROR;
++                        warn();
++		        goto cleanup;
++		      }
++		  }
++
++	      move = RIGHT;
++	      break;
++	    default:
++	      move = (move == UP) ? RIGHT : DOWN;
++	      break;
++	    }
++	}
++
++      if (p)
++        {
++          p->end = counter - 1;
++        }
++
++      if (p == node && move != DOWN)
++	break;
++
++      if (move == DOWN)
++	{
++	  if (p->down)
++	    p = p->down;
++	  else
++	    move = RIGHT;
++	}
++      if ((move == RIGHT) && !(p->type & CONST_SET))
++	{
++	  if (p->right)
++	    p = p->right;
++	  else
++	    move = UP;
++	}
++      if (move == UP)
++	p = _asn1_find_up (p);
++    }
++
++  _asn1_delete_not_used (*element);
++
++  if ((ider_len < 0) ||
++      (!(flags & ASN1_DECODE_FLAG_ALLOW_PADDING) && (ider_len != 0)))
++    {
++      warn();
++      result = ASN1_DER_ERROR;
++      goto cleanup;
++    }
++
++  *max_ider_len = total_len - ider_len;
++
++  return ASN1_SUCCESS;
++
++cleanup:
++  asn1_delete_structure (element);
++  return result;
++}
++
++
++/**
++ * asn1_der_decoding:
++ * @element: pointer to an ASN1 structure.
++ * @ider: vector that contains the DER encoding.
++ * @ider_len: number of bytes of *@ider: @ider[0]..@ider[len-1].
++ * @errorDescription: null-terminated string contains details when an
++ *   error occurred.
++ *
++ * Fill the structure *@element with values of a DER encoding
++ * string. The structure must just be created with function
++ * asn1_create_element().
++ *
++ * Note that the *@element variable is provided as a pointer for
++ * historical reasons.
++ *
++ * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
++ *   if @ELEMENT is %NULL, and %ASN1_TAG_ERROR or
++ *   %ASN1_DER_ERROR if the der encoding doesn't match the structure
++ *   name (*@ELEMENT deleted).
++ **/
++int
++asn1_der_decoding (asn1_node * element, const void *ider, int ider_len,
++		   char *errorDescription)
++{
++  return asn1_der_decoding2 (element, ider, &ider_len, 0, errorDescription);
++}
++
++/**
++ * asn1_der_decoding_element:
++ * @structure: pointer to an ASN1 structure
++ * @elementName: name of the element to fill
++ * @ider: vector that contains the DER encoding of the whole structure.
++ * @len: number of bytes of *der: der[0]..der[len-1]
++ * @errorDescription: null-terminated string contains details when an
++ *   error occurred.
++ *
++ * Fill the element named @ELEMENTNAME with values of a DER encoding
++ * string.  The structure must just be created with function
++ * asn1_create_element().  The DER vector must contain the encoding
++ * string of the whole @STRUCTURE.  If an error occurs during the
++ * decoding procedure, the *@STRUCTURE is deleted and set equal to
++ * %NULL.
++ *
++ * This function is deprecated and may just be an alias to asn1_der_decoding
++ * in future versions. Use asn1_der_decoding() instead.
++ *
++ * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
++ *   if ELEMENT is %NULL or @elementName == NULL, and
++ *   %ASN1_TAG_ERROR or %ASN1_DER_ERROR if the der encoding doesn't
++ *   match the structure @structure (*ELEMENT deleted).
++ **/
++int
++asn1_der_decoding_element (asn1_node * structure, const char *elementName,
++			   const void *ider, int len, char *errorDescription)
++{
++  return asn1_der_decoding(structure, ider, len, errorDescription);
++}
++
++/**
++ * asn1_der_decoding_startEnd:
++ * @element: pointer to an ASN1 element
++ * @ider: vector that contains the DER encoding.
++ * @ider_len: number of bytes of *@ider: @ider[0]..@ider[len-1]
++ * @name_element: an element of NAME structure.
++ * @start: the position of the first byte of NAME_ELEMENT decoding
++ *   (@ider[*start])
++ * @end: the position of the last byte of NAME_ELEMENT decoding
++ *  (@ider[*end])
++ *
++ * Find the start and end point of an element in a DER encoding
++ * string. I mean that if you have a der encoding and you have already
++ * used the function asn1_der_decoding() to fill a structure, it may
++ * happen that you want to find the piece of string concerning an
++ * element of the structure.
++ *
++ * One example is the sequence "tbsCertificate" inside an X509
++ * certificate.
++ *
++ * Note that since libtasn1 3.7 the @ider and @ider_len parameters
++ * can be omitted, if the element is already decoded using asn1_der_decoding().
++ *
++ * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
++ *   if ELEMENT is %asn1_node EMPTY or @name_element is not a valid
++ *   element, %ASN1_TAG_ERROR or %ASN1_DER_ERROR if the der encoding
++ *   doesn't match the structure ELEMENT.
++ **/
++int
++asn1_der_decoding_startEnd (asn1_node element, const void *ider, int ider_len,
++			    const char *name_element, int *start, int *end)
++{
++  asn1_node node, node_to_find;
++  int result = ASN1_DER_ERROR;
++
++  node = element;
++
++  if (node == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  node_to_find = asn1_find_node (node, name_element);
++
++  if (node_to_find == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  *start = node_to_find->start;
++  *end = node_to_find->end;
++
++  if (*start == 0 && *end == 0)
++    {
++      if (ider == NULL || ider_len == 0)
++        return ASN1_GENERIC_ERROR;
++
++      /* it seems asn1_der_decoding() wasn't called before. Do it now */
++      result = asn1_der_decoding (&node, ider, ider_len, NULL);
++      if (result != ASN1_SUCCESS)
++        {
++          warn();
++          return result;
++        }
++
++      node_to_find = asn1_find_node (node, name_element);
++      if (node_to_find == NULL)
++        return ASN1_ELEMENT_NOT_FOUND;
++
++      *start = node_to_find->start;
++      *end = node_to_find->end;
++    }
++
++  if (*end < *start)
++    return ASN1_GENERIC_ERROR;
++
++  return ASN1_SUCCESS;
++}
++
++/**
++ * asn1_expand_any_defined_by:
++ * @definitions: ASN1 definitions
++ * @element: pointer to an ASN1 structure
++ *
++ * Expands every "ANY DEFINED BY" element of a structure created from
++ * a DER decoding process (asn1_der_decoding function). The element
++ * ANY must be defined by an OBJECT IDENTIFIER. The type used to
++ * expand the element ANY is the first one following the definition of
++ * the actual value of the OBJECT IDENTIFIER.
++ *
++ * Returns: %ASN1_SUCCESS if Substitution OK, %ASN1_ERROR_TYPE_ANY if
++ *   some "ANY DEFINED BY" element couldn't be expanded due to a
++ *   problem in OBJECT_ID -> TYPE association, or other error codes
++ *   depending on DER decoding.
++ **/
++int
++asn1_expand_any_defined_by (asn1_node_const definitions, asn1_node * element)
++{
++  char name[2 * ASN1_MAX_NAME_SIZE + 2],
++    value[ASN1_MAX_NAME_SIZE];
++  int retCode = ASN1_SUCCESS, result;
++  int len, len2, len3;
++  asn1_node_const p2;
++  asn1_node p, p3, aux = NULL;
++  char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
++  const char *definitionsName;
++
++  if ((definitions == NULL) || (*element == NULL))
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  definitionsName = definitions->name;
++
++  p = *element;
++  while (p)
++    {
++
++      switch (type_field (p->type))
++	{
++	case ASN1_ETYPE_ANY:
++	  if ((p->type & CONST_DEFINED_BY) && (p->value))
++	    {
++	      /* search the "DEF_BY" element */
++	      p2 = p->down;
++	      while ((p2) && (type_field (p2->type) != ASN1_ETYPE_CONSTANT))
++		p2 = p2->right;
++
++	      if (!p2)
++		{
++		  retCode = ASN1_ERROR_TYPE_ANY;
++		  break;
++		}
++
++	      p3 = _asn1_find_up (p);
++
++	      if (!p3)
++		{
++		  retCode = ASN1_ERROR_TYPE_ANY;
++		  break;
++		}
++
++	      p3 = p3->down;
++	      while (p3)
++		{
++		  if (!(strcmp (p3->name, p2->name)))
++		    break;
++		  p3 = p3->right;
++		}
++
++	      if ((!p3) || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) ||
++		  (p3->value == NULL))
++		{
++
++		  p3 = _asn1_find_up (p);
++		  p3 = _asn1_find_up (p3);
++
++		  if (!p3)
++		    {
++		      retCode = ASN1_ERROR_TYPE_ANY;
++		      break;
++		    }
++
++		  p3 = p3->down;
++
++		  while (p3)
++		    {
++		      if (!(strcmp (p3->name, p2->name)))
++			break;
++		      p3 = p3->right;
++		    }
++
++		  if ((!p3) || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID)
++		      || (p3->value == NULL))
++		    {
++		      retCode = ASN1_ERROR_TYPE_ANY;
++		      break;
++		    }
++		}
++
++	      /* search the OBJECT_ID into definitions */
++	      p2 = definitions->down;
++	      while (p2)
++		{
++		  if ((type_field (p2->type) == ASN1_ETYPE_OBJECT_ID) &&
++		      (p2->type & CONST_ASSIGN))
++		    {
++		      snprintf(name, sizeof(name), "%s.%s", definitionsName, p2->name);
++
++		      len = ASN1_MAX_NAME_SIZE;
++		      result =
++			asn1_read_value (definitions, name, value, &len);
++
++		      if ((result == ASN1_SUCCESS)
++			  && (!_asn1_strcmp (p3->value, value)))
++			{
++			  p2 = p2->right;	/* pointer to the structure to
++						   use for expansion */
++			  while ((p2) && (p2->type & CONST_ASSIGN))
++			    p2 = p2->right;
++
++			  if (p2)
++			    {
++			      snprintf(name, sizeof(name), "%s.%s", definitionsName, p2->name);
++
++			      result =
++				asn1_create_element (definitions, name, &aux);
++			      if (result == ASN1_SUCCESS)
++				{
++				  _asn1_cpy_name (aux, p);
++				  len2 =
++				    asn1_get_length_der (p->value,
++							 p->value_len, &len3);
++				  if (len2 < 0)
++				    return ASN1_DER_ERROR;
++
++				  result =
++				    asn1_der_decoding (&aux, p->value + len3,
++						       len2,
++						       errorDescription);
++				  if (result == ASN1_SUCCESS)
++				    {
++
++				      _asn1_set_right (aux, p->right);
++				      _asn1_set_right (p, aux);
++
++				      result = asn1_delete_structure (&p);
++				      if (result == ASN1_SUCCESS)
++					{
++					  p = aux;
++					  aux = NULL;
++					  break;
++					}
++				      else
++					{	/* error with asn1_delete_structure */
++					  asn1_delete_structure (&aux);
++					  retCode = result;
++					  break;
++					}
++				    }
++				  else
++				    {	/* error with asn1_der_decoding */
++				      retCode = result;
++				      break;
++				    }
++				}
++			      else
++				{	/* error with asn1_create_element */
++				  retCode = result;
++				  break;
++				}
++			    }
++			  else
++			    {	/* error with the pointer to the structure to exapand */
++			      retCode = ASN1_ERROR_TYPE_ANY;
++			      break;
++			    }
++			}
++		    }
++		  p2 = p2->right;
++		}		/* end while */
++
++	      if (!p2)
++		{
++		  retCode = ASN1_ERROR_TYPE_ANY;
++		  break;
++		}
++
++	    }
++	  break;
++	default:
++	  break;
++	}
++
++
++      if (p->down)
++	{
++	  p = p->down;
++	}
++      else if (p == *element)
++	{
++	  p = NULL;
++	  break;
++	}
++      else if (p->right)
++	p = p->right;
++      else
++	{
++	  while (1)
++	    {
++	      p = _asn1_find_up (p);
++	      if (p == *element)
++		{
++		  p = NULL;
++		  break;
++		}
++	      if (p->right)
++		{
++		  p = p->right;
++		  break;
++		}
++	    }
++	}
++    }
++
++  return retCode;
++}
++
++/**
++ * asn1_expand_octet_string:
++ * @definitions: ASN1 definitions
++ * @element: pointer to an ASN1 structure
++ * @octetName: name of the OCTECT STRING field to expand.
++ * @objectName: name of the OBJECT IDENTIFIER field to use to define
++ *    the type for expansion.
++ *
++ * Expands an "OCTET STRING" element of a structure created from a DER
++ * decoding process (the asn1_der_decoding() function).  The type used
++ * for expansion is the first one following the definition of the
++ * actual value of the OBJECT IDENTIFIER indicated by OBJECTNAME.
++ *
++ * Returns: %ASN1_SUCCESS if substitution OK, %ASN1_ELEMENT_NOT_FOUND
++ *   if @objectName or @octetName are not correct,
++ *   %ASN1_VALUE_NOT_VALID if it wasn't possible to find the type to
++ *   use for expansion, or other errors depending on DER decoding.
++ **/
++int
++asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element,
++			  const char *octetName, const char *objectName)
++{
++  char name[2 * ASN1_MAX_NAME_SIZE + 1], value[ASN1_MAX_NAME_SIZE];
++  int retCode = ASN1_SUCCESS, result;
++  int len, len2, len3;
++  asn1_node_const p2;
++  asn1_node aux = NULL;
++  asn1_node octetNode = NULL, objectNode = NULL;
++  char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
++
++  if ((definitions == NULL) || (*element == NULL))
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  octetNode = asn1_find_node (*element, octetName);
++  if (octetNode == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++  if (type_field (octetNode->type) != ASN1_ETYPE_OCTET_STRING)
++    return ASN1_ELEMENT_NOT_FOUND;
++  if (octetNode->value == NULL)
++    return ASN1_VALUE_NOT_FOUND;
++
++  objectNode = asn1_find_node (*element, objectName);
++  if (objectNode == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  if (type_field (objectNode->type) != ASN1_ETYPE_OBJECT_ID)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  if (objectNode->value == NULL)
++    return ASN1_VALUE_NOT_FOUND;
++
++
++  /* search the OBJECT_ID into definitions */
++  p2 = definitions->down;
++  while (p2)
++    {
++      if ((type_field (p2->type) == ASN1_ETYPE_OBJECT_ID) &&
++	  (p2->type & CONST_ASSIGN))
++	{
++	  strcpy (name, definitions->name);
++	  strcat (name, ".");
++	  strcat (name, p2->name);
++
++	  len = sizeof (value);
++	  result = asn1_read_value (definitions, name, value, &len);
++
++	  if ((result == ASN1_SUCCESS)
++	      && (!_asn1_strcmp (objectNode->value, value)))
++	    {
++
++	      p2 = p2->right;	/* pointer to the structure to
++				   use for expansion */
++	      while ((p2) && (p2->type & CONST_ASSIGN))
++		p2 = p2->right;
++
++	      if (p2)
++		{
++		  strcpy (name, definitions->name);
++		  strcat (name, ".");
++		  strcat (name, p2->name);
++
++		  result = asn1_create_element (definitions, name, &aux);
++		  if (result == ASN1_SUCCESS)
++		    {
++		      _asn1_cpy_name (aux, octetNode);
++		      len2 =
++			asn1_get_length_der (octetNode->value,
++					     octetNode->value_len, &len3);
++		      if (len2 < 0)
++			return ASN1_DER_ERROR;
++
++		      result =
++			asn1_der_decoding (&aux, octetNode->value + len3,
++					   len2, errorDescription);
++		      if (result == ASN1_SUCCESS)
++			{
++
++			  _asn1_set_right (aux, octetNode->right);
++			  _asn1_set_right (octetNode, aux);
++
++			  result = asn1_delete_structure (&octetNode);
++			  if (result == ASN1_SUCCESS)
++			    {
++			      aux = NULL;
++			      break;
++			    }
++			  else
++			    {	/* error with asn1_delete_structure */
++			      asn1_delete_structure (&aux);
++			      retCode = result;
++			      break;
++			    }
++			}
++		      else
++			{	/* error with asn1_der_decoding */
++			  retCode = result;
++			  break;
++			}
++		    }
++		  else
++		    {		/* error with asn1_create_element */
++		      retCode = result;
++		      break;
++		    }
++		}
++	      else
++		{		/* error with the pointer to the structure to exapand */
++		  retCode = ASN1_VALUE_NOT_VALID;
++		  break;
++		}
++	    }
++	}
++
++      p2 = p2->right;
++
++    }
++
++  if (!p2)
++    retCode = ASN1_VALUE_NOT_VALID;
++
++  return retCode;
++}
++
++/*-
++ * _asn1_decode_simple_der:
++ * @etype: The type of the string to be encoded (ASN1_ETYPE_)
++ * @der: the encoded string
++ * @_der_len: the bytes of the encoded string
++ * @str: a pointer to the data
++ * @str_len: the length of the data
++ * @dflags: DECODE_FLAG_*
++ *
++ * Decodes a simple DER encoded type (e.g. a string, which is not constructed).
++ * The output is a pointer inside the @der.
++ *
++ * Returns: %ASN1_SUCCESS if successful or an error value.
++ -*/
++static int
++_asn1_decode_simple_der (unsigned int etype, const unsigned char *der,
++			unsigned int _der_len, const unsigned char **str,
++			unsigned int *str_len, unsigned dflags)
++{
++  int tag_len, len_len;
++  const unsigned char *p;
++  int der_len = _der_len;
++  unsigned char class;
++  unsigned long tag;
++  long ret;
++
++  if (der == NULL || der_len == 0)
++    return ASN1_VALUE_NOT_VALID;
++
++  if (ETYPE_OK (etype) == 0 || ETYPE_IS_STRING(etype) == 0)
++    return ASN1_VALUE_NOT_VALID;
++
++  /* doesn't handle constructed classes */
++  class = ETYPE_CLASS(etype);
++  if (class != ASN1_CLASS_UNIVERSAL)
++    return ASN1_VALUE_NOT_VALID;
++
++  p = der;
++
++  if (dflags & DECODE_FLAG_HAVE_TAG)
++    {
++      ret = asn1_get_tag_der (p, der_len, &class, &tag_len, &tag);
++      if (ret != ASN1_SUCCESS)
++        return ret;
++
++      if (class != ETYPE_CLASS (etype) || tag != ETYPE_TAG (etype))
++        {
++          warn();
++          return ASN1_DER_ERROR;
++        }
++
++      p += tag_len;
++      der_len -= tag_len;
++      if (der_len <= 0)
++        return ASN1_DER_ERROR;
++    }
++
++  ret = asn1_get_length_der (p, der_len, &len_len);
++  if (ret < 0)
++    return ASN1_DER_ERROR;
++
++  p += len_len;
++  der_len -= len_len;
++  if (der_len <= 0)
++    return ASN1_DER_ERROR;
++
++  *str_len = ret;
++  *str = p;
++
++  return ASN1_SUCCESS;
++}
++
++/**
++ * asn1_decode_simple_der:
++ * @etype: The type of the string to be encoded (ASN1_ETYPE_)
++ * @der: the encoded string
++ * @_der_len: the bytes of the encoded string
++ * @str: a pointer to the data
++ * @str_len: the length of the data
++ *
++ * Decodes a simple DER encoded type (e.g. a string, which is not constructed).
++ * The output is a pointer inside the @der.
++ *
++ * Returns: %ASN1_SUCCESS if successful or an error value.
++ **/
++int
++asn1_decode_simple_der (unsigned int etype, const unsigned char *der,
++			unsigned int _der_len, const unsigned char **str,
++			unsigned int *str_len)
++{
++  return _asn1_decode_simple_der(etype, der, _der_len, str, str_len, DECODE_FLAG_HAVE_TAG);
++}
++
++static int append(uint8_t **dst, unsigned *dst_size, const unsigned char *src, unsigned src_size)
++{
++  if (src_size == 0)
++    return ASN1_SUCCESS;
++
++  *dst = _asn1_realloc(*dst, *dst_size+src_size);
++  if (*dst == NULL)
++    return ASN1_MEM_ALLOC_ERROR;
++  memcpy(*dst + *dst_size, src, src_size);
++  *dst_size += src_size;
++  return ASN1_SUCCESS;
++}
++
++/*-
++ * _asn1_decode_simple_ber:
++ * @etype: The type of the string to be encoded (ASN1_ETYPE_)
++ * @der: the encoded string
++ * @_der_len: the bytes of the encoded string
++ * @str: a pointer to the data
++ * @str_len: the length of the data
++ * @ber_len: the total length occupied by BER (may be %NULL)
++ * @have_tag: whether a DER tag is included
++ *
++ * Decodes a BER encoded type. The output is an allocated value
++ * of the data. This decodes BER STRINGS only. Other types are
++ * decoded as DER.
++ *
++ * Returns: %ASN1_SUCCESS if successful or an error value.
++ -*/
++static int
++_asn1_decode_simple_ber (unsigned int etype, const unsigned char *der,
++			unsigned int _der_len, unsigned char **str,
++			unsigned int *str_len, unsigned int *ber_len,
++			unsigned dflags)
++{
++  int tag_len, len_len;
++  const unsigned char *p;
++  int der_len = _der_len;
++  uint8_t *total = NULL;
++  unsigned total_size = 0;
++  unsigned char class;
++  unsigned long tag;
++  unsigned char *out = NULL;
++  const unsigned char *cout = NULL;
++  unsigned out_len;
++  long result;
++
++  if (ber_len) *ber_len = 0;
++
++  if (der == NULL || der_len == 0)
++    {
++      warn();
++      return ASN1_VALUE_NOT_VALID;
++    }
++
++  if (ETYPE_OK (etype) == 0)
++    {
++      warn();
++      return ASN1_VALUE_NOT_VALID;
++    }
++
++  /* doesn't handle constructed + definite classes */
++  class = ETYPE_CLASS (etype);
++  if (class != ASN1_CLASS_UNIVERSAL)
++    {
++      warn();
++      return ASN1_VALUE_NOT_VALID;
++    }
++
++  p = der;
++
++  if (dflags & DECODE_FLAG_HAVE_TAG)
++    {
++      result = asn1_get_tag_der (p, der_len, &class, &tag_len, &tag);
++        if (result != ASN1_SUCCESS)
++          {
++            warn();
++            return result;
++          }
++
++        if (tag != ETYPE_TAG (etype))
++          {
++            warn();
++            return ASN1_DER_ERROR;
++          }
++
++        p += tag_len;
++
++        DECR_LEN(der_len, tag_len);
++
++        if (ber_len) *ber_len += tag_len;
++    }
++
++  /* indefinite constructed */
++  if ((((dflags & DECODE_FLAG_CONSTRUCTED) || class == ASN1_CLASS_STRUCTURED) && ETYPE_IS_STRING(etype)) &&
++      !(dflags & DECODE_FLAG_LEVEL3))
++    {
++      if (der_len == 0)
++        {
++          warn();
++          result = ASN1_DER_ERROR;
++          goto cleanup;
++        }
++
++      if (der_len > 0 && p[0] == 0x80) /* indefinite */
++        {
++          len_len = 1;
++          DECR_LEN(der_len, len_len);
++          p += len_len;
++
++          if (ber_len) *ber_len += len_len;
++
++          /* decode the available octet strings */
++          do
++            {
++              unsigned tmp_len;
++              unsigned flags = DECODE_FLAG_HAVE_TAG;
++
++              if (dflags & DECODE_FLAG_LEVEL1)
++                flags |= DECODE_FLAG_LEVEL2;
++              else if (dflags & DECODE_FLAG_LEVEL2)
++                flags |= DECODE_FLAG_LEVEL3;
++              else
++		flags |= DECODE_FLAG_LEVEL1;
++
++              result = _asn1_decode_simple_ber(etype, p, der_len, &out, &out_len, &tmp_len,
++                                               flags);
++              if (result != ASN1_SUCCESS)
++                {
++                  warn();
++                  goto cleanup;
++                }
++
++              p += tmp_len;
++              DECR_LEN(der_len, tmp_len);
++
++              if (ber_len) *ber_len += tmp_len;
++
++              DECR_LEN(der_len, 2); /* we need the EOC */
++
++              result = append(&total, &total_size, out, out_len);
++              if (result != ASN1_SUCCESS)
++                {
++                  warn();
++                  goto cleanup;
++	        }
++
++              free(out);
++              out = NULL;
++
++	      if (p[0] == 0 && p[1] == 0) /* EOC */
++	        {
++                  if (ber_len) *ber_len += 2;
++                  break;
++                }
++
++              /* no EOC */
++              der_len += 2;
++
++              if (der_len == 2)
++                {
++                  warn();
++                  result = ASN1_DER_ERROR;
++                  goto cleanup;
++                }
++            }
++          while(1);
++        }
++      else /* constructed */
++        {
++          long const_len;
++
++          result = asn1_get_length_ber(p, der_len, &len_len);
++          if (result < 0)
++            {
++              warn();
++              result = ASN1_DER_ERROR;
++              goto cleanup;
++            }
++
++          DECR_LEN(der_len, len_len);
++          p += len_len;
++
++          const_len = result;
++
++          if (ber_len) *ber_len += len_len;
++
++          /* decode the available octet strings */
++          while(const_len > 0)
++            {
++              unsigned tmp_len;
++              unsigned flags = DECODE_FLAG_HAVE_TAG;
++
++              if (dflags & DECODE_FLAG_LEVEL1)
++                flags |= DECODE_FLAG_LEVEL2;
++              else if (dflags & DECODE_FLAG_LEVEL2)
++                flags |= DECODE_FLAG_LEVEL3;
++              else
++		flags |= DECODE_FLAG_LEVEL1;
++
++              result = _asn1_decode_simple_ber(etype, p, der_len, &out, &out_len, &tmp_len,
++                                               flags);
++              if (result != ASN1_SUCCESS)
++                {
++                  warn();
++                  goto cleanup;
++                }
++
++              p += tmp_len;
++              DECR_LEN(der_len, tmp_len);
++              DECR_LEN(const_len, tmp_len);
++
++              if (ber_len) *ber_len += tmp_len;
++
++              result = append(&total, &total_size, out, out_len);
++              if (result != ASN1_SUCCESS)
++                {
++                  warn();
++                  goto cleanup;
++	        }
++
++              free(out);
++              out = NULL;
++            }
++        }
++    }
++  else if (class == ETYPE_CLASS(etype))
++    {
++      if (ber_len)
++        {
++          result = asn1_get_length_der (p, der_len, &len_len);
++          if (result < 0)
++            {
++              warn();
++              result = ASN1_DER_ERROR;
++              goto cleanup;
++            }
++          *ber_len += result + len_len;
++        }
++
++      /* non-string values are decoded as DER */
++      result = _asn1_decode_simple_der(etype, der, _der_len, &cout, &out_len, dflags);
++      if (result != ASN1_SUCCESS)
++        {
++          warn();
++          goto cleanup;
++        }
++
++      result = append(&total, &total_size, cout, out_len);
++      if (result != ASN1_SUCCESS)
++        {
++          warn();
++          goto cleanup;
++        }
++    }
++  else
++    {
++      warn();
++      result = ASN1_DER_ERROR;
++      goto cleanup;
++    }
++
++  *str = total;
++  *str_len = total_size;
++
++  return ASN1_SUCCESS;
++cleanup:
++  free(out);
++  free(total);
++  return result;
++}
++
++/**
++ * asn1_decode_simple_ber:
++ * @etype: The type of the string to be encoded (ASN1_ETYPE_)
++ * @der: the encoded string
++ * @_der_len: the bytes of the encoded string
++ * @str: a pointer to the data
++ * @str_len: the length of the data
++ * @ber_len: the total length occupied by BER (may be %NULL)
++ *
++ * Decodes a BER encoded type. The output is an allocated value
++ * of the data. This decodes BER STRINGS only. Other types are
++ * decoded as DER.
++ *
++ * Returns: %ASN1_SUCCESS if successful or an error value.
++ **/
++int
++asn1_decode_simple_ber (unsigned int etype, const unsigned char *der,
++			unsigned int _der_len, unsigned char **str,
++			unsigned int *str_len, unsigned int *ber_len)
++{
++  return _asn1_decode_simple_ber(etype, der, _der_len, str, str_len, ber_len, DECODE_FLAG_HAVE_TAG);
++}
+diff --git a/grub-core/lib/libtasn1/lib/element.c b/grub-core/lib/libtasn1/lib/element.c
+new file mode 100644
+index 00000000000..997eb2725dc
+--- /dev/null
++++ b/grub-core/lib/libtasn1/lib/element.c
+@@ -0,0 +1,1111 @@
++/*
++ * Copyright (C) 2000-2014 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * The LIBTASN1 library is free software; you can redistribute it
++ * and/or modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301, USA
++ */
++
++/*****************************************************/
++/* File: element.c                                   */
++/* Description: Functions with the read and write    */
++/*   functions.                                      */
++/*****************************************************/
++
++
++#include <int.h>
++#include "parser_aux.h"
++#include <gstr.h>
++#include "structure.h"
++#include "c-ctype.h"
++#include "element.h"
++
++void
++_asn1_hierarchical_name (asn1_node_const node, char *name, int name_size)
++{
++  asn1_node_const p;
++  char tmp_name[64];
++
++  p = node;
++
++  name[0] = 0;
++
++  while (p != NULL)
++    {
++      if (p->name[0] != 0)
++	{
++	  _asn1_str_cpy (tmp_name, sizeof (tmp_name), name),
++	    _asn1_str_cpy (name, name_size, p->name);
++	  _asn1_str_cat (name, name_size, ".");
++	  _asn1_str_cat (name, name_size, tmp_name);
++	}
++      p = _asn1_find_up (p);
++    }
++
++  if (name[0] == 0)
++    _asn1_str_cpy (name, name_size, "ROOT");
++}
++
++
++/******************************************************************/
++/* Function : _asn1_convert_integer                               */
++/* Description: converts an integer from a null terminated string */
++/*              to der decoding. The convertion from a null       */
++/*              terminated string to an integer is made with      */
++/*              the 'strtol' function.                            */
++/* Parameters:                                                    */
++/*   value: null terminated string to convert.                    */
++/*   value_out: convertion result (memory must be already         */
++/*              allocated).                                       */
++/*   value_out_size: number of bytes of value_out.                */
++/*   len: number of significant byte of value_out.                */
++/* Return: ASN1_MEM_ERROR or ASN1_SUCCESS                         */
++/******************************************************************/
++int
++_asn1_convert_integer (const unsigned char *value, unsigned char *value_out,
++		       int value_out_size, int *len)
++{
++  char negative;
++  unsigned char val[SIZEOF_UNSIGNED_LONG_INT];
++  long valtmp;
++  int k, k2;
++
++  valtmp = _asn1_strtol (value, NULL, 10);
++
++  for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT; k++)
++    {
++      val[SIZEOF_UNSIGNED_LONG_INT - k - 1] = (valtmp >> (8 * k)) & 0xFF;
++    }
++
++  if (val[0] & 0x80)
++    negative = 1;
++  else
++    negative = 0;
++
++  for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT - 1; k++)
++    {
++      if (negative && (val[k] != 0xFF))
++	break;
++      else if (!negative && val[k])
++	break;
++    }
++
++  if ((negative && !(val[k] & 0x80)) || (!negative && (val[k] & 0x80)))
++    k--;
++
++  *len = SIZEOF_UNSIGNED_LONG_INT - k;
++
++  if (SIZEOF_UNSIGNED_LONG_INT - k > value_out_size)
++    /* VALUE_OUT is too short to contain the value conversion */
++    return ASN1_MEM_ERROR;
++
++  if (value_out != NULL)
++    {
++      for (k2 = k; k2 < SIZEOF_UNSIGNED_LONG_INT; k2++)
++        value_out[k2 - k] = val[k2];
++    }
++
++#if 0
++  printf ("_asn1_convert_integer: valueIn=%s, lenOut=%d", value, *len);
++  for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT; k++)
++    printf (", vOut[%d]=%d", k, value_out[k]);
++  printf ("\n");
++#endif
++
++  return ASN1_SUCCESS;
++}
++
++/* Appends a new element into the sequence (or set) defined by this
++ * node. The new element will have a name of '?number', where number
++ * is a monotonically increased serial number.
++ *
++ * The last element in the list may be provided in @pcache, to avoid
++ * traversing the list, an expensive operation in long lists.
++ *
++ * On success it returns in @pcache the added element (which is the
++ * tail in the list of added elements).
++ */
++int
++_asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcache)
++{
++  asn1_node p, p2;
++  char temp[LTOSTR_MAX_SIZE];
++  long n;
++
++  if (!node || !(node->down))
++    return ASN1_GENERIC_ERROR;
++
++  p = node->down;
++  while ((type_field (p->type) == ASN1_ETYPE_TAG)
++	 || (type_field (p->type) == ASN1_ETYPE_SIZE))
++    p = p->right;
++
++  p2 = _asn1_copy_structure3 (p);
++  if (p2 == NULL)
++    return ASN1_GENERIC_ERROR;
++
++  if (pcache == NULL || pcache->tail == NULL || pcache->head != node)
++    {
++      while (p->right)
++        {
++          p = p->right;
++        }
++    }
++  else
++    {
++      p = pcache->tail;
++    }
++
++  _asn1_set_right (p, p2);
++  if (pcache)
++    {
++      pcache->head = node;
++      pcache->tail = p2;
++    }
++
++  if (p->name[0] == 0)
++    _asn1_str_cpy (temp, sizeof (temp), "?1");
++  else
++    {
++      n = strtol (p->name + 1, NULL, 0);
++      n++;
++      temp[0] = '?';
++      _asn1_ltostr (n, temp + 1);
++    }
++  _asn1_set_name (p2, temp);
++  /*  p2->type |= CONST_OPTION; */
++
++  return ASN1_SUCCESS;
++}
++
++
++/**
++ * asn1_write_value:
++ * @node_root: pointer to a structure
++ * @name: the name of the element inside the structure that you want to set.
++ * @ivalue: vector used to specify the value to set. If len is >0,
++ *   VALUE must be a two's complement form integer.  if len=0 *VALUE
++ *   must be a null terminated string with an integer value.
++ * @len: number of bytes of *value to use to set the value:
++ *   value[0]..value[len-1] or 0 if value is a null terminated string
++ *
++ * Set the value of one element inside a structure.
++ *
++ * If an element is OPTIONAL and you want to delete it, you must use
++ * the value=NULL and len=0.  Using "pkix.asn":
++ *
++ * result=asn1_write_value(cert, "tbsCertificate.issuerUniqueID",
++ * NULL, 0);
++ *
++ * Description for each type:
++ *
++ * INTEGER: VALUE must contain a two's complement form integer.
++ *
++ *            value[0]=0xFF ,               len=1 -> integer=-1.
++ *            value[0]=0xFF value[1]=0xFF , len=2 -> integer=-1.
++ *            value[0]=0x01 ,               len=1 -> integer= 1.
++ *            value[0]=0x00 value[1]=0x01 , len=2 -> integer= 1.
++ *            value="123"                 , len=0 -> integer= 123.
++ *
++ * ENUMERATED: As INTEGER (but only with not negative numbers).
++ *
++ * BOOLEAN: VALUE must be the null terminated string "TRUE" or
++ *   "FALSE" and LEN != 0.
++ *
++ *            value="TRUE" , len=1 -> boolean=TRUE.
++ *            value="FALSE" , len=1 -> boolean=FALSE.
++ *
++ * OBJECT IDENTIFIER: VALUE must be a null terminated string with
++ *   each number separated by a dot (e.g. "1.2.3.543.1").  LEN != 0.
++ *
++ *            value="1 2 840 10040 4 3" , len=1 -> OID=dsa-with-sha.
++ *
++ * UTCTime: VALUE must be a null terminated string in one of these
++ *   formats: "YYMMDDhhmmssZ", "YYMMDDhhmmssZ",
++ *   "YYMMDDhhmmss+hh'mm'", "YYMMDDhhmmss-hh'mm'",
++ *   "YYMMDDhhmm+hh'mm'", or "YYMMDDhhmm-hh'mm'".  LEN != 0.
++ *
++ *            value="9801011200Z" , len=1 -> time=Jannuary 1st, 1998
++ *            at 12h 00m Greenwich Mean Time
++ *
++ * GeneralizedTime: VALUE must be in one of this format:
++ *   "YYYYMMDDhhmmss.sZ", "YYYYMMDDhhmmss.sZ",
++ *   "YYYYMMDDhhmmss.s+hh'mm'", "YYYYMMDDhhmmss.s-hh'mm'",
++ *   "YYYYMMDDhhmm+hh'mm'", or "YYYYMMDDhhmm-hh'mm'" where ss.s
++ *   indicates the seconds with any precision like "10.1" or "01.02".
++ *   LEN != 0
++ *
++ *            value="2001010112001.12-0700" , len=1 -> time=Jannuary
++ *            1st, 2001 at 12h 00m 01.12s Pacific Daylight Time
++ *
++ * OCTET STRING: VALUE contains the octet string and LEN is the
++ *   number of octets.
++ *
++ *            value="$\backslash$x01$\backslash$x02$\backslash$x03" ,
++ *            len=3 -> three bytes octet string
++ *
++ * GeneralString: VALUE contains the generalstring and LEN is the
++ *   number of octets.
++ *
++ *            value="$\backslash$x01$\backslash$x02$\backslash$x03" ,
++ *            len=3 -> three bytes generalstring
++ *
++ * BIT STRING: VALUE contains the bit string organized by bytes and
++ *   LEN is the number of bits.
++ *
++ *   value="$\backslash$xCF" , len=6 -> bit string="110011" (six
++ *   bits)
++ *
++ * CHOICE: if NAME indicates a choice type, VALUE must specify one of
++ *   the alternatives with a null terminated string. LEN != 0. Using
++ *   "pkix.asn"\:
++ *
++ *           result=asn1_write_value(cert,
++ *           "certificate1.tbsCertificate.subject", "rdnSequence",
++ *           1);
++ *
++ * ANY: VALUE indicates the der encoding of a structure.  LEN != 0.
++ *
++ * SEQUENCE OF: VALUE must be the null terminated string "NEW" and
++ *   LEN != 0. With this instruction another element is appended in
++ *   the sequence. The name of this element will be "?1" if it's the
++ *   first one, "?2" for the second and so on.
++ *
++ *   Using "pkix.asn"\:
++ *
++ *   result=asn1_write_value(cert,
++ *   "certificate1.tbsCertificate.subject.rdnSequence", "NEW", 1);
++ *
++ * SET OF: the same as SEQUENCE OF.  Using "pkix.asn":
++ *
++ *           result=asn1_write_value(cert,
++ *           "tbsCertificate.subject.rdnSequence.?LAST", "NEW", 1);
++ *
++ * Returns: %ASN1_SUCCESS if the value was set,
++ *   %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element, and
++ *   %ASN1_VALUE_NOT_VALID if @ivalue has a wrong format.
++ **/
++int
++asn1_write_value (asn1_node node_root, const char *name,
++		  const void *ivalue, int len)
++{
++  asn1_node node, p, p2;
++  unsigned char *temp, *value_temp = NULL, *default_temp = NULL;
++  int len2, k, k2, negative;
++  size_t i;
++  const unsigned char *value = ivalue;
++  unsigned int type;
++
++  node = asn1_find_node (node_root, name);
++  if (node == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  if ((node->type & CONST_OPTION) && (value == NULL) && (len == 0))
++    {
++      asn1_delete_structure (&node);
++      return ASN1_SUCCESS;
++    }
++
++  type = type_field (node->type);
++
++  if ((type == ASN1_ETYPE_SEQUENCE_OF || type == ASN1_ETYPE_SET_OF) && (value == NULL) && (len == 0))
++    {
++      p = node->down;
++      while ((type_field (p->type) == ASN1_ETYPE_TAG)
++	     || (type_field (p->type) == ASN1_ETYPE_SIZE))
++	p = p->right;
++
++      while (p->right)
++	asn1_delete_structure (&p->right);
++
++      return ASN1_SUCCESS;
++    }
++
++  /* Don't allow element deletion for other types */
++  if (value == NULL)
++    {
++      return ASN1_VALUE_NOT_VALID;
++    }
++
++  switch (type)
++    {
++    case ASN1_ETYPE_BOOLEAN:
++      if (!_asn1_strcmp (value, "TRUE"))
++	{
++	  if (node->type & CONST_DEFAULT)
++	    {
++	      p = node->down;
++	      while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
++		p = p->right;
++	      if (p->type & CONST_TRUE)
++		_asn1_set_value (node, NULL, 0);
++	      else
++		_asn1_set_value (node, "T", 1);
++	    }
++	  else
++	    _asn1_set_value (node, "T", 1);
++	}
++      else if (!_asn1_strcmp (value, "FALSE"))
++	{
++	  if (node->type & CONST_DEFAULT)
++	    {
++	      p = node->down;
++	      while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
++		p = p->right;
++	      if (p->type & CONST_FALSE)
++		_asn1_set_value (node, NULL, 0);
++	      else
++		_asn1_set_value (node, "F", 1);
++	    }
++	  else
++	    _asn1_set_value (node, "F", 1);
++	}
++      else
++	return ASN1_VALUE_NOT_VALID;
++      break;
++    case ASN1_ETYPE_INTEGER:
++    case ASN1_ETYPE_ENUMERATED:
++      if (len == 0)
++	{
++	  if ((c_isdigit (value[0])) || (value[0] == '-'))
++	    {
++	      value_temp = malloc (SIZEOF_UNSIGNED_LONG_INT);
++	      if (value_temp == NULL)
++		return ASN1_MEM_ALLOC_ERROR;
++
++	      _asn1_convert_integer (value, value_temp,
++				     SIZEOF_UNSIGNED_LONG_INT, &len);
++	    }
++	  else
++	    {			/* is an identifier like v1 */
++	      if (!(node->type & CONST_LIST))
++		return ASN1_VALUE_NOT_VALID;
++	      p = node->down;
++	      while (p)
++		{
++		  if (type_field (p->type) == ASN1_ETYPE_CONSTANT)
++		    {
++		      if (!_asn1_strcmp (p->name, value))
++			{
++			  value_temp = malloc (SIZEOF_UNSIGNED_LONG_INT);
++			  if (value_temp == NULL)
++			    return ASN1_MEM_ALLOC_ERROR;
++
++			  _asn1_convert_integer (p->value,
++						 value_temp,
++						 SIZEOF_UNSIGNED_LONG_INT,
++						 &len);
++			  break;
++			}
++		    }
++		  p = p->right;
++		}
++	      if (p == NULL)
++		return ASN1_VALUE_NOT_VALID;
++	    }
++	}
++      else
++	{			/* len != 0 */
++	  value_temp = malloc (len);
++	  if (value_temp == NULL)
++	    return ASN1_MEM_ALLOC_ERROR;
++	  memcpy (value_temp, value, len);
++	}
++
++      if (value_temp[0] & 0x80)
++	negative = 1;
++      else
++	negative = 0;
++
++      if (negative && (type_field (node->type) == ASN1_ETYPE_ENUMERATED))
++	{
++	  free (value_temp);
++	  return ASN1_VALUE_NOT_VALID;
++	}
++
++      for (k = 0; k < len - 1; k++)
++	if (negative && (value_temp[k] != 0xFF))
++	  break;
++	else if (!negative && value_temp[k])
++	  break;
++
++      if ((negative && !(value_temp[k] & 0x80)) ||
++	  (!negative && (value_temp[k] & 0x80)))
++	k--;
++
++      _asn1_set_value_lv (node, value_temp + k, len - k);
++
++      if (node->type & CONST_DEFAULT)
++	{
++	  p = node->down;
++	  while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
++	    p = p->right;
++	  if ((c_isdigit (p->value[0])) || (p->value[0] == '-'))
++	    {
++	      default_temp = malloc (SIZEOF_UNSIGNED_LONG_INT);
++	      if (default_temp == NULL)
++		{
++		  free (value_temp);
++		  return ASN1_MEM_ALLOC_ERROR;
++		}
++
++	      _asn1_convert_integer (p->value, default_temp,
++				     SIZEOF_UNSIGNED_LONG_INT, &len2);
++	    }
++	  else
++	    {			/* is an identifier like v1 */
++	      if (!(node->type & CONST_LIST))
++		{
++		  free (value_temp);
++		  return ASN1_VALUE_NOT_VALID;
++		}
++	      p2 = node->down;
++	      while (p2)
++		{
++		  if (type_field (p2->type) == ASN1_ETYPE_CONSTANT)
++		    {
++		      if (!_asn1_strcmp (p2->name, p->value))
++			{
++			  default_temp = malloc (SIZEOF_UNSIGNED_LONG_INT);
++			  if (default_temp == NULL)
++			    {
++			      free (value_temp);
++			      return ASN1_MEM_ALLOC_ERROR;
++			    }
++
++			  _asn1_convert_integer (p2->value,
++						 default_temp,
++						 SIZEOF_UNSIGNED_LONG_INT,
++						 &len2);
++			  break;
++			}
++		    }
++		  p2 = p2->right;
++		}
++	      if (p2 == NULL)
++		{
++		  free (value_temp);
++		  return ASN1_VALUE_NOT_VALID;
++		}
++	    }
++
++
++	  if ((len - k) == len2)
++	    {
++	      for (k2 = 0; k2 < len2; k2++)
++		if (value_temp[k + k2] != default_temp[k2])
++		  {
++		    break;
++		  }
++	      if (k2 == len2)
++		_asn1_set_value (node, NULL, 0);
++	    }
++	  free (default_temp);
++	}
++      free (value_temp);
++      break;
++    case ASN1_ETYPE_OBJECT_ID:
++      for (i = 0; i < _asn1_strlen (value); i++)
++	if ((!c_isdigit (value[i])) && (value[i] != '.') && (value[i] != '+'))
++	  return ASN1_VALUE_NOT_VALID;
++      if (node->type & CONST_DEFAULT)
++	{
++	  p = node->down;
++	  while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
++	    p = p->right;
++	  if (!_asn1_strcmp (value, p->value))
++	    {
++	      _asn1_set_value (node, NULL, 0);
++	      break;
++	    }
++	}
++      _asn1_set_value (node, value, _asn1_strlen (value) + 1);
++      break;
++    case ASN1_ETYPE_UTC_TIME:
++      {
++	len = _asn1_strlen (value);
++	if (len < 11)
++	  return ASN1_VALUE_NOT_VALID;
++	for (k = 0; k < 10; k++)
++	  if (!c_isdigit (value[k]))
++	    return ASN1_VALUE_NOT_VALID;
++	switch (len)
++	  {
++	  case 11:
++	    if (value[10] != 'Z')
++	      return ASN1_VALUE_NOT_VALID;
++	    break;
++	  case 13:
++	    if ((!c_isdigit (value[10])) || (!c_isdigit (value[11])) ||
++		(value[12] != 'Z'))
++	      return ASN1_VALUE_NOT_VALID;
++	    break;
++	  case 15:
++	    if ((value[10] != '+') && (value[10] != '-'))
++	      return ASN1_VALUE_NOT_VALID;
++	    for (k = 11; k < 15; k++)
++	      if (!c_isdigit (value[k]))
++		return ASN1_VALUE_NOT_VALID;
++	    break;
++	  case 17:
++	    if ((!c_isdigit (value[10])) || (!c_isdigit (value[11])))
++	      return ASN1_VALUE_NOT_VALID;
++	    if ((value[12] != '+') && (value[12] != '-'))
++	      return ASN1_VALUE_NOT_VALID;
++	    for (k = 13; k < 17; k++)
++	      if (!c_isdigit (value[k]))
++		return ASN1_VALUE_NOT_VALID;
++	    break;
++	  default:
++	    return ASN1_VALUE_NOT_FOUND;
++	  }
++	_asn1_set_value (node, value, len);
++      }
++      break;
++    case ASN1_ETYPE_GENERALIZED_TIME:
++      len = _asn1_strlen (value);
++      _asn1_set_value (node, value, len);
++      break;
++    case ASN1_ETYPE_OCTET_STRING:
++    case ASN1_ETYPE_GENERALSTRING:
++    case ASN1_ETYPE_NUMERIC_STRING:
++    case ASN1_ETYPE_IA5_STRING:
++    case ASN1_ETYPE_TELETEX_STRING:
++    case ASN1_ETYPE_PRINTABLE_STRING:
++    case ASN1_ETYPE_UNIVERSAL_STRING:
++    case ASN1_ETYPE_BMP_STRING:
++    case ASN1_ETYPE_UTF8_STRING:
++    case ASN1_ETYPE_VISIBLE_STRING:
++      if (len == 0)
++	len = _asn1_strlen (value);
++      _asn1_set_value_lv (node, value, len);
++      break;
++    case ASN1_ETYPE_BIT_STRING:
++      if (len == 0)
++	len = _asn1_strlen (value);
++      asn1_length_der ((len >> 3) + 2, NULL, &len2);
++      temp = malloc ((len >> 3) + 2 + len2);
++      if (temp == NULL)
++	return ASN1_MEM_ALLOC_ERROR;
++
++      asn1_bit_der (value, len, temp, &len2);
++      _asn1_set_value_m (node, temp, len2);
++      temp = NULL;
++      break;
++    case ASN1_ETYPE_CHOICE:
++      p = node->down;
++      while (p)
++	{
++	  if (!_asn1_strcmp (p->name, value))
++	    {
++	      p2 = node->down;
++	      while (p2)
++		{
++		  if (p2 != p)
++		    {
++		      asn1_delete_structure (&p2);
++		      p2 = node->down;
++		    }
++		  else
++		    p2 = p2->right;
++		}
++	      break;
++	    }
++	  p = p->right;
++	}
++      if (!p)
++	return ASN1_ELEMENT_NOT_FOUND;
++      break;
++    case ASN1_ETYPE_ANY:
++      _asn1_set_value_lv (node, value, len);
++      break;
++    case ASN1_ETYPE_SEQUENCE_OF:
++    case ASN1_ETYPE_SET_OF:
++      if (_asn1_strcmp (value, "NEW"))
++	return ASN1_VALUE_NOT_VALID;
++      _asn1_append_sequence_set (node, NULL);
++      break;
++    default:
++      return ASN1_ELEMENT_NOT_FOUND;
++      break;
++    }
++
++  return ASN1_SUCCESS;
++}
++
++
++#define PUT_VALUE( ptr, ptr_size, data, data_size) \
++	*len = data_size; \
++	if (ptr_size < data_size) { \
++		return ASN1_MEM_ERROR; \
++	} else { \
++		if (ptr && data_size > 0) \
++		  memcpy (ptr, data, data_size); \
++	}
++
++#define PUT_STR_VALUE( ptr, ptr_size, data) \
++	*len = _asn1_strlen (data) + 1; \
++	if (ptr_size < *len) { \
++		return ASN1_MEM_ERROR; \
++	} else { \
++		/* this strcpy is checked */ \
++		if (ptr) { \
++		  _asn1_strcpy (ptr, data); \
++		} \
++	}
++
++#define PUT_AS_STR_VALUE( ptr, ptr_size, data, data_size) \
++	*len = data_size + 1; \
++	if (ptr_size < *len) { \
++		return ASN1_MEM_ERROR; \
++	} else { \
++		/* this strcpy is checked */ \
++		if (ptr) { \
++		  if (data_size > 0) \
++		    memcpy (ptr, data, data_size); \
++		  ptr[data_size] = 0; \
++		} \
++	}
++
++#define ADD_STR_VALUE( ptr, ptr_size, data) \
++        *len += _asn1_strlen(data); \
++        if (ptr_size < (int) *len) { \
++                (*len)++; \
++                return ASN1_MEM_ERROR; \
++        } else { \
++                /* this strcat is checked */ \
++                if (ptr) _asn1_strcat (ptr, data); \
++        }
++
++/**
++ * asn1_read_value:
++ * @root: pointer to a structure.
++ * @name: the name of the element inside a structure that you want to read.
++ * @ivalue: vector that will contain the element's content, must be a
++ *   pointer to memory cells already allocated (may be %NULL).
++ * @len: number of bytes of *value: value[0]..value[len-1]. Initialy
++ *   holds the sizeof value.
++ *
++ * Returns the value of one element inside a structure.
++ * If an element is OPTIONAL and this returns
++ * %ASN1_ELEMENT_NOT_FOUND, it means that this element wasn't present
++ * in the der encoding that created the structure.  The first element
++ * of a SEQUENCE_OF or SET_OF is named "?1". The second one "?2" and
++ * so on. If the @root provided is a node to specific sequence element,
++ * then the keyword "?CURRENT" is also acceptable and indicates the
++ * current sequence element of this node.
++ *
++ * Note that there can be valid values with length zero. In these case
++ * this function will succeed and @len will be zero.
++ *
++ * INTEGER: VALUE will contain a two's complement form integer.
++ *
++ *            integer=-1  -> value[0]=0xFF , len=1.
++ *            integer=1   -> value[0]=0x01 , len=1.
++ *
++ * ENUMERATED: As INTEGER (but only with not negative numbers).
++ *
++ * BOOLEAN: VALUE will be the null terminated string "TRUE" or
++ *   "FALSE" and LEN=5 or LEN=6.
++ *
++ * OBJECT IDENTIFIER: VALUE will be a null terminated string with
++ *   each number separated by a dot (i.e. "1.2.3.543.1").
++ *
++ *                      LEN = strlen(VALUE)+1
++ *
++ * UTCTime: VALUE will be a null terminated string in one of these
++ *   formats: "YYMMDDhhmmss+hh'mm'" or "YYMMDDhhmmss-hh'mm'".
++ *   LEN=strlen(VALUE)+1.
++ *
++ * GeneralizedTime: VALUE will be a null terminated string in the
++ *   same format used to set the value.
++ *
++ * OCTET STRING: VALUE will contain the octet string and LEN will be
++ *   the number of octets.
++ *
++ * GeneralString: VALUE will contain the generalstring and LEN will
++ *   be the number of octets.
++ *
++ * BIT STRING: VALUE will contain the bit string organized by bytes
++ *   and LEN will be the number of bits.
++ *
++ * CHOICE: If NAME indicates a choice type, VALUE will specify the
++ *   alternative selected.
++ *
++ * ANY: If NAME indicates an any type, VALUE will indicate the DER
++ *   encoding of the structure actually used.
++ *
++ * Returns: %ASN1_SUCCESS if value is returned,
++ *   %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element,
++ *   %ASN1_VALUE_NOT_FOUND if there isn't any value for the element
++ *   selected, and %ASN1_MEM_ERROR if The value vector isn't big enough
++ *   to store the result, and in this case @len will contain the number of
++ *   bytes needed. On the occasion that the stored data are of zero-length
++ *   this function may return %ASN1_SUCCESS even if the provided @len is zero.
++ **/
++int
++asn1_read_value (asn1_node_const root, const char *name, void *ivalue, int *len)
++{
++  return asn1_read_value_type (root, name, ivalue, len, NULL);
++}
++
++/**
++ * asn1_read_value_type:
++ * @root: pointer to a structure.
++ * @name: the name of the element inside a structure that you want to read.
++ * @ivalue: vector that will contain the element's content, must be a
++ *   pointer to memory cells already allocated (may be %NULL).
++ * @len: number of bytes of *value: value[0]..value[len-1]. Initialy
++ *   holds the sizeof value.
++ * @etype: The type of the value read (ASN1_ETYPE)
++ *
++ * Returns the type and value of one element inside a structure.
++ * If an element is OPTIONAL and this returns
++ * %ASN1_ELEMENT_NOT_FOUND, it means that this element wasn't present
++ * in the der encoding that created the structure.  The first element
++ * of a SEQUENCE_OF or SET_OF is named "?1". The second one "?2" and
++ * so on. If the @root provided is a node to specific sequence element,
++ * then the keyword "?CURRENT" is also acceptable and indicates the
++ * current sequence element of this node.
++ *
++ * Note that there can be valid values with length zero. In these case
++ * this function will succeed and @len will be zero.
++ *
++ *
++ * INTEGER: VALUE will contain a two's complement form integer.
++ *
++ *            integer=-1  -> value[0]=0xFF , len=1.
++ *            integer=1   -> value[0]=0x01 , len=1.
++ *
++ * ENUMERATED: As INTEGER (but only with not negative numbers).
++ *
++ * BOOLEAN: VALUE will be the null terminated string "TRUE" or
++ *   "FALSE" and LEN=5 or LEN=6.
++ *
++ * OBJECT IDENTIFIER: VALUE will be a null terminated string with
++ *   each number separated by a dot (i.e. "1.2.3.543.1").
++ *
++ *                      LEN = strlen(VALUE)+1
++ *
++ * UTCTime: VALUE will be a null terminated string in one of these
++ *   formats: "YYMMDDhhmmss+hh'mm'" or "YYMMDDhhmmss-hh'mm'".
++ *   LEN=strlen(VALUE)+1.
++ *
++ * GeneralizedTime: VALUE will be a null terminated string in the
++ *   same format used to set the value.
++ *
++ * OCTET STRING: VALUE will contain the octet string and LEN will be
++ *   the number of octets.
++ *
++ * GeneralString: VALUE will contain the generalstring and LEN will
++ *   be the number of octets.
++ *
++ * BIT STRING: VALUE will contain the bit string organized by bytes
++ *   and LEN will be the number of bits.
++ *
++ * CHOICE: If NAME indicates a choice type, VALUE will specify the
++ *   alternative selected.
++ *
++ * ANY: If NAME indicates an any type, VALUE will indicate the DER
++ *   encoding of the structure actually used.
++ *
++ * Returns: %ASN1_SUCCESS if value is returned,
++ *   %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element,
++ *   %ASN1_VALUE_NOT_FOUND if there isn't any value for the element
++ *   selected, and %ASN1_MEM_ERROR if The value vector isn't big enough
++ *   to store the result, and in this case @len will contain the number of
++ *   bytes needed. On the occasion that the stored data are of zero-length
++ *   this function may return %ASN1_SUCCESS even if the provided @len is zero.
++ **/
++int
++asn1_read_value_type (asn1_node_const root, const char *name, void *ivalue,
++		      int *len, unsigned int *etype)
++{
++  asn1_node_const node, p, p2;
++  int len2, len3, result;
++  int value_size = *len;
++  unsigned char *value = ivalue;
++  unsigned type;
++
++  node = asn1_find_node (root, name);
++  if (node == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  type = type_field (node->type);
++
++  if ((type != ASN1_ETYPE_NULL) &&
++      (type != ASN1_ETYPE_CHOICE) &&
++      !(node->type & CONST_DEFAULT) && !(node->type & CONST_ASSIGN) &&
++      (node->value == NULL))
++    return ASN1_VALUE_NOT_FOUND;
++
++  if (etype)
++    *etype = type;
++  switch (type)
++    {
++    case ASN1_ETYPE_NULL:
++      PUT_STR_VALUE (value, value_size, "NULL");
++      break;
++    case ASN1_ETYPE_BOOLEAN:
++      if ((node->type & CONST_DEFAULT) && (node->value == NULL))
++	{
++	  p = node->down;
++	  while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
++	    p = p->right;
++	  if (p->type & CONST_TRUE)
++	    {
++	      PUT_STR_VALUE (value, value_size, "TRUE");
++	    }
++	  else
++	    {
++	      PUT_STR_VALUE (value, value_size, "FALSE");
++	    }
++	}
++      else if (node->value[0] == 'T')
++	{
++	  PUT_STR_VALUE (value, value_size, "TRUE");
++	}
++      else
++	{
++	  PUT_STR_VALUE (value, value_size, "FALSE");
++	}
++      break;
++    case ASN1_ETYPE_INTEGER:
++    case ASN1_ETYPE_ENUMERATED:
++      if ((node->type & CONST_DEFAULT) && (node->value == NULL))
++	{
++	  p = node->down;
++	  while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
++	    p = p->right;
++	  if ((c_isdigit (p->value[0])) || (p->value[0] == '-')
++	      || (p->value[0] == '+'))
++	    {
++	      result = _asn1_convert_integer
++		  (p->value, value, value_size, len);
++              if (result != ASN1_SUCCESS)
++		return result;
++	    }
++	  else
++	    {			/* is an identifier like v1 */
++	      p2 = node->down;
++	      while (p2)
++		{
++		  if (type_field (p2->type) == ASN1_ETYPE_CONSTANT)
++		    {
++		      if (!_asn1_strcmp (p2->name, p->value))
++			{
++			  result = _asn1_convert_integer
++			      (p2->value, value, value_size,
++			       len);
++			  if (result != ASN1_SUCCESS)
++			    return result;
++			  break;
++			}
++		    }
++		  p2 = p2->right;
++		}
++	    }
++	}
++      else
++	{
++	  len2 = -1;
++	  result = asn1_get_octet_der
++	      (node->value, node->value_len, &len2, value, value_size,
++	       len);
++          if (result != ASN1_SUCCESS)
++	    return result;
++	}
++      break;
++    case ASN1_ETYPE_OBJECT_ID:
++      if (node->type & CONST_ASSIGN)
++	{
++	  *len = 0;
++	  if (value)
++	    value[0] = 0;
++	  p = node->down;
++	  while (p)
++	    {
++	      if (type_field (p->type) == ASN1_ETYPE_CONSTANT)
++		{
++		  ADD_STR_VALUE (value, value_size, p->value);
++		  if (p->right)
++		    {
++		      ADD_STR_VALUE (value, value_size, ".");
++		    }
++		}
++	      p = p->right;
++	    }
++	  (*len)++;
++	}
++      else if ((node->type & CONST_DEFAULT) && (node->value == NULL))
++	{
++	  p = node->down;
++	  while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
++	    p = p->right;
++	  PUT_STR_VALUE (value, value_size, p->value);
++	}
++      else
++	{
++	  PUT_STR_VALUE (value, value_size, node->value);
++	}
++      break;
++    case ASN1_ETYPE_GENERALIZED_TIME:
++    case ASN1_ETYPE_UTC_TIME:
++      PUT_AS_STR_VALUE (value, value_size, node->value, node->value_len);
++      break;
++    case ASN1_ETYPE_OCTET_STRING:
++    case ASN1_ETYPE_GENERALSTRING:
++    case ASN1_ETYPE_NUMERIC_STRING:
++    case ASN1_ETYPE_IA5_STRING:
++    case ASN1_ETYPE_TELETEX_STRING:
++    case ASN1_ETYPE_PRINTABLE_STRING:
++    case ASN1_ETYPE_UNIVERSAL_STRING:
++    case ASN1_ETYPE_BMP_STRING:
++    case ASN1_ETYPE_UTF8_STRING:
++    case ASN1_ETYPE_VISIBLE_STRING:
++      len2 = -1;
++      result = asn1_get_octet_der
++	  (node->value, node->value_len, &len2, value, value_size,
++	   len);
++      if (result != ASN1_SUCCESS)
++	return result;
++      break;
++    case ASN1_ETYPE_BIT_STRING:
++      len2 = -1;
++      result = asn1_get_bit_der
++	  (node->value, node->value_len, &len2, value, value_size,
++	   len);
++      if (result != ASN1_SUCCESS)
++	return result;
++      break;
++    case ASN1_ETYPE_CHOICE:
++      PUT_STR_VALUE (value, value_size, node->down->name);
++      break;
++    case ASN1_ETYPE_ANY:
++      len3 = -1;
++      len2 = asn1_get_length_der (node->value, node->value_len, &len3);
++      if (len2 < 0)
++	return ASN1_DER_ERROR;
++      PUT_VALUE (value, value_size, node->value + len3, len2);
++      break;
++    default:
++      return ASN1_ELEMENT_NOT_FOUND;
++      break;
++    }
++  return ASN1_SUCCESS;
++}
++
++
++/**
++ * asn1_read_tag:
++ * @root: pointer to a structure
++ * @name: the name of the element inside a structure.
++ * @tagValue:  variable that will contain the TAG value.
++ * @classValue: variable that will specify the TAG type.
++ *
++ * Returns the TAG and the CLASS of one element inside a structure.
++ * CLASS can have one of these constants: %ASN1_CLASS_APPLICATION,
++ * %ASN1_CLASS_UNIVERSAL, %ASN1_CLASS_PRIVATE or
++ * %ASN1_CLASS_CONTEXT_SPECIFIC.
++ *
++ * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if
++ *   @name is not a valid element.
++ **/
++int
++asn1_read_tag (asn1_node_const root, const char *name, int *tagValue,
++	       int *classValue)
++{
++  asn1_node node, p, pTag;
++
++  node = asn1_find_node (root, name);
++  if (node == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  p = node->down;
++
++  /* pTag will points to the IMPLICIT TAG */
++  pTag = NULL;
++  if (node->type & CONST_TAG)
++    {
++      while (p)
++	{
++	  if (type_field (p->type) == ASN1_ETYPE_TAG)
++	    {
++	      if ((p->type & CONST_IMPLICIT) && (pTag == NULL))
++		pTag = p;
++	      else if (p->type & CONST_EXPLICIT)
++		pTag = NULL;
++	    }
++	  p = p->right;
++	}
++    }
++
++  if (pTag)
++    {
++      *tagValue = _asn1_strtoul (pTag->value, NULL, 10);
++
++      if (pTag->type & CONST_APPLICATION)
++	*classValue = ASN1_CLASS_APPLICATION;
++      else if (pTag->type & CONST_UNIVERSAL)
++	*classValue = ASN1_CLASS_UNIVERSAL;
++      else if (pTag->type & CONST_PRIVATE)
++	*classValue = ASN1_CLASS_PRIVATE;
++      else
++	*classValue = ASN1_CLASS_CONTEXT_SPECIFIC;
++    }
++  else
++    {
++      unsigned type = type_field (node->type);
++      *classValue = ASN1_CLASS_UNIVERSAL;
++
++      switch (type)
++	{
++	CASE_HANDLED_ETYPES:
++	  *tagValue = _asn1_tags[type].tag;
++	  break;
++	case ASN1_ETYPE_TAG:
++	case ASN1_ETYPE_CHOICE:
++	case ASN1_ETYPE_ANY:
++	  *tagValue = -1;
++	  break;
++	default:
++	  break;
++	}
++    }
++
++  return ASN1_SUCCESS;
++}
++
++/**
++ * asn1_read_node_value:
++ * @node: pointer to a node.
++ * @data: a point to a asn1_data_node_st
++ *
++ * Returns the value a data node inside a asn1_node structure.
++ * The data returned should be handled as constant values.
++ *
++ * Returns: %ASN1_SUCCESS if the node exists.
++ **/
++int
++asn1_read_node_value (asn1_node_const node, asn1_data_node_st * data)
++{
++  data->name = node->name;
++  data->value = node->value;
++  data->value_len = node->value_len;
++  data->type = type_field (node->type);
++
++  return ASN1_SUCCESS;
++}
+diff --git a/grub-core/lib/libtasn1/lib/errors.c b/grub-core/lib/libtasn1/lib/errors.c
+new file mode 100644
+index 00000000000..cee74daf795
+--- /dev/null
++++ b/grub-core/lib/libtasn1/lib/errors.c
+@@ -0,0 +1,100 @@
++/*
++ * Copyright (C) 2002-2014 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * The LIBTASN1 library is free software; you can redistribute it
++ * and/or modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301, USA
++ */
++
++#include <int.h>
++#ifdef STDC_HEADERS
++#include <stdarg.h>
++#endif
++
++#define LIBTASN1_ERROR_ENTRY(name) { #name, name }
++
++struct libtasn1_error_entry
++{
++  const char *name;
++  int number;
++};
++typedef struct libtasn1_error_entry libtasn1_error_entry;
++
++static const libtasn1_error_entry error_algorithms[] = {
++  LIBTASN1_ERROR_ENTRY (ASN1_SUCCESS),
++  LIBTASN1_ERROR_ENTRY (ASN1_FILE_NOT_FOUND),
++  LIBTASN1_ERROR_ENTRY (ASN1_ELEMENT_NOT_FOUND),
++  LIBTASN1_ERROR_ENTRY (ASN1_IDENTIFIER_NOT_FOUND),
++  LIBTASN1_ERROR_ENTRY (ASN1_DER_ERROR),
++  LIBTASN1_ERROR_ENTRY (ASN1_VALUE_NOT_FOUND),
++  LIBTASN1_ERROR_ENTRY (ASN1_GENERIC_ERROR),
++  LIBTASN1_ERROR_ENTRY (ASN1_VALUE_NOT_VALID),
++  LIBTASN1_ERROR_ENTRY (ASN1_TAG_ERROR),
++  LIBTASN1_ERROR_ENTRY (ASN1_TAG_IMPLICIT),
++  LIBTASN1_ERROR_ENTRY (ASN1_ERROR_TYPE_ANY),
++  LIBTASN1_ERROR_ENTRY (ASN1_SYNTAX_ERROR),
++  LIBTASN1_ERROR_ENTRY (ASN1_MEM_ERROR),
++  LIBTASN1_ERROR_ENTRY (ASN1_MEM_ALLOC_ERROR),
++  LIBTASN1_ERROR_ENTRY (ASN1_DER_OVERFLOW),
++  LIBTASN1_ERROR_ENTRY (ASN1_NAME_TOO_LONG),
++  LIBTASN1_ERROR_ENTRY (ASN1_ARRAY_ERROR),
++  LIBTASN1_ERROR_ENTRY (ASN1_ELEMENT_NOT_EMPTY),
++  LIBTASN1_ERROR_ENTRY (ASN1_TIME_ENCODING_ERROR),
++  LIBTASN1_ERROR_ENTRY (ASN1_RECURSION),
++  {0, 0}
++};
++
++/**
++ * asn1_perror:
++ * @error: is an error returned by a libtasn1 function.
++ *
++ * Prints a string to stderr with a description of an error.  This
++ * function is like perror().  The only difference is that it accepts
++ * an error returned by a libtasn1 function.
++ *
++ * Since: 1.6
++ **/
++void
++asn1_perror (int error)
++{
++  const char *str = asn1_strerror (error);
++  fprintf (stderr, "LIBTASN1 ERROR: %s\n", str ? str : "(null)");
++}
++
++/**
++ * asn1_strerror:
++ * @error: is an error returned by a libtasn1 function.
++ *
++ * Returns a string with a description of an error.  This function is
++ * similar to strerror.  The only difference is that it accepts an
++ * error (number) returned by a libtasn1 function.
++ *
++ * Returns: Pointer to static zero-terminated string describing error
++ *   code.
++ *
++ * Since: 1.6
++ **/
++const char *
++asn1_strerror (int error)
++{
++  const libtasn1_error_entry *p;
++
++  for (p = error_algorithms; p->name != NULL; p++)
++    if (p->number == error)
++      return p->name + sizeof ("ASN1_") - 1;
++
++  return NULL;
++}
+diff --git a/grub-core/lib/libtasn1/lib/gstr.c b/grub-core/lib/libtasn1/lib/gstr.c
+new file mode 100644
+index 00000000000..e91a3a151c0
+--- /dev/null
++++ b/grub-core/lib/libtasn1/lib/gstr.c
+@@ -0,0 +1,74 @@
++/*
++ * Copyright (C) 2002-2014 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * The LIBTASN1 library is free software; you can redistribute it
++ * and/or modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301, USA
++ */
++
++#include <int.h>
++#include "gstr.h"
++
++/* These function are like strcat, strcpy. They only
++ * do bounds checking (they shouldn't cause buffer overruns),
++ * and they always produce null terminated strings.
++ *
++ * They should be used only with null terminated strings.
++ */
++void
++_asn1_str_cat (char *dest, size_t dest_tot_size, const char *src)
++{
++  size_t str_size = strlen (src);
++  size_t dest_size = strlen (dest);
++
++  if (dest_tot_size - dest_size > str_size)
++    {
++      strcat (dest, src);
++    }
++  else
++    {
++      if (dest_tot_size - dest_size > 0)
++	{
++	  strncat (dest, src, (dest_tot_size - dest_size) - 1);
++	  dest[dest_tot_size - 1] = 0;
++	}
++    }
++}
++
++/* Returns the bytes copied (not including the null terminator) */
++unsigned int
++_asn1_str_cpy (char *dest, size_t dest_tot_size, const char *src)
++{
++  size_t str_size = strlen (src);
++
++  if (dest_tot_size > str_size)
++    {
++      strcpy (dest, src);
++      return str_size;
++    }
++  else
++    {
++      if (dest_tot_size > 0)
++	{
++	  str_size = dest_tot_size - 1;
++	  memcpy (dest, src, str_size);
++	  dest[str_size] = 0;
++	  return str_size;
++	}
++      else
++	return 0;
++    }
++}
+diff --git a/grub-core/lib/libtasn1/lib/parser_aux.c b/grub-core/lib/libtasn1/lib/parser_aux.c
+new file mode 100644
+index 00000000000..d5dbbf8765d
+--- /dev/null
++++ b/grub-core/lib/libtasn1/lib/parser_aux.c
+@@ -0,0 +1,1173 @@
++/*
++ * Copyright (C) 2000-2016 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * The LIBTASN1 library is free software; you can redistribute it
++ * and/or modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301, USA
++ */
++
++#include <limits.h> // WORD_BIT
++
++#include "int.h"
++#include "parser_aux.h"
++#include "gstr.h"
++#include "structure.h"
++#include "element.h"
++#include "c-ctype.h"
++
++char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1];	/* identifier name not found */
++
++/* Return a hash of the N bytes of X using the method described by
++   Bruno Haible in https://www.haible.de/bruno/hashfunc.html.
++   Note that while many hash functions reduce their result via modulo
++   to a 0..table_size-1 range, this function does not do that.
++
++   This implementation has been changed from size_t -> unsigned int. */
++
++#ifdef __clang__
++__attribute__((no_sanitize("integer")))
++#endif
++_GL_ATTRIBUTE_PURE
++static unsigned int
++_asn1_hash_name (const char *x)
++{
++  const unsigned char *s = (unsigned char *) x;
++  unsigned h = 0;
++
++  while (*s)
++    h = (*s++) + ((h << 9) | (h >> (WORD_BIT - 9)));
++
++  return h;
++}
++
++/******************************************************/
++/* Function : _asn1_add_static_node                   */
++/* Description: creates a new NODE_ASN element and    */
++/* puts it in the list pointed by e_list.       */
++/* Parameters:                                        */
++/*   e_list: of type list_type; must be NULL initially */
++/*   type: type of the new element (see ASN1_ETYPE_   */
++/*         and CONST_ constants).                     */
++/* Return: pointer to the new element.                */
++/******************************************************/
++asn1_node
++_asn1_add_static_node (list_type **e_list, unsigned int type)
++{
++  list_type *p;
++  asn1_node punt;
++
++  punt = calloc (1, sizeof (struct asn1_node_st));
++  if (punt == NULL)
++    return NULL;
++
++  p = malloc (sizeof (list_type));
++  if (p == NULL)
++    {
++      free (punt);
++      return NULL;
++    }
++
++  p->node = punt;
++  p->next = *e_list;
++  *e_list = p;
++
++  punt->type = type;
++
++  return punt;
++}
++
++static
++int _asn1_add_static_node2 (list_type **e_list, asn1_node node)
++{
++  list_type *p;
++
++  p = malloc (sizeof (list_type));
++  if (p == NULL)
++    {
++      return -1;
++    }
++
++  p->node = node;
++  p->next = *e_list;
++  *e_list = p;
++
++  return 0;
++}
++
++/**
++ * asn1_find_node:
++ * @pointer: NODE_ASN element pointer.
++ * @name: null terminated string with the element's name to find.
++ *
++ * Searches for an element called @name starting from @pointer.  The
++ * name is composed by different identifiers separated by dots.  When
++ * *@pointer has a name, the first identifier must be the name of
++ * *@pointer, otherwise it must be the name of one child of *@pointer.
++ *
++ * Returns: the search result, or %NULL if not found.
++ **/
++asn1_node
++asn1_find_node (asn1_node_const pointer, const char *name)
++{
++  asn1_node_const p;
++  char *n_end, n[ASN1_MAX_NAME_SIZE + 1];
++  const char *n_start;
++  unsigned int nsize;
++  unsigned int nhash;
++
++  if (pointer == NULL)
++    return NULL;
++
++  if (name == NULL)
++    return NULL;
++
++  p = pointer;
++  n_start = name;
++
++  if (name[0] == '?' && name[1] == 'C' && p->name[0] == '?')
++    { /* ?CURRENT */
++      n_start = strchr(n_start, '.');
++      if (n_start)
++        n_start++;
++    }
++  else if (p->name[0] != 0)
++    {				/* has *pointer got a name ? */
++      n_end = strchr (n_start, '.');	/* search the first dot */
++      if (n_end)
++	{
++	  nsize = n_end - n_start;
++	  if (nsize >= sizeof(n))
++		return NULL;
++
++	  memcpy (n, n_start, nsize);
++	  n[nsize] = 0;
++	  n_start = n_end;
++	  n_start++;
++
++	  nhash = _asn1_hash_name (n);
++	}
++      else
++	{
++	  _asn1_str_cpy (n, sizeof (n), n_start);
++	  nhash = _asn1_hash_name (n);
++
++	  n_start = NULL;
++	}
++
++      while (p)
++	{
++	  if (nhash == p->name_hash && (!strcmp (p->name, n)))
++	    break;
++	  else
++	    p = p->right;
++	}			/* while */
++
++      if (p == NULL)
++	return NULL;
++    }
++  else
++    {				/* *pointer doesn't have a name */
++      if (n_start[0] == 0)
++	return (asn1_node) p;
++    }
++
++  while (n_start)
++    {				/* Has the end of NAME been reached? */
++      n_end = strchr (n_start, '.');	/* search the next dot */
++      if (n_end)
++	{
++	  nsize = n_end - n_start;
++	  if (nsize >= sizeof(n))
++		return NULL;
++
++	  memcpy (n, n_start, nsize);
++	  n[nsize] = 0;
++	  n_start = n_end;
++	  n_start++;
++
++	  nhash = _asn1_hash_name (n);
++	}
++      else
++	{
++	  _asn1_str_cpy (n, sizeof (n), n_start);
++	  nhash = _asn1_hash_name (n);
++	  n_start = NULL;
++	}
++
++      if (p->down == NULL)
++	return NULL;
++
++      p = p->down;
++      if (p == NULL)
++        return NULL;
++
++      /* The identifier "?LAST" indicates the last element
++         in the right chain. */
++      if (n[0] == '?' && n[1] == 'L') /* ?LAST */
++	{
++	  while (p->right)
++	    p = p->right;
++	}
++      else
++	{			/* no "?LAST" */
++	  while (p)
++	    {
++	      if (p->name_hash == nhash && !strcmp (p->name, n))
++		break;
++	      else
++		p = p->right;
++	    }
++	}
++      if (p == NULL)
++        return NULL;
++    }				/* while */
++
++  return (asn1_node) p;
++}
++
++
++/******************************************************************/
++/* Function : _asn1_set_value                                     */
++/* Description: sets the field VALUE in a NODE_ASN element. The   */
++/*              previous value (if exist) will be lost            */
++/* Parameters:                                                    */
++/*   node: element pointer.                                       */
++/*   value: pointer to the value that you want to set.            */
++/*   len: character number of value.                              */
++/* Return: pointer to the NODE_ASN element.                       */
++/******************************************************************/
++asn1_node
++_asn1_set_value (asn1_node node, const void *value, unsigned int len)
++{
++  if (node == NULL)
++    return node;
++  if (node->value)
++    {
++      if (node->value != node->small_value)
++	free (node->value);
++      node->value = NULL;
++      node->value_len = 0;
++    }
++
++  if (!len)
++    return node;
++
++  if (len < sizeof (node->small_value))
++    {
++      node->value = node->small_value;
++    }
++  else
++    {
++      node->value = malloc (len);
++      if (node->value == NULL)
++	return NULL;
++    }
++  node->value_len = len;
++
++  memcpy (node->value, value, len);
++  return node;
++}
++
++/******************************************************************/
++/* Function : _asn1_set_value_lv                                  */
++/* Description: sets the field VALUE in a NODE_ASN element. The   */
++/*              previous value (if exist) will be lost. The value */
++/*		given is stored as an length-value format (LV     */
++/* Parameters:                                                    */
++/*   node: element pointer.                                       */
++/*   value: pointer to the value that you want to set.            */
++/*   len: character number of value.                              */
++/* Return: pointer to the NODE_ASN element.                       */
++/******************************************************************/
++asn1_node
++_asn1_set_value_lv (asn1_node node, const void *value, unsigned int len)
++{
++  int len2;
++  void *temp;
++
++  if (node == NULL)
++    return node;
++
++  asn1_length_der (len, NULL, &len2);
++  temp = malloc (len + len2);
++  if (temp == NULL)
++    return NULL;
++
++  asn1_octet_der (value, len, temp, &len2);
++  return _asn1_set_value_m (node, temp, len2);
++}
++
++/* the same as _asn1_set_value except that it sets an already malloc'ed
++ * value.
++ */
++asn1_node
++_asn1_set_value_m (asn1_node node, void *value, unsigned int len)
++{
++  if (node == NULL)
++    return node;
++
++  if (node->value)
++    {
++      if (node->value != node->small_value)
++	free (node->value);
++      node->value = NULL;
++      node->value_len = 0;
++    }
++
++  if (!len)
++    return node;
++
++  node->value = value;
++  node->value_len = len;
++
++  return node;
++}
++
++/******************************************************************/
++/* Function : _asn1_append_value                                  */
++/* Description: appends to the field VALUE in a NODE_ASN element. */
++/*							          */
++/* Parameters:                                                    */
++/*   node: element pointer.                                       */
++/*   value: pointer to the value that you want to be appended.    */
++/*   len: character number of value.                              */
++/* Return: pointer to the NODE_ASN element.                       */
++/******************************************************************/
++asn1_node
++_asn1_append_value (asn1_node node, const void *value, unsigned int len)
++{
++  if (node == NULL)
++    return node;
++
++  if (node->value == NULL)
++    return _asn1_set_value (node, value, len);
++
++  if (len == 0)
++    return node;
++
++  if (node->value == node->small_value)
++    {
++      /* value is in node */
++      int prev_len = node->value_len;
++      node->value_len += len;
++      node->value = malloc (node->value_len);
++      if (node->value == NULL)
++	{
++	  node->value_len = 0;
++	  return NULL;
++	}
++
++      if (prev_len > 0)
++        memcpy (node->value, node->small_value, prev_len);
++
++      memcpy (&node->value[prev_len], value, len);
++
++      return node;
++    }
++  else /* if (node->value != NULL && node->value != node->small_value) */
++    {
++      /* value is allocated */
++      int prev_len = node->value_len;
++      node->value_len += len;
++
++      node->value = _asn1_realloc (node->value, node->value_len);
++      if (node->value == NULL)
++	{
++	  node->value_len = 0;
++	  return NULL;
++	}
++
++      memcpy (&node->value[prev_len], value, len);
++
++      return node;
++    }
++}
++
++/******************************************************************/
++/* Function : _asn1_set_name                                      */
++/* Description: sets the field NAME in a NODE_ASN element. The    */
++/*              previous value (if exist) will be lost            */
++/* Parameters:                                                    */
++/*   node: element pointer.                                       */
++/*   name: a null terminated string with the name that you want   */
++/*         to set.                                                */
++/* Return: pointer to the NODE_ASN element.                       */
++/******************************************************************/
++asn1_node
++_asn1_set_name (asn1_node node, const char *name)
++{
++  if (node == NULL)
++    return node;
++
++  _asn1_str_cpy (node->name, sizeof (node->name), name ? name : "");
++  node->name_hash = _asn1_hash_name (node->name);
++
++  return node;
++}
++
++/******************************************************************/
++/* Function : _asn1_cpy_name                                      */
++/* Description: copies the field NAME in a NODE_ASN element.      */
++/* Parameters:                                                    */
++/*   dst: a dest element pointer.                                 */
++/*   src: a source element pointer.                               */
++/* Return: pointer to the NODE_ASN element.                       */
++/******************************************************************/
++asn1_node
++_asn1_cpy_name (asn1_node dst, asn1_node_const src)
++{
++  if (dst == NULL)
++    return dst;
++
++  if (src == NULL)
++    {
++      dst->name[0] = 0;
++      dst->name_hash = _asn1_hash_name (dst->name);
++      return dst;
++    }
++
++  _asn1_str_cpy (dst->name, sizeof (dst->name), src->name);
++  dst->name_hash = src->name_hash;
++
++  return dst;
++}
++
++/******************************************************************/
++/* Function : _asn1_set_right                                     */
++/* Description: sets the field RIGHT in a NODE_ASN element.       */
++/* Parameters:                                                    */
++/*   node: element pointer.                                       */
++/*   right: pointer to a NODE_ASN element that you want be pointed*/
++/*          by NODE.                                              */
++/* Return: pointer to *NODE.                                      */
++/******************************************************************/
++asn1_node
++_asn1_set_right (asn1_node node, asn1_node right)
++{
++  if (node == NULL)
++    return node;
++  node->right = right;
++  if (right)
++    right->left = node;
++  return node;
++}
++
++
++/******************************************************************/
++/* Function : _asn1_get_last_right                                */
++/* Description: return the last element along the right chain.    */
++/* Parameters:                                                    */
++/*   node: starting element pointer.                              */
++/* Return: pointer to the last element along the right chain.     */
++/******************************************************************/
++asn1_node
++_asn1_get_last_right (asn1_node_const node)
++{
++  asn1_node_const p;
++
++  if (node == NULL)
++    return NULL;
++  p = node;
++  while (p->right)
++    p = p->right;
++  return (asn1_node) p;
++}
++
++/******************************************************************/
++/* Function : _asn1_remove_node                                   */
++/* Description: gets free the memory allocated for an NODE_ASN    */
++/*              element (not the elements pointed by it).         */
++/* Parameters:                                                    */
++/*   node: NODE_ASN element pointer.                              */
++/*   flags: ASN1_DELETE_FLAG_*                                    */
++/******************************************************************/
++void
++_asn1_remove_node (asn1_node node, unsigned int flags)
++{
++  if (node == NULL)
++    return;
++
++  if (node->value != NULL)
++    {
++      if (flags & ASN1_DELETE_FLAG_ZEROIZE)
++        {
++          safe_memset(node->value, 0, node->value_len);
++        }
++
++      if (node->value != node->small_value)
++        free (node->value);
++    }
++  free (node);
++}
++
++/******************************************************************/
++/* Function : _asn1_find_up                                       */
++/* Description: return the father of the NODE_ASN element.        */
++/* Parameters:                                                    */
++/*   node: NODE_ASN element pointer.                              */
++/* Return: Null if not found.                                     */
++/******************************************************************/
++asn1_node
++_asn1_find_up (asn1_node_const node)
++{
++  asn1_node_const p;
++
++  if (node == NULL)
++    return NULL;
++
++  p = node;
++
++  while ((p->left != NULL) && (p->left->right == p))
++    p = p->left;
++
++  return p->left;
++}
++
++static
++unsigned _asn1_is_up (asn1_node_const up_cand, asn1_node_const down)
++{
++  asn1_node_const d, u;
++
++  if (up_cand == NULL || down == NULL)
++    return 0;
++
++  d = down;
++
++  while ((u = _asn1_find_up(d)) != NULL && u != d)
++    {
++      if (u == up_cand)
++        return 1;
++      d = u;
++    }
++
++  return 0;
++}
++
++/******************************************************************/
++/* Function : _asn1_delete_node_from_list                         */
++/* Description: deletes the list element given                    */
++/******************************************************************/
++void
++_asn1_delete_node_from_list (list_type *list, asn1_node node)
++{
++  list_type *p = list;
++
++  while (p)
++    {
++      if (p->node == node)
++        p->node = NULL;
++      p = p->next;
++    }
++}
++
++/******************************************************************/
++/* Function : _asn1_delete_list                                   */
++/* Description: deletes the list elements (not the elements       */
++/*  pointed by them).                                             */
++/******************************************************************/
++void
++_asn1_delete_list (list_type *e_list)
++{
++  list_type *p;
++
++  while (e_list)
++    {
++      p = e_list;
++      e_list = e_list->next;
++      free (p);
++    }
++}
++
++/******************************************************************/
++/* Function : _asn1_delete_list_and nodes                         */
++/* Description: deletes the list elements and the elements        */
++/*  pointed by them.                                              */
++/******************************************************************/
++void
++_asn1_delete_list_and_nodes (list_type *e_list)
++{
++  list_type *p;
++
++  while (e_list)
++    {
++      p = e_list;
++      e_list = e_list->next;
++      _asn1_remove_node (p->node, 0);
++      free (p);
++    }
++}
++
++
++char *
++_asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE])
++{
++  uint64_t d, r;
++  char temp[LTOSTR_MAX_SIZE];
++  int count, k, start;
++  uint64_t val;
++
++  if (v < 0)
++    {
++      str[0] = '-';
++      start = 1;
++      val = -((uint64_t)v);
++    }
++  else
++    {
++      val = v;
++      start = 0;
++    }
++
++  count = 0;
++  do
++    {
++      d = val / 10;
++      r = val - d * 10;
++      temp[start + count] = '0' + (char) r;
++      count++;
++      val = d;
++    }
++  while (val && ((start+count) < LTOSTR_MAX_SIZE-1));
++
++  for (k = 0; k < count; k++)
++    str[k + start] = temp[start + count - k - 1];
++  str[count + start] = 0;
++  return str;
++}
++
++
++/******************************************************************/
++/* Function : _asn1_change_integer_value                          */
++/* Description: converts into DER coding the value assign to an   */
++/*   INTEGER constant.                                            */
++/* Parameters:                                                    */
++/*   node: root of an ASN1element.                                */
++/* Return:                                                        */
++/*   ASN1_ELEMENT_NOT_FOUND if NODE is NULL,                       */
++/*   otherwise ASN1_SUCCESS                                             */
++/******************************************************************/
++int
++_asn1_change_integer_value (asn1_node node)
++{
++  asn1_node p;
++  unsigned char val[SIZEOF_UNSIGNED_LONG_INT];
++  unsigned char val2[SIZEOF_UNSIGNED_LONG_INT + 1];
++  int len;
++
++  if (node == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  p = node;
++  while (p)
++    {
++      if ((type_field (p->type) == ASN1_ETYPE_INTEGER)
++	  && (p->type & CONST_ASSIGN))
++	{
++	  if (p->value)
++	    {
++	      _asn1_convert_integer (p->value, val, sizeof (val), &len);
++	      asn1_octet_der (val, len, val2, &len);
++	      _asn1_set_value (p, val2, len);
++	    }
++	}
++
++      if (p->down)
++	{
++	  p = p->down;
++	}
++      else
++	{
++	  if (p == node)
++	    p = NULL;
++	  else if (p->right)
++	    p = p->right;
++	  else
++	    {
++	      while (1)
++		{
++		  p = _asn1_find_up (p);
++		  if (p == node)
++		    {
++		      p = NULL;
++		      break;
++		    }
++		  if (p && p->right)
++		    {
++		      p = p->right;
++		      break;
++		    }
++		}
++	    }
++	}
++    }
++
++  return ASN1_SUCCESS;
++}
++
++#define MAX_CONSTANTS 1024
++/******************************************************************/
++/* Function : _asn1_expand_object_id                              */
++/* Description: expand the IDs of an OBJECT IDENTIFIER constant.  */
++/* Parameters:                                                    */
++/*   list: root of an object list                                 */
++/*   node: root of an ASN1 element.                               */
++/* Return:                                                        */
++/*   ASN1_ELEMENT_NOT_FOUND if NODE is NULL,                      */
++/*   otherwise ASN1_SUCCESS                                       */
++/******************************************************************/
++int
++_asn1_expand_object_id (list_type **list, asn1_node node)
++{
++  asn1_node p, p2, p3, p4, p5;
++  char name_root[ASN1_MAX_NAME_SIZE], name2[2 * ASN1_MAX_NAME_SIZE + 1];
++  int move, tlen, tries;
++  unsigned max_constants;
++
++  if (node == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  _asn1_str_cpy (name_root, sizeof (name_root), node->name);
++
++  p = node;
++  move = DOWN;
++  tries = 0;
++
++  while (!((p == node) && (move == UP)))
++    {
++      if (move != UP)
++	{
++	  if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID)
++	      && (p->type & CONST_ASSIGN))
++	    {
++	      p2 = p->down;
++	      if (p2 && (type_field (p2->type) == ASN1_ETYPE_CONSTANT))
++		{
++		  if (p2->value && !c_isdigit (p2->value[0]))
++		    {
++		      _asn1_str_cpy (name2, sizeof (name2), name_root);
++		      _asn1_str_cat (name2, sizeof (name2), ".");
++		      _asn1_str_cat (name2, sizeof (name2), (char *) p2->value);
++		      p3 = asn1_find_node (node, name2);
++		      if (!p3 || _asn1_is_up(p2, p3) ||
++			  (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) ||
++			  !(p3->type & CONST_ASSIGN))
++			return ASN1_ELEMENT_NOT_FOUND;
++
++		      _asn1_set_down (p, p2->right);
++		      if (p2->down)
++			_asn1_delete_structure (*list, &p2->down, 0);
++		      _asn1_delete_node_from_list(*list, p2);
++		      _asn1_remove_node (p2, 0);
++		      p2 = p;
++		      p4 = p3->down;
++		      max_constants = 0;
++		      while (p4)
++			{
++			  if (type_field (p4->type) == ASN1_ETYPE_CONSTANT)
++			    {
++			      max_constants++;
++			      if (max_constants == MAX_CONSTANTS)
++                                return ASN1_RECURSION;
++
++			      p5 =
++				_asn1_add_single_node (ASN1_ETYPE_CONSTANT);
++			      _asn1_set_name (p5, p4->name);
++			      if (p4->value)
++			        {
++			          tlen = _asn1_strlen (p4->value);
++			          if (tlen > 0)
++			            _asn1_set_value (p5, p4->value, tlen + 1);
++			        }
++			      _asn1_add_static_node2(list, p5);
++
++			      if (p2 == p)
++				{
++				  _asn1_set_right (p5, p->down);
++				  _asn1_set_down (p, p5);
++				}
++			      else
++				{
++				  _asn1_set_right (p5, p2->right);
++				  _asn1_set_right (p2, p5);
++				}
++			      p2 = p5;
++			    }
++			  p4 = p4->right;
++			}
++		      move = DOWN;
++
++		      tries++;
++                      if (tries >= EXPAND_OBJECT_ID_MAX_RECURSION)
++                        return ASN1_RECURSION;
++
++		      continue;
++		    }
++		}
++	    }
++	  move = DOWN;
++	}
++      else
++	move = RIGHT;
++
++      tries = 0;
++      if (move == DOWN)
++	{
++	  if (p->down)
++	    p = p->down;
++	  else
++	    move = RIGHT;
++	}
++
++      if (p == node)
++	{
++	  move = UP;
++	  continue;
++	}
++
++      if (move == RIGHT)
++	{
++	  if (p && p->right)
++	    p = p->right;
++	  else
++	    move = UP;
++	}
++      if (move == UP)
++	p = _asn1_find_up (p);
++    }
++
++  /*******************************/
++  /*       expand DEFAULT        */
++  /*******************************/
++  p = node;
++  move = DOWN;
++
++  while (!((p == node) && (move == UP)))
++    {
++      if (move != UP)
++	{
++	  if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) &&
++	      (p->type & CONST_DEFAULT))
++	    {
++	      p2 = p->down;
++	      if (p2 && (type_field (p2->type) == ASN1_ETYPE_DEFAULT))
++		{
++		  _asn1_str_cpy (name2, sizeof (name2), name_root);
++		  _asn1_str_cat (name2, sizeof (name2), ".");
++		  if (p2->value)
++		    _asn1_str_cat (name2, sizeof (name2), (char *) p2->value);
++		  p3 = asn1_find_node (node, name2);
++		  if (!p3 || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID)
++		      || !(p3->type & CONST_ASSIGN))
++		    return ASN1_ELEMENT_NOT_FOUND;
++		  p4 = p3->down;
++		  name2[0] = 0;
++		  while (p4)
++		    {
++		      if (type_field (p4->type) == ASN1_ETYPE_CONSTANT)
++			{
++			  if (p4->value == NULL)
++			    return ASN1_VALUE_NOT_FOUND;
++
++			  if (name2[0])
++			    _asn1_str_cat (name2, sizeof (name2), ".");
++			  _asn1_str_cat (name2, sizeof (name2),
++					 (char *) p4->value);
++			}
++		      p4 = p4->right;
++		    }
++		  tlen = strlen (name2);
++		  if (tlen > 0)
++		    _asn1_set_value (p2, name2, tlen + 1);
++		}
++	    }
++	  move = DOWN;
++	}
++      else
++	move = RIGHT;
++
++      if (move == DOWN)
++	{
++	  if (p->down)
++	    p = p->down;
++	  else
++	    move = RIGHT;
++	}
++
++      if (p == node)
++	{
++	  move = UP;
++	  continue;
++	}
++
++      if (move == RIGHT)
++	{
++	  if (p && p->right)
++	    p = p->right;
++	  else
++	    move = UP;
++	}
++      if (move == UP)
++	p = _asn1_find_up (p);
++    }
++
++  return ASN1_SUCCESS;
++}
++
++
++/******************************************************************/
++/* Function : _asn1_type_set_config                               */
++/* Description: sets the CONST_SET and CONST_NOT_USED properties  */
++/*   in the fields of the SET elements.                           */
++/* Parameters:                                                    */
++/*   node: root of an ASN1 element.                               */
++/* Return:                                                        */
++/*   ASN1_ELEMENT_NOT_FOUND if NODE is NULL,                       */
++/*   otherwise ASN1_SUCCESS                                             */
++/******************************************************************/
++int
++_asn1_type_set_config (asn1_node node)
++{
++  asn1_node p, p2;
++  int move;
++
++  if (node == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  p = node;
++  move = DOWN;
++
++  while (!((p == node) && (move == UP)))
++    {
++      if (move != UP)
++	{
++	  if (type_field (p->type) == ASN1_ETYPE_SET)
++	    {
++	      p2 = p->down;
++	      while (p2)
++		{
++		  if (type_field (p2->type) != ASN1_ETYPE_TAG)
++		    p2->type |= CONST_SET | CONST_NOT_USED;
++		  p2 = p2->right;
++		}
++	    }
++	  move = DOWN;
++	}
++      else
++	move = RIGHT;
++
++      if (move == DOWN)
++	{
++	  if (p->down)
++	    p = p->down;
++	  else
++	    move = RIGHT;
++	}
++
++      if (p == node)
++	{
++	  move = UP;
++	  continue;
++	}
++
++      if (move == RIGHT)
++	{
++	  if (p && p->right)
++	    p = p->right;
++	  else
++	    move = UP;
++	}
++      if (move == UP)
++	p = _asn1_find_up (p);
++    }
++
++  return ASN1_SUCCESS;
++}
++
++
++/******************************************************************/
++/* Function : _asn1_check_identifier                              */
++/* Description: checks the definitions of all the identifiers     */
++/*   and the first element of an OBJECT_ID (e.g. {pkix 0 4}).     */
++/*   The _asn1_identifierMissing global variable is filled if     */
++/*   necessary.                                                   */
++/* Parameters:                                                    */
++/*   node: root of an ASN1 element.                               */
++/* Return:                                                        */
++/*   ASN1_ELEMENT_NOT_FOUND      if NODE is NULL,                 */
++/*   ASN1_IDENTIFIER_NOT_FOUND   if an identifier is not defined, */
++/*   otherwise ASN1_SUCCESS                                       */
++/******************************************************************/
++int
++_asn1_check_identifier (asn1_node_const node)
++{
++  asn1_node_const p, p2;
++  char name2[ASN1_MAX_NAME_SIZE * 2 + 2];
++
++  if (node == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  p = node;
++  while (p)
++    {
++      if (p->value && type_field (p->type) == ASN1_ETYPE_IDENTIFIER)
++	{
++	  _asn1_str_cpy (name2, sizeof (name2), node->name);
++	  _asn1_str_cat (name2, sizeof (name2), ".");
++	  _asn1_str_cat (name2, sizeof (name2), (char *) p->value);
++	  p2 = asn1_find_node (node, name2);
++	  if (p2 == NULL)
++	    {
++	      if (p->value)
++		_asn1_str_cpy (_asn1_identifierMissing, sizeof(_asn1_identifierMissing), (char*)p->value);
++	      else
++		_asn1_strcpy (_asn1_identifierMissing, "(null)");
++	      return ASN1_IDENTIFIER_NOT_FOUND;
++	    }
++	}
++      else if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) &&
++	       (p->type & CONST_DEFAULT))
++	{
++	  p2 = p->down;
++	  if (p2 && (type_field (p2->type) == ASN1_ETYPE_DEFAULT))
++	    {
++	      _asn1_str_cpy (name2, sizeof (name2), node->name);
++	      if (p2->value)
++	        {
++	          _asn1_str_cat (name2, sizeof (name2), ".");
++	          _asn1_str_cat (name2, sizeof (name2), (char *) p2->value);
++	          _asn1_str_cpy (_asn1_identifierMissing, sizeof(_asn1_identifierMissing), (char*)p2->value);
++	        }
++	      else
++		_asn1_strcpy (_asn1_identifierMissing, "(null)");
++
++	      p2 = asn1_find_node (node, name2);
++	      if (!p2 || (type_field (p2->type) != ASN1_ETYPE_OBJECT_ID) ||
++		  !(p2->type & CONST_ASSIGN))
++		return ASN1_IDENTIFIER_NOT_FOUND;
++	      else
++		_asn1_identifierMissing[0] = 0;
++	    }
++	}
++      else if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) &&
++	       (p->type & CONST_ASSIGN))
++	{
++	  p2 = p->down;
++	  if (p2 && (type_field (p2->type) == ASN1_ETYPE_CONSTANT))
++	    {
++	      if (p2->value && !c_isdigit (p2->value[0]))
++		{
++		  _asn1_str_cpy (name2, sizeof (name2), node->name);
++		  _asn1_str_cat (name2, sizeof (name2), ".");
++		  _asn1_str_cat (name2, sizeof (name2), (char *) p2->value);
++		  _asn1_str_cpy (_asn1_identifierMissing, sizeof(_asn1_identifierMissing), (char*)p2->value);
++
++		  p2 = asn1_find_node (node, name2);
++		  if (!p2 || (type_field (p2->type) != ASN1_ETYPE_OBJECT_ID)
++		      || !(p2->type & CONST_ASSIGN))
++		    return ASN1_IDENTIFIER_NOT_FOUND;
++		  else
++		    _asn1_identifierMissing[0] = 0;
++		}
++	    }
++	}
++
++      if (p->down)
++	{
++	  p = p->down;
++	}
++      else if (p->right)
++	p = p->right;
++      else
++	{
++	  while (p)
++	    {
++	      p = _asn1_find_up (p);
++	      if (p == node)
++		{
++		  p = NULL;
++		  break;
++		}
++	      if (p && p->right)
++		{
++		  p = p->right;
++		  break;
++		}
++	    }
++	}
++    }
++
++  return ASN1_SUCCESS;
++}
++
++
++/******************************************************************/
++/* Function : _asn1_set_default_tag                               */
++/* Description: sets the default IMPLICIT or EXPLICIT property in */
++/*   the tagged elements that don't have this declaration.        */
++/* Parameters:                                                    */
++/*   node: pointer to a DEFINITIONS element.                      */
++/* Return:                                                        */
++/*   ASN1_ELEMENT_NOT_FOUND if NODE is NULL or not a pointer to   */
++/*     a DEFINITIONS element,                                     */
++/*   otherwise ASN1_SUCCESS                                       */
++/******************************************************************/
++int
++_asn1_set_default_tag (asn1_node node)
++{
++  asn1_node p;
++
++  if ((node == NULL) || (type_field (node->type) != ASN1_ETYPE_DEFINITIONS))
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  p = node;
++  while (p)
++    {
++      if ((type_field (p->type) == ASN1_ETYPE_TAG) &&
++	  !(p->type & CONST_EXPLICIT) && !(p->type & CONST_IMPLICIT))
++	{
++	  if (node->type & CONST_EXPLICIT)
++	    p->type |= CONST_EXPLICIT;
++	  else
++	    p->type |= CONST_IMPLICIT;
++	}
++
++      if (p->down)
++	{
++	  p = p->down;
++	}
++      else if (p->right)
++	p = p->right;
++      else
++	{
++	  while (1)
++	    {
++	      p = _asn1_find_up (p);
++	      if (p == node)
++		{
++		  p = NULL;
++		  break;
++		}
++	      if (p && p->right)
++		{
++		  p = p->right;
++		  break;
++		}
++	    }
++	}
++    }
++
++  return ASN1_SUCCESS;
++}
+diff --git a/grub-core/lib/libtasn1/lib/structure.c b/grub-core/lib/libtasn1/lib/structure.c
+new file mode 100644
+index 00000000000..8189c56a4c9
+--- /dev/null
++++ b/grub-core/lib/libtasn1/lib/structure.c
+@@ -0,0 +1,1220 @@
++/*
++ * Copyright (C) 2002-2014 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * The LIBTASN1 library is free software; you can redistribute it
++ * and/or modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301, USA
++ */
++
++
++/*****************************************************/
++/* File: structure.c                                 */
++/* Description: Functions to create and delete an    */
++/*  ASN1 tree.                                       */
++/*****************************************************/
++
++
++#include <int.h>
++#include <structure.h>
++#include "parser_aux.h"
++#include <gstr.h>
++
++
++extern char _asn1_identifierMissing[];
++
++
++/******************************************************/
++/* Function : _asn1_add_single_node                     */
++/* Description: creates a new NODE_ASN element.       */
++/* Parameters:                                        */
++/*   type: type of the new element (see ASN1_ETYPE_         */
++/*         and CONST_ constants).                     */
++/* Return: pointer to the new element.                */
++/******************************************************/
++asn1_node
++_asn1_add_single_node (unsigned int type)
++{
++  asn1_node punt;
++
++  punt = calloc (1, sizeof (struct asn1_node_st));
++  if (punt == NULL)
++    return NULL;
++
++  punt->type = type;
++
++  return punt;
++}
++
++
++/******************************************************************/
++/* Function : _asn1_find_left                                     */
++/* Description: returns the NODE_ASN element with RIGHT field that*/
++/*              points the element NODE.                          */
++/* Parameters:                                                    */
++/*   node: NODE_ASN element pointer.                              */
++/* Return: NULL if not found.                                     */
++/******************************************************************/
++asn1_node
++_asn1_find_left (asn1_node_const node)
++{
++  if ((node == NULL) || (node->left == NULL) || (node->left->down == node))
++    return NULL;
++
++  return node->left;
++}
++
++
++int
++_asn1_create_static_structure (asn1_node_const pointer, char *output_file_name,
++			       char *vector_name)
++{
++  FILE *file;
++  asn1_node_const p;
++  unsigned long t;
++
++  file = fopen (output_file_name, "w");
++
++  if (file == NULL)
++    return ASN1_FILE_NOT_FOUND;
++
++  fprintf (file, "#if HAVE_CONFIG_H\n");
++  fprintf (file, "# include \"config.h\"\n");
++  fprintf (file, "#endif\n\n");
++
++  fprintf (file, "#include <libtasn1.h>\n\n");
++
++  fprintf (file, "const asn1_static_node %s[] = {\n", vector_name);
++
++  p = pointer;
++
++  while (p)
++    {
++      fprintf (file, "  { ");
++
++      if (p->name[0] != 0)
++	fprintf (file, "\"%s\", ", p->name);
++      else
++	fprintf (file, "NULL, ");
++
++      t = p->type;
++      if (p->down)
++	t |= CONST_DOWN;
++      if (p->right)
++	t |= CONST_RIGHT;
++
++      fprintf (file, "%lu, ", t);
++
++      if (p->value)
++	fprintf (file, "\"%s\"},\n", p->value);
++      else
++	fprintf (file, "NULL },\n");
++
++      if (p->down)
++	{
++	  p = p->down;
++	}
++      else if (p->right)
++	{
++	  p = p->right;
++	}
++      else
++	{
++	  while (1)
++	    {
++	      p = _asn1_find_up (p);
++	      if (p == pointer)
++		{
++		  p = NULL;
++		  break;
++		}
++	      if (p->right)
++		{
++		  p = p->right;
++		  break;
++		}
++	    }
++	}
++    }
++
++  fprintf (file, "  { NULL, 0, NULL }\n};\n");
++
++  fclose (file);
++
++  return ASN1_SUCCESS;
++}
++
++
++/**
++ * asn1_array2tree:
++ * @array: specify the array that contains ASN.1 declarations
++ * @definitions: return the pointer to the structure created by
++ *   *ARRAY ASN.1 declarations
++ * @errorDescription: return the error description.
++ *
++ * Creates the structures needed to manage the ASN.1 definitions.
++ * @array is a vector created by asn1_parser2array().
++ *
++ * Returns: %ASN1_SUCCESS if structure was created correctly,
++ *   %ASN1_ELEMENT_NOT_EMPTY if *@definitions not NULL,
++ *   %ASN1_IDENTIFIER_NOT_FOUND if in the file there is an identifier
++ *   that is not defined (see @errorDescription for more information),
++ *   %ASN1_ARRAY_ERROR if the array pointed by @array is wrong.
++ **/
++int
++asn1_array2tree (const asn1_static_node * array, asn1_node * definitions,
++		 char *errorDescription)
++{
++  asn1_node p, p_last = NULL;
++  unsigned long k;
++  int move;
++  int result;
++  unsigned int type;
++  list_type *e_list = NULL;
++
++  if (errorDescription)
++    errorDescription[0] = 0;
++
++  if (*definitions != NULL)
++    return ASN1_ELEMENT_NOT_EMPTY;
++
++  move = UP;
++
++  for (k = 0; array[k].value || array[k].type || array[k].name; k++)
++    {
++      type = convert_old_type (array[k].type);
++
++      p = _asn1_add_static_node (&e_list, type & (~CONST_DOWN));
++      if (array[k].name)
++	_asn1_set_name (p, array[k].name);
++      if (array[k].value)
++	_asn1_set_value (p, array[k].value, strlen (array[k].value) + 1);
++
++      if (*definitions == NULL)
++	*definitions = p;
++
++      if (move == DOWN)
++	{
++	  if (p_last && p_last->down)
++	      _asn1_delete_structure (e_list, &p_last->down, 0);
++	  _asn1_set_down (p_last, p);
++	}
++      else if (move == RIGHT)
++        {
++	  if (p_last && p_last->right)
++	      _asn1_delete_structure (e_list, &p_last->right, 0);
++	  _asn1_set_right (p_last, p);
++        }
++
++      p_last = p;
++
++      if (type & CONST_DOWN)
++	move = DOWN;
++      else if (type & CONST_RIGHT)
++	move = RIGHT;
++      else
++	{
++	  while (p_last != *definitions)
++	    {
++	      p_last = _asn1_find_up (p_last);
++
++	      if (p_last == NULL)
++		break;
++
++	      if (p_last->type & CONST_RIGHT)
++		{
++		  p_last->type &= ~CONST_RIGHT;
++		  move = RIGHT;
++		  break;
++		}
++	    }			/* while */
++	}
++    }				/* while */
++
++  if (p_last == *definitions)
++    {
++      result = _asn1_check_identifier (*definitions);
++      if (result == ASN1_SUCCESS)
++	{
++	  _asn1_change_integer_value (*definitions);
++	  result = _asn1_expand_object_id (&e_list, *definitions);
++	}
++    }
++  else
++    {
++      result = ASN1_ARRAY_ERROR;
++    }
++
++  if (errorDescription != NULL)
++    {
++      if (result == ASN1_IDENTIFIER_NOT_FOUND)
++	{
++	  Estrcpy (errorDescription, ":: identifier '");
++	  Estrcat (errorDescription, _asn1_identifierMissing);
++	  Estrcat (errorDescription, "' not found");
++	}
++      else
++	errorDescription[0] = 0;
++    }
++
++  if (result != ASN1_SUCCESS)
++    {
++      _asn1_delete_list_and_nodes (e_list);
++      *definitions = NULL;
++    }
++  else
++    _asn1_delete_list (e_list);
++
++  return result;
++}
++
++/**
++ * asn1_delete_structure:
++ * @structure: pointer to the structure that you want to delete.
++ *
++ * Deletes the structure *@structure.  At the end, *@structure is set
++ * to NULL.
++ *
++ * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if
++ *   *@structure was NULL.
++ **/
++int
++asn1_delete_structure (asn1_node * structure)
++{
++  return _asn1_delete_structure (NULL, structure, 0);
++}
++
++/**
++ * asn1_delete_structure2:
++ * @structure: pointer to the structure that you want to delete.
++ * @flags: additional flags (see %ASN1_DELETE_FLAG)
++ *
++ * Deletes the structure *@structure.  At the end, *@structure is set
++ * to NULL.
++ *
++ * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if
++ *   *@structure was NULL.
++ **/
++int
++asn1_delete_structure2 (asn1_node * structure, unsigned int flags)
++{
++  return _asn1_delete_structure (NULL, structure, flags);
++}
++
++int
++_asn1_delete_structure (list_type *e_list, asn1_node * structure, unsigned int flags)
++{
++  asn1_node p, p2, p3;
++
++  if (*structure == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  p = *structure;
++  while (p)
++    {
++      if (p->down)
++	{
++	  p = p->down;
++	}
++      else
++	{			/* no down */
++	  p2 = p->right;
++	  if (p != *structure)
++	    {
++	      p3 = _asn1_find_up (p);
++	      _asn1_set_down (p3, p2);
++	      if (e_list)
++		_asn1_delete_node_from_list (e_list, p);
++	      _asn1_remove_node (p, flags);
++	      p = p3;
++	    }
++	  else
++	    {			/* p==root */
++	      p3 = _asn1_find_left (p);
++	      if (!p3)
++		{
++		  p3 = _asn1_find_up (p);
++		  if (p3)
++		    _asn1_set_down (p3, p2);
++		  else
++		    {
++		      if (p->right)
++			p->right->left = NULL;
++		    }
++		}
++	      else
++		_asn1_set_right (p3, p2);
++	      if (e_list)
++		_asn1_delete_node_from_list (e_list, p);
++	      _asn1_remove_node (p, flags);
++	      p = NULL;
++	    }
++	}
++    }
++
++  *structure = NULL;
++  return ASN1_SUCCESS;
++}
++
++
++/**
++ * asn1_delete_element:
++ * @structure: pointer to the structure that contains the element you
++ *   want to delete.
++ * @element_name: element's name you want to delete.
++ *
++ * Deletes the element named *@element_name inside *@structure.
++ *
++ * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if
++ *   the @element_name was not found.
++ **/
++int
++asn1_delete_element (asn1_node structure, const char *element_name)
++{
++  asn1_node p2, p3, source_node;
++
++  source_node = asn1_find_node (structure, element_name);
++
++  if (source_node == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  p2 = source_node->right;
++  p3 = _asn1_find_left (source_node);
++  if (!p3)
++    {
++      p3 = _asn1_find_up (source_node);
++      if (p3)
++	_asn1_set_down (p3, p2);
++      else if (source_node->right)
++	source_node->right->left = NULL;
++    }
++  else
++    _asn1_set_right (p3, p2);
++
++  return asn1_delete_structure (&source_node);
++}
++
++#ifndef __clang_analyzer__
++asn1_node
++_asn1_copy_structure3 (asn1_node_const source_node)
++{
++  asn1_node_const p_s;
++  asn1_node dest_node, p_d, p_d_prev;
++  int move;
++
++  if (source_node == NULL)
++    return NULL;
++
++  dest_node = _asn1_add_single_node (source_node->type);
++
++  p_s = source_node;
++  p_d = dest_node;
++
++  move = DOWN;
++
++  do
++    {
++      if (move != UP)
++	{
++	  if (p_s->name[0] != 0)
++	    _asn1_cpy_name (p_d, p_s);
++	  if (p_s->value)
++	    _asn1_set_value (p_d, p_s->value, p_s->value_len);
++	  if (p_s->down)
++	    {
++	      p_s = p_s->down;
++	      p_d_prev = p_d;
++	      p_d = _asn1_add_single_node (p_s->type);
++	      _asn1_set_down (p_d_prev, p_d);
++	      continue;
++	    }
++	  p_d->start = p_s->start;
++	  p_d->end = p_s->end;
++	}
++
++      if (p_s == source_node)
++	break;
++
++      if (p_s->right)
++	{
++	  move = RIGHT;
++	  p_s = p_s->right;
++	  p_d_prev = p_d;
++	  p_d = _asn1_add_single_node (p_s->type);
++	  _asn1_set_right (p_d_prev, p_d);
++	}
++      else
++	{
++	  move = UP;
++	  p_s = _asn1_find_up (p_s);
++	  p_d = _asn1_find_up (p_d);
++	}
++    }
++  while (p_s != source_node);
++  return dest_node;
++}
++#else
++
++/* Non-production code */
++asn1_node
++_asn1_copy_structure3 (asn1_node_const source_node)
++{
++  return NULL;
++}
++#endif /* __clang_analyzer__ */
++
++
++static asn1_node
++_asn1_copy_structure2 (asn1_node_const root, const char *source_name)
++{
++  asn1_node source_node;
++
++  source_node = asn1_find_node (root, source_name);
++
++  return _asn1_copy_structure3 (source_node);
++
++}
++
++
++static int
++_asn1_type_choice_config (asn1_node node)
++{
++  asn1_node p, p2, p3, p4;
++  int move, tlen;
++
++  if (node == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  p = node;
++  move = DOWN;
++
++  while (!((p == node) && (move == UP)))
++    {
++      if (move != UP)
++	{
++	  if ((type_field (p->type) == ASN1_ETYPE_CHOICE)
++	      && (p->type & CONST_TAG))
++	    {
++	      p2 = p->down;
++	      while (p2)
++		{
++		  if (type_field (p2->type) != ASN1_ETYPE_TAG)
++		    {
++		      p2->type |= CONST_TAG;
++		      p3 = _asn1_find_left (p2);
++		      while (p3)
++			{
++			  if (type_field (p3->type) == ASN1_ETYPE_TAG)
++			    {
++			      p4 = _asn1_add_single_node (p3->type);
++			      tlen = _asn1_strlen (p3->value);
++			      if (tlen > 0)
++				_asn1_set_value (p4, p3->value, tlen + 1);
++			      _asn1_set_right (p4, p2->down);
++			      _asn1_set_down (p2, p4);
++			    }
++			  p3 = _asn1_find_left (p3);
++			}
++		    }
++		  p2 = p2->right;
++		}
++	      p->type &= ~(CONST_TAG);
++	      p2 = p->down;
++	      while (p2)
++		{
++		  p3 = p2->right;
++		  if (type_field (p2->type) == ASN1_ETYPE_TAG)
++		    asn1_delete_structure (&p2);
++		  p2 = p3;
++		}
++	    }
++	  move = DOWN;
++	}
++      else
++	move = RIGHT;
++
++      if (move == DOWN)
++	{
++	  if (p->down)
++	    p = p->down;
++	  else
++	    move = RIGHT;
++	}
++
++      if (p == node)
++	{
++	  move = UP;
++	  continue;
++	}
++
++      if (move == RIGHT)
++	{
++	  if (p->right)
++	    p = p->right;
++	  else
++	    move = UP;
++	}
++      if (move == UP)
++	p = _asn1_find_up (p);
++    }
++
++  return ASN1_SUCCESS;
++}
++
++
++static int
++_asn1_expand_identifier (asn1_node * node, asn1_node_const root)
++{
++  asn1_node p, p2, p3;
++  char name2[ASN1_MAX_NAME_SIZE + 2];
++  int move;
++
++  if (node == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  p = *node;
++  move = DOWN;
++
++  while (!((p == *node) && (move == UP)))
++    {
++      if (move != UP)
++	{
++	  if (type_field (p->type) == ASN1_ETYPE_IDENTIFIER)
++	    {
++	      snprintf (name2, sizeof (name2), "%s.%s", root->name, p->value);
++	      p2 = _asn1_copy_structure2 (root, name2);
++	      if (p2 == NULL)
++		{
++		  return ASN1_IDENTIFIER_NOT_FOUND;
++		}
++	      _asn1_cpy_name (p2, p);
++	      p2->right = p->right;
++	      p2->left = p->left;
++	      if (p->right)
++		p->right->left = p2;
++	      p3 = p->down;
++	      if (p3)
++		{
++		  while (p3->right)
++		    p3 = p3->right;
++		  _asn1_set_right (p3, p2->down);
++		  _asn1_set_down (p2, p->down);
++		}
++
++	      p3 = _asn1_find_left (p);
++	      if (p3)
++		_asn1_set_right (p3, p2);
++	      else
++		{
++		  p3 = _asn1_find_up (p);
++		  if (p3)
++		    _asn1_set_down (p3, p2);
++		  else
++		    {
++		      p2->left = NULL;
++		    }
++		}
++
++	      if (p->type & CONST_SIZE)
++		p2->type |= CONST_SIZE;
++	      if (p->type & CONST_TAG)
++		p2->type |= CONST_TAG;
++	      if (p->type & CONST_OPTION)
++		p2->type |= CONST_OPTION;
++	      if (p->type & CONST_DEFAULT)
++		p2->type |= CONST_DEFAULT;
++	      if (p->type & CONST_SET)
++		p2->type |= CONST_SET;
++	      if (p->type & CONST_NOT_USED)
++		p2->type |= CONST_NOT_USED;
++
++	      if (p == *node)
++		*node = p2;
++	      _asn1_remove_node (p, 0);
++	      p = p2;
++	      move = DOWN;
++	      continue;
++	    }
++	  move = DOWN;
++	}
++      else
++	move = RIGHT;
++
++      if (move == DOWN)
++	{
++	  if (p->down)
++	    p = p->down;
++	  else
++	    move = RIGHT;
++	}
++
++      if (p == *node)
++	{
++	  move = UP;
++	  continue;
++	}
++
++      if (move == RIGHT)
++	{
++	  if (p->right)
++	    p = p->right;
++	  else
++	    move = UP;
++	}
++      if (move == UP)
++	p = _asn1_find_up (p);
++    }
++
++  return ASN1_SUCCESS;
++}
++
++
++/**
++ * asn1_create_element:
++ * @definitions: pointer to the structure returned by "parser_asn1" function
++ * @source_name: the name of the type of the new structure (must be
++ *   inside p_structure).
++ * @element: pointer to the structure created.
++ *
++ * Creates a structure of type @source_name.  Example using
++ *  "pkix.asn":
++ *
++ * rc = asn1_create_element(cert_def, "PKIX1.Certificate", certptr);
++ *
++ * Returns: %ASN1_SUCCESS if creation OK, %ASN1_ELEMENT_NOT_FOUND if
++ *   @source_name is not known.
++ **/
++int
++asn1_create_element (asn1_node_const definitions, const char *source_name,
++		     asn1_node * element)
++{
++  asn1_node dest_node;
++  int res;
++
++  dest_node = _asn1_copy_structure2 (definitions, source_name);
++
++  if (dest_node == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  _asn1_set_name (dest_node, "");
++
++  res = _asn1_expand_identifier (&dest_node, definitions);
++  _asn1_type_choice_config (dest_node);
++
++  *element = dest_node;
++
++  return res;
++}
++
++
++/**
++ * asn1_print_structure:
++ * @out: pointer to the output file (e.g. stdout).
++ * @structure: pointer to the structure that you want to visit.
++ * @name: an element of the structure
++ * @mode: specify how much of the structure to print, can be
++ *   %ASN1_PRINT_NAME, %ASN1_PRINT_NAME_TYPE,
++ *   %ASN1_PRINT_NAME_TYPE_VALUE, or %ASN1_PRINT_ALL.
++ *
++ * Prints on the @out file descriptor the structure's tree starting
++ * from the @name element inside the structure @structure.
++ **/
++void
++asn1_print_structure (FILE * out, asn1_node_const structure, const char *name,
++		      int mode)
++{
++  asn1_node_const p, root;
++  int k, indent = 0, len, len2, len3;
++
++  if (out == NULL)
++    return;
++
++  root = asn1_find_node (structure, name);
++
++  if (root == NULL)
++    return;
++
++  p = root;
++  while (p)
++    {
++      if (mode == ASN1_PRINT_ALL)
++	{
++	  for (k = 0; k < indent; k++)
++	    fprintf (out, " ");
++	  fprintf (out, "name:");
++	  if (p->name[0] != 0)
++	    fprintf (out, "%s  ", p->name);
++	  else
++	    fprintf (out, "NULL  ");
++	}
++      else
++	{
++	  switch (type_field (p->type))
++	    {
++	    case ASN1_ETYPE_CONSTANT:
++	    case ASN1_ETYPE_TAG:
++	    case ASN1_ETYPE_SIZE:
++	      break;
++	    default:
++	      for (k = 0; k < indent; k++)
++		fprintf (out, " ");
++	      fprintf (out, "name:");
++	      if (p->name[0] != 0)
++		fprintf (out, "%s  ", p->name);
++	      else
++		fprintf (out, "NULL  ");
++	    }
++	}
++
++      if (mode != ASN1_PRINT_NAME)
++	{
++	  unsigned type = type_field (p->type);
++	  switch (type)
++	    {
++	    case ASN1_ETYPE_CONSTANT:
++	      if (mode == ASN1_PRINT_ALL)
++		fprintf (out, "type:CONST");
++	      break;
++	    case ASN1_ETYPE_TAG:
++	      if (mode == ASN1_PRINT_ALL)
++		fprintf (out, "type:TAG");
++	      break;
++	    case ASN1_ETYPE_SIZE:
++	      if (mode == ASN1_PRINT_ALL)
++		fprintf (out, "type:SIZE");
++	      break;
++	    case ASN1_ETYPE_DEFAULT:
++	      fprintf (out, "type:DEFAULT");
++	      break;
++	    case ASN1_ETYPE_IDENTIFIER:
++	      fprintf (out, "type:IDENTIFIER");
++	      break;
++	    case ASN1_ETYPE_ANY:
++	      fprintf (out, "type:ANY");
++	      break;
++	    case ASN1_ETYPE_CHOICE:
++	      fprintf (out, "type:CHOICE");
++	      break;
++	    case ASN1_ETYPE_DEFINITIONS:
++	      fprintf (out, "type:DEFINITIONS");
++	      break;
++	    CASE_HANDLED_ETYPES:
++	      fprintf (out, "%s", _asn1_tags[type].desc);
++	      break;
++	    default:
++	      break;
++	    }
++	}
++
++      if ((mode == ASN1_PRINT_NAME_TYPE_VALUE) || (mode == ASN1_PRINT_ALL))
++	{
++	  switch (type_field (p->type))
++	    {
++	    case ASN1_ETYPE_CONSTANT:
++	      if (mode == ASN1_PRINT_ALL)
++		if (p->value)
++		  fprintf (out, "  value:%s", p->value);
++	      break;
++	    case ASN1_ETYPE_TAG:
++	      if (mode == ASN1_PRINT_ALL)
++		if (p->value)
++		  fprintf (out, "  value:%s", p->value);
++	      break;
++	    case ASN1_ETYPE_SIZE:
++	      if (mode == ASN1_PRINT_ALL)
++		if (p->value)
++		  fprintf (out, "  value:%s", p->value);
++	      break;
++	    case ASN1_ETYPE_DEFAULT:
++	      if (p->value)
++		fprintf (out, "  value:%s", p->value);
++	      else if (p->type & CONST_TRUE)
++		fprintf (out, "  value:TRUE");
++	      else if (p->type & CONST_FALSE)
++		fprintf (out, "  value:FALSE");
++	      break;
++	    case ASN1_ETYPE_IDENTIFIER:
++	      if (p->value)
++		fprintf (out, "  value:%s", p->value);
++	      break;
++	    case ASN1_ETYPE_INTEGER:
++	      if (p->value)
++		{
++		  len2 = -1;
++		  len = asn1_get_length_der (p->value, p->value_len, &len2);
++		  fprintf (out, "  value:0x");
++		  if (len > 0)
++		    for (k = 0; k < len; k++)
++		      fprintf (out, "%02x", (unsigned) (p->value)[k + len2]);
++		}
++	      break;
++	    case ASN1_ETYPE_ENUMERATED:
++	      if (p->value)
++		{
++		  len2 = -1;
++		  len = asn1_get_length_der (p->value, p->value_len, &len2);
++		  fprintf (out, "  value:0x");
++		  if (len > 0)
++		    for (k = 0; k < len; k++)
++		      fprintf (out, "%02x", (unsigned) (p->value)[k + len2]);
++		}
++	      break;
++	    case ASN1_ETYPE_BOOLEAN:
++	      if (p->value)
++		{
++		  if (p->value[0] == 'T')
++		    fprintf (out, "  value:TRUE");
++		  else if (p->value[0] == 'F')
++		    fprintf (out, "  value:FALSE");
++		}
++	      break;
++	    case ASN1_ETYPE_BIT_STRING:
++	      if (p->value)
++		{
++		  len2 = -1;
++		  len = asn1_get_length_der (p->value, p->value_len, &len2);
++		  if (len > 0)
++		    {
++		      fprintf (out, "  value(%i):",
++			       (len - 1) * 8 - (p->value[len2]));
++		      for (k = 1; k < len; k++)
++			fprintf (out, "%02x", (unsigned) (p->value)[k + len2]);
++		    }
++		}
++	      break;
++	    case ASN1_ETYPE_GENERALIZED_TIME:
++	    case ASN1_ETYPE_UTC_TIME:
++	      if (p->value)
++		{
++		  fprintf (out, "  value:");
++		  for (k = 0; k < p->value_len; k++)
++		    fprintf (out, "%c", (p->value)[k]);
++		}
++	      break;
++	    case ASN1_ETYPE_GENERALSTRING:
++	    case ASN1_ETYPE_NUMERIC_STRING:
++	    case ASN1_ETYPE_IA5_STRING:
++	    case ASN1_ETYPE_TELETEX_STRING:
++	    case ASN1_ETYPE_PRINTABLE_STRING:
++	    case ASN1_ETYPE_UNIVERSAL_STRING:
++	    case ASN1_ETYPE_UTF8_STRING:
++	    case ASN1_ETYPE_VISIBLE_STRING:
++	      if (p->value)
++		{
++		  len2 = -1;
++		  len = asn1_get_length_der (p->value, p->value_len, &len2);
++		  fprintf (out, "  value:");
++		  if (len > 0)
++		    for (k = 0; k < len; k++)
++		      fprintf (out, "%c", (p->value)[k + len2]);
++		}
++	      break;
++	    case ASN1_ETYPE_BMP_STRING:
++	    case ASN1_ETYPE_OCTET_STRING:
++	      if (p->value)
++		{
++		  len2 = -1;
++		  len = asn1_get_length_der (p->value, p->value_len, &len2);
++		  fprintf (out, "  value:");
++		  if (len > 0)
++		    for (k = 0; k < len; k++)
++		      fprintf (out, "%02x", (unsigned) (p->value)[k + len2]);
++		}
++	      break;
++	    case ASN1_ETYPE_OBJECT_ID:
++	      if (p->value)
++		fprintf (out, "  value:%s", p->value);
++	      break;
++	    case ASN1_ETYPE_ANY:
++	      if (p->value)
++		{
++		  len3 = -1;
++		  len2 = asn1_get_length_der (p->value, p->value_len, &len3);
++		  fprintf (out, "  value:");
++		  if (len2 > 0)
++		    for (k = 0; k < len2; k++)
++		      fprintf (out, "%02x", (unsigned) (p->value)[k + len3]);
++		}
++	      break;
++	    case ASN1_ETYPE_SET:
++	    case ASN1_ETYPE_SET_OF:
++	    case ASN1_ETYPE_CHOICE:
++	    case ASN1_ETYPE_DEFINITIONS:
++	    case ASN1_ETYPE_SEQUENCE_OF:
++	    case ASN1_ETYPE_SEQUENCE:
++	    case ASN1_ETYPE_NULL:
++	      break;
++	    default:
++	      break;
++	    }
++	}
++
++      if (mode == ASN1_PRINT_ALL)
++	{
++	  if (p->type & 0x1FFFFF00)
++	    {
++	      fprintf (out, "  attr:");
++	      if (p->type & CONST_UNIVERSAL)
++		fprintf (out, "UNIVERSAL,");
++	      if (p->type & CONST_PRIVATE)
++		fprintf (out, "PRIVATE,");
++	      if (p->type & CONST_APPLICATION)
++		fprintf (out, "APPLICATION,");
++	      if (p->type & CONST_EXPLICIT)
++		fprintf (out, "EXPLICIT,");
++	      if (p->type & CONST_IMPLICIT)
++		fprintf (out, "IMPLICIT,");
++	      if (p->type & CONST_TAG)
++		fprintf (out, "TAG,");
++	      if (p->type & CONST_DEFAULT)
++		fprintf (out, "DEFAULT,");
++	      if (p->type & CONST_TRUE)
++		fprintf (out, "TRUE,");
++	      if (p->type & CONST_FALSE)
++		fprintf (out, "FALSE,");
++	      if (p->type & CONST_LIST)
++		fprintf (out, "LIST,");
++	      if (p->type & CONST_MIN_MAX)
++		fprintf (out, "MIN_MAX,");
++	      if (p->type & CONST_OPTION)
++		fprintf (out, "OPTION,");
++	      if (p->type & CONST_1_PARAM)
++		fprintf (out, "1_PARAM,");
++	      if (p->type & CONST_SIZE)
++		fprintf (out, "SIZE,");
++	      if (p->type & CONST_DEFINED_BY)
++		fprintf (out, "DEF_BY,");
++	      if (p->type & CONST_GENERALIZED)
++		fprintf (out, "GENERALIZED,");
++	      if (p->type & CONST_UTC)
++		fprintf (out, "UTC,");
++	      if (p->type & CONST_SET)
++		fprintf (out, "SET,");
++	      if (p->type & CONST_NOT_USED)
++		fprintf (out, "NOT_USED,");
++	      if (p->type & CONST_ASSIGN)
++		fprintf (out, "ASSIGNMENT,");
++	    }
++	}
++
++      if (mode == ASN1_PRINT_ALL)
++	{
++	  fprintf (out, "\n");
++	}
++      else
++	{
++	  switch (type_field (p->type))
++	    {
++	    case ASN1_ETYPE_CONSTANT:
++	    case ASN1_ETYPE_TAG:
++	    case ASN1_ETYPE_SIZE:
++	      break;
++	    default:
++	      fprintf (out, "\n");
++	    }
++	}
++
++      if (p->down)
++	{
++	  p = p->down;
++	  indent += 2;
++	}
++      else if (p == root)
++	{
++	  p = NULL;
++	  break;
++	}
++      else if (p->right)
++	p = p->right;
++      else
++	{
++	  while (1)
++	    {
++	      p = _asn1_find_up (p);
++	      if (p == root)
++		{
++		  p = NULL;
++		  break;
++		}
++	      indent -= 2;
++	      if (p->right)
++		{
++		  p = p->right;
++		  break;
++		}
++	    }
++	}
++    }
++}
++
++
++
++/**
++ * asn1_number_of_elements:
++ * @element: pointer to the root of an ASN1 structure.
++ * @name: the name of a sub-structure of ROOT.
++ * @num: pointer to an integer where the result will be stored
++ *
++ * Counts the number of elements of a sub-structure called NAME with
++ * names equal to "?1","?2", ...
++ *
++ * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if
++ *   @name is not known, %ASN1_GENERIC_ERROR if pointer @num is %NULL.
++ **/
++int
++asn1_number_of_elements (asn1_node_const element, const char *name, int *num)
++{
++  asn1_node_const node, p;
++
++  if (num == NULL)
++    return ASN1_GENERIC_ERROR;
++
++  *num = 0;
++
++  node = asn1_find_node (element, name);
++  if (node == NULL)
++    return ASN1_ELEMENT_NOT_FOUND;
++
++  p = node->down;
++
++  while (p)
++    {
++      if (p->name[0] == '?')
++	(*num)++;
++      p = p->right;
++    }
++
++  return ASN1_SUCCESS;
++}
++
++
++/**
++ * asn1_find_structure_from_oid:
++ * @definitions: ASN1 definitions
++ * @oidValue: value of the OID to search (e.g. "1.2.3.4").
++ *
++ * Search the structure that is defined just after an OID definition.
++ *
++ * Returns: %NULL when @oidValue not found, otherwise the pointer to a
++ *   constant string that contains the element name defined just after
++ *   the OID.
++ **/
++const char *
++asn1_find_structure_from_oid (asn1_node_const definitions, const char *oidValue)
++{
++  char name[2 * ASN1_MAX_NAME_SIZE + 2];
++  char value[ASN1_MAX_NAME_SIZE];
++  asn1_node p;
++  int len;
++  int result;
++  const char *definitionsName;
++
++  if ((definitions == NULL) || (oidValue == NULL))
++    return NULL;		/* ASN1_ELEMENT_NOT_FOUND; */
++
++  definitionsName = definitions->name;
++
++  /* search the OBJECT_ID into definitions */
++  p = definitions->down;
++  while (p)
++    {
++      if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) &&
++	  (p->type & CONST_ASSIGN))
++	{
++          snprintf(name, sizeof(name), "%s.%s", definitionsName, p->name);
++
++	  len = ASN1_MAX_NAME_SIZE;
++	  result = asn1_read_value (definitions, name, value, &len);
++
++	  if ((result == ASN1_SUCCESS) && (!strcmp (oidValue, value)))
++	    {
++	      p = p->right;
++	      if (p == NULL)	/* reach the end of ASN1 definitions */
++		return NULL;	/* ASN1_ELEMENT_NOT_FOUND; */
++
++	      return p->name;
++	    }
++	}
++      p = p->right;
++    }
++
++  return NULL;			/* ASN1_ELEMENT_NOT_FOUND; */
++}
++
++/**
++ * asn1_copy_node:
++ * @dst: Destination asn1 node.
++ * @dst_name: Field name in destination node.
++ * @src: Source asn1 node.
++ * @src_name: Field name in source node.
++ *
++ * Create a deep copy of a asn1_node variable. That
++ * function requires @dst to be expanded using asn1_create_element().
++ *
++ * Returns: Return %ASN1_SUCCESS on success.
++ **/
++int
++asn1_copy_node (asn1_node dst, const char *dst_name,
++		asn1_node_const src, const char *src_name)
++{
++  int result;
++  asn1_node dst_node;
++  void *data = NULL;
++  int size = 0;
++
++  result = asn1_der_coding (src, src_name, NULL, &size, NULL);
++  if (result != ASN1_MEM_ERROR)
++    return result;
++
++  data = malloc (size);
++  if (data == NULL)
++    return ASN1_MEM_ERROR;
++
++  result = asn1_der_coding (src, src_name, data, &size, NULL);
++  if (result != ASN1_SUCCESS)
++    {
++      free (data);
++      return result;
++    }
++
++  dst_node = asn1_find_node (dst, dst_name);
++  if (dst_node == NULL)
++    {
++      free (data);
++      return ASN1_ELEMENT_NOT_FOUND;
++    }
++
++  result = asn1_der_decoding (&dst_node, data, size, NULL);
++
++  free (data);
++
++  return result;
++}
++
++/**
++ * asn1_dup_node:
++ * @src: Source asn1 node.
++ * @src_name: Field name in source node.
++ *
++ * Create a deep copy of a asn1_node variable. This function
++ * will return an exact copy of the provided structure.
++ *
++ * Returns: Return %NULL on failure.
++ **/
++asn1_node
++asn1_dup_node (asn1_node_const src, const char *src_name)
++{
++  return _asn1_copy_structure2(src, src_name);
++}
+diff --git a/grub-core/lib/libtasn1/lib/element.h b/grub-core/lib/libtasn1/lib/element.h
+new file mode 100644
+index 00000000000..440a33f4bb1
+--- /dev/null
++++ b/grub-core/lib/libtasn1/lib/element.h
+@@ -0,0 +1,40 @@
++/*
++ * Copyright (C) 2000-2014 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * The LIBTASN1 library is free software; you can redistribute it
++ * and/or modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301, USA
++ */
++
++#ifndef _ELEMENT_H
++#define _ELEMENT_H
++
++
++struct node_tail_cache_st
++{
++	asn1_node head; /* the first element of the sequence */
++	asn1_node tail;
++};
++
++int _asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcached);
++
++int _asn1_convert_integer (const unsigned char *value,
++			   unsigned char *value_out,
++			   int value_out_size, int *len);
++
++void _asn1_hierarchical_name (asn1_node_const node, char *name, int name_size);
++
++#endif
+diff --git a/grub-core/lib/libtasn1/lib/gstr.h b/grub-core/lib/libtasn1/lib/gstr.h
+new file mode 100644
+index 00000000000..48229844ff3
+--- /dev/null
++++ b/grub-core/lib/libtasn1/lib/gstr.h
+@@ -0,0 +1,47 @@
++/*
++ * Copyright (C) 2002-2014 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * The LIBTASN1 library is free software; you can redistribute it
++ * and/or modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301, USA
++ */
++
++#ifndef GSTR_H
++# define GSTR_H
++
++unsigned int _asn1_str_cpy (char *dest, size_t dest_tot_size,
++			    const char *src);
++void _asn1_str_cat (char *dest, size_t dest_tot_size, const char *src);
++
++#define Estrcpy(x,y) _asn1_str_cpy(x,ASN1_MAX_ERROR_DESCRIPTION_SIZE,y)
++#define Estrcat(x,y) _asn1_str_cat(x,ASN1_MAX_ERROR_DESCRIPTION_SIZE,y)
++
++inline static
++void safe_memset(void *data, int c, size_t size)
++{
++	volatile unsigned volatile_zero = 0;
++	volatile char *vdata = (volatile char*)data;
++
++	/* This is based on a nice trick for safe memset,
++	 * sent by David Jacobson in the openssl-dev mailing list.
++	 */
++
++	if (size > 0) do {
++		memset(data, c, size);
++	} while(vdata[volatile_zero] != c);
++}
++
++#endif /* GSTR_H */
+diff --git a/grub-core/lib/libtasn1/lib/int.h b/grub-core/lib/libtasn1/lib/int.h
+new file mode 100644
+index 00000000000..ea1625786c1
+--- /dev/null
++++ b/grub-core/lib/libtasn1/lib/int.h
+@@ -0,0 +1,221 @@
++/*
++ * Copyright (C) 2002-2014 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * The LIBTASN1 library is free software; you can redistribute it
++ * and/or modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301, USA
++ */
++
++#ifndef INT_H
++#define INT_H
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <string.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <stdint.h>
++
++#ifdef HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#endif
++
++#include <libtasn1.h>
++
++#define ASN1_SMALL_VALUE_SIZE 16
++
++/* This structure is also in libtasn1.h, but then contains less
++   fields.  You cannot make any modifications to these first fields
++   without breaking ABI.  */
++struct asn1_node_st
++{
++  /* public fields: */
++  char name[ASN1_MAX_NAME_SIZE + 1];	/* Node name */
++  unsigned int name_hash;
++  unsigned int type;		/* Node type */
++  unsigned char *value;		/* Node value */
++  int value_len;
++  asn1_node down;		/* Pointer to the son node */
++  asn1_node right;		/* Pointer to the brother node */
++  asn1_node left;		/* Pointer to the next list element */
++  /* private fields: */
++  unsigned char small_value[ASN1_SMALL_VALUE_SIZE];	/* For small values */
++
++  /* values used during decoding/coding */
++  int tmp_ival;
++  unsigned start; /* the start of the DER sequence - if decoded */
++  unsigned end; /* the end of the DER sequence - if decoded */
++};
++
++typedef struct tag_and_class_st
++{
++  unsigned tag;
++  unsigned class;
++  const char *desc;
++} tag_and_class_st;
++
++/* the types that are handled in _asn1_tags */
++#define CASE_HANDLED_ETYPES \
++	case ASN1_ETYPE_NULL: \
++	case ASN1_ETYPE_BOOLEAN: \
++	case ASN1_ETYPE_INTEGER: \
++	case ASN1_ETYPE_ENUMERATED: \
++	case ASN1_ETYPE_OBJECT_ID: \
++	case ASN1_ETYPE_OCTET_STRING: \
++	case ASN1_ETYPE_GENERALSTRING: \
++        case ASN1_ETYPE_NUMERIC_STRING: \
++        case ASN1_ETYPE_IA5_STRING: \
++        case ASN1_ETYPE_TELETEX_STRING: \
++        case ASN1_ETYPE_PRINTABLE_STRING: \
++        case ASN1_ETYPE_UNIVERSAL_STRING: \
++        case ASN1_ETYPE_BMP_STRING: \
++        case ASN1_ETYPE_UTF8_STRING: \
++        case ASN1_ETYPE_VISIBLE_STRING: \
++	case ASN1_ETYPE_BIT_STRING: \
++	case ASN1_ETYPE_SEQUENCE: \
++	case ASN1_ETYPE_SEQUENCE_OF: \
++	case ASN1_ETYPE_SET: \
++	case ASN1_ETYPE_UTC_TIME: \
++	case ASN1_ETYPE_GENERALIZED_TIME: \
++	case ASN1_ETYPE_SET_OF
++
++#define ETYPE_TAG(etype) (_asn1_tags[etype].tag)
++#define ETYPE_CLASS(etype) (_asn1_tags[etype].class)
++#define ETYPE_OK(etype) (((etype) != ASN1_ETYPE_INVALID && \
++                          (etype) <= _asn1_tags_size && \
++                          _asn1_tags[(etype)].desc != NULL)?1:0)
++
++#define ETYPE_IS_STRING(etype) ((etype == ASN1_ETYPE_GENERALSTRING || \
++	etype == ASN1_ETYPE_NUMERIC_STRING || etype == ASN1_ETYPE_IA5_STRING || \
++	etype == ASN1_ETYPE_TELETEX_STRING || etype == ASN1_ETYPE_PRINTABLE_STRING || \
++	etype == ASN1_ETYPE_UNIVERSAL_STRING || etype == ASN1_ETYPE_BMP_STRING || \
++	etype == ASN1_ETYPE_UTF8_STRING || etype == ASN1_ETYPE_VISIBLE_STRING || \
++	etype == ASN1_ETYPE_OCTET_STRING)?1:0)
++
++extern unsigned int _asn1_tags_size;
++extern const tag_and_class_st _asn1_tags[];
++
++#define _asn1_strlen(s) strlen((const char *) s)
++#define _asn1_strtol(n,e,b) strtol((const char *) n, e, b)
++#define _asn1_strtoul(n,e,b) strtoul((const char *) n, e, b)
++#define _asn1_strcmp(a,b) strcmp((const char *)a, (const char *)b)
++#define _asn1_strcpy(a,b) strcpy((char *)a, (const char *)b)
++#define _asn1_strcat(a,b) strcat((char *)a, (const char *)b)
++
++#if SIZEOF_UNSIGNED_LONG_INT == 8
++# define _asn1_strtou64(n,e,b) strtoul((const char *) n, e, b)
++#else
++# define _asn1_strtou64(n,e,b) strtoull((const char *) n, e, b)
++#endif
++
++#define MAX_LOG_SIZE 1024	/* maximum number of characters of a log message */
++
++/* Define used for visiting trees. */
++#define UP     1
++#define RIGHT  2
++#define DOWN   3
++
++/***********************************************************************/
++/* List of constants to better specify the type of typedef asn1_node_st.   */
++/***********************************************************************/
++/*  Used with TYPE_TAG  */
++#define CONST_UNIVERSAL   (1U<<8)
++#define CONST_PRIVATE     (1U<<9)
++#define CONST_APPLICATION (1U<<10)
++#define CONST_EXPLICIT    (1U<<11)
++#define CONST_IMPLICIT    (1U<<12)
++
++#define CONST_TAG         (1U<<13)	/*  Used in ASN.1 assignement  */
++#define CONST_OPTION      (1U<<14)
++#define CONST_DEFAULT     (1U<<15)
++#define CONST_TRUE        (1U<<16)
++#define CONST_FALSE       (1U<<17)
++
++#define CONST_LIST        (1U<<18)	/*  Used with TYPE_INTEGER and TYPE_BIT_STRING  */
++#define CONST_MIN_MAX     (1U<<19)
++
++#define CONST_1_PARAM     (1U<<20)
++
++#define CONST_SIZE        (1U<<21)
++
++#define CONST_DEFINED_BY  (1U<<22)
++
++/* Those two are deprecated and used for backwards compatibility */
++#define CONST_GENERALIZED (1U<<23)
++#define CONST_UTC         (1U<<24)
++
++/* #define CONST_IMPORTS     (1U<<25) */
++
++#define CONST_NOT_USED    (1U<<26)
++#define CONST_SET         (1U<<27)
++#define CONST_ASSIGN      (1U<<28)
++
++#define CONST_DOWN        (1U<<29)
++#define CONST_RIGHT       (1U<<30)
++
++
++#define ASN1_ETYPE_TIME 17
++/****************************************/
++/* Returns the first 8 bits.            */
++/* Used with the field type of asn1_node_st */
++/****************************************/
++inline static unsigned int
++type_field (unsigned int ntype)
++{
++  return (ntype & 0xff);
++}
++
++/* To convert old types from a static structure */
++inline static unsigned int
++convert_old_type (unsigned int ntype)
++{
++  unsigned int type = ntype & 0xff;
++  if (type == ASN1_ETYPE_TIME)
++    {
++      if (ntype & CONST_UTC)
++	type = ASN1_ETYPE_UTC_TIME;
++      else
++	type = ASN1_ETYPE_GENERALIZED_TIME;
++
++      ntype &= ~(CONST_UTC | CONST_GENERALIZED);
++      ntype &= 0xffffff00;
++      ntype |= type;
++
++      return ntype;
++    }
++  else
++    return ntype;
++}
++
++static inline
++void *_asn1_realloc(void *ptr, size_t size)
++{
++  void *ret;
++
++  if (size == 0)
++    return ptr;
++
++  ret = realloc(ptr, size);
++  if (ret == NULL)
++    {
++      free(ptr);
++    }
++  return ret;
++}
++
++#endif /* INT_H */
+diff --git a/grub-core/lib/libtasn1/lib/parser_aux.h b/grub-core/lib/libtasn1/lib/parser_aux.h
+new file mode 100644
+index 00000000000..598e684b355
+--- /dev/null
++++ b/grub-core/lib/libtasn1/lib/parser_aux.h
+@@ -0,0 +1,172 @@
++/*
++ * Copyright (C) 2000-2014 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * The LIBTASN1 library is free software; you can redistribute it
++ * and/or modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301, USA
++ */
++
++#ifndef _PARSER_AUX_H
++#define _PARSER_AUX_H
++
++/***********************************************/
++/* Type: list_type                             */
++/* Description: type used in the list during   */
++/* the structure creation.                     */
++/***********************************************/
++typedef struct list_struct
++{
++  asn1_node node;
++  struct list_struct *next;
++} list_type;
++
++/***************************************/
++/*  Functions used by ASN.1 parser     */
++/***************************************/
++asn1_node _asn1_add_static_node (list_type **e_list, unsigned int type);
++
++void _asn1_delete_list (list_type *e_list);
++
++void _asn1_delete_list_and_nodes (list_type *e_list);
++
++void _asn1_delete_node_from_list (list_type *list, asn1_node node);
++
++asn1_node
++_asn1_set_value (asn1_node node, const void *value, unsigned int len);
++
++asn1_node _asn1_set_value_m (asn1_node node, void *value, unsigned int len);
++
++asn1_node
++_asn1_set_value_lv (asn1_node node, const void *value, unsigned int len);
++
++asn1_node
++_asn1_append_value (asn1_node node, const void *value, unsigned int len);
++
++asn1_node _asn1_set_name (asn1_node node, const char *name);
++
++asn1_node _asn1_cpy_name (asn1_node dst, asn1_node_const src);
++
++asn1_node _asn1_set_right (asn1_node node, asn1_node right);
++
++asn1_node _asn1_get_last_right (asn1_node_const node);
++
++void _asn1_remove_node (asn1_node node, unsigned int flags);
++
++/* Max 64-bit integer length is 20 chars + 1 for sign + 1 for null termination */
++#define LTOSTR_MAX_SIZE 22
++char *_asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE]);
++
++asn1_node _asn1_find_up (asn1_node_const node);
++
++int _asn1_change_integer_value (asn1_node node);
++
++#define EXPAND_OBJECT_ID_MAX_RECURSION 16
++int _asn1_expand_object_id (list_type **list, asn1_node node);
++
++int _asn1_type_set_config (asn1_node node);
++
++int _asn1_check_identifier (asn1_node_const node);
++
++int _asn1_set_default_tag (asn1_node node);
++
++/******************************************************************/
++/* Function : _asn1_get_right                                     */
++/* Description: returns the element pointed by the RIGHT field of */
++/*              a NODE_ASN element.                               */
++/* Parameters:                                                    */
++/*   node: NODE_ASN element pointer.                              */
++/* Return: field RIGHT of NODE.                                   */
++/******************************************************************/
++inline static asn1_node
++_asn1_get_right (asn1_node_const node)
++{
++  if (node == NULL)
++    return NULL;
++  return node->right;
++}
++
++/******************************************************************/
++/* Function : _asn1_set_down                                      */
++/* Description: sets the field DOWN in a NODE_ASN element.        */
++/* Parameters:                                                    */
++/*   node: element pointer.                                       */
++/*   down: pointer to a NODE_ASN element that you want be pointed */
++/*          by NODE.                                              */
++/* Return: pointer to *NODE.                                      */
++/******************************************************************/
++inline static asn1_node
++_asn1_set_down (asn1_node node, asn1_node down)
++{
++  if (node == NULL)
++    return node;
++  node->down = down;
++  if (down)
++    down->left = node;
++  return node;
++}
++
++/******************************************************************/
++/* Function : _asn1_get_down                                      */
++/* Description: returns the element pointed by the DOWN field of  */
++/*              a NODE_ASN element.                               */
++/* Parameters:                                                    */
++/*   node: NODE_ASN element pointer.                              */
++/* Return: field DOWN of NODE.                                    */
++/******************************************************************/
++inline static asn1_node
++_asn1_get_down (asn1_node_const node)
++{
++  if (node == NULL)
++    return NULL;
++  return node->down;
++}
++
++/******************************************************************/
++/* Function : _asn1_get_name                                      */
++/* Description: returns the name of a NODE_ASN element.           */
++/* Parameters:                                                    */
++/*   node: NODE_ASN element pointer.                              */
++/* Return: a null terminated string.                              */
++/******************************************************************/
++inline static char *
++_asn1_get_name (asn1_node_const node)
++{
++  if (node == NULL)
++    return NULL;
++  return (char *) node->name;
++}
++
++/******************************************************************/
++/* Function : _asn1_mod_type                                      */
++/* Description: change the field TYPE of an NODE_ASN element.     */
++/*              The new value is the old one | (bitwise or) the   */
++/*              paramener VALUE.                                  */
++/* Parameters:                                                    */
++/*   node: NODE_ASN element pointer.                              */
++/*   value: the integer value that must be or-ed with the current */
++/*          value of field TYPE.                                  */
++/* Return: NODE pointer.                                          */
++/******************************************************************/
++inline static asn1_node
++_asn1_mod_type (asn1_node node, unsigned int value)
++{
++  if (node == NULL)
++    return node;
++  node->type |= value;
++  return node;
++}
++
++#endif
+diff --git a/grub-core/lib/libtasn1/lib/structure.h b/grub-core/lib/libtasn1/lib/structure.h
+new file mode 100644
+index 00000000000..99e685da07a
+--- /dev/null
++++ b/grub-core/lib/libtasn1/lib/structure.h
+@@ -0,0 +1,45 @@
++/*
++ * Copyright (C) 2002-2014 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * The LIBTASN1 library is free software; you can redistribute it
++ * and/or modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301, USA
++ */
++
++/*************************************************/
++/* File: structure.h                             */
++/* Description: list of exported object by       */
++/*   "structure.c"                               */
++/*************************************************/
++
++#ifndef _STRUCTURE_H
++#define _STRUCTURE_H
++
++#include "parser_aux.h" // list_type
++
++int _asn1_create_static_structure (asn1_node_const pointer,
++				   char *output_file_name, char *vector_name);
++
++asn1_node _asn1_copy_structure3 (asn1_node_const source_node);
++
++asn1_node _asn1_add_single_node (unsigned int type);
++
++asn1_node _asn1_find_left (asn1_node_const node);
++
++int
++_asn1_delete_structure (list_type *e_list, asn1_node *structure, unsigned int flags);
++
++#endif
+diff --git a/include/grub/libtasn1.h b/include/grub/libtasn1.h
+new file mode 100644
+index 00000000000..6fd7a30dc35
+--- /dev/null
++++ b/include/grub/libtasn1.h
+@@ -0,0 +1,588 @@
++/*
++ * Copyright (C) 2002-2014 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * LIBTASN1 is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Lesser General Public License as
++ * published by the Free Software Foundation; either version 2.1 of
++ * the License, or (at your option) any later version.
++ *
++ * LIBTASN1 is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with LIBTASN1; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301, USA
++ *
++ */
++
++/**
++ * libtasn1:Short_Description:
++ *
++ * GNU ASN.1 library
++ */
++/**
++ * libtasn1:Long_Description:
++ *
++ * The Libtasn1 library provides Abstract Syntax Notation One (ASN.1, as
++ * specified by the X.680 ITU-T recommendation) parsing and structures
++ * management, and Distinguished Encoding Rules (DER, as per X.690)
++ * encoding and decoding functions.
++ */
++
++
++#ifndef LIBTASN1_H
++#define LIBTASN1_H
++
++#ifndef ASN1_API
++#if defined ASN1_BUILDING && defined HAVE_VISIBILITY && HAVE_VISIBILITY
++#define ASN1_API __attribute__((__visibility__("default")))
++#elif defined ASN1_BUILDING && defined _MSC_VER && ! defined ASN1_STATIC
++#define ASN1_API __declspec(dllexport)
++#elif defined _MSC_VER && ! defined ASN1_STATIC
++#define ASN1_API __declspec(dllimport)
++#else
++#define ASN1_API
++#endif
++#endif
++
++#ifdef __GNUC__
++# define __LIBTASN1_CONST__  __attribute__((const))
++# define __LIBTASN1_PURE__  __attribute__((pure))
++#else
++# define __LIBTASN1_CONST__
++# define __LIBTASN1_PURE__
++#endif
++
++#include <sys/types.h>
++#include <time.h>
++#include <stdio.h>		/* for FILE* */
++
++#ifdef __cplusplus
++extern "C"
++{
++#endif
++
++/**
++ * ASN1_VERSION:
++ *
++ * Version of the library as a string.
++ */
++#define ASN1_VERSION "4.16.0"
++
++/**
++ * ASN1_VERSION_MAJOR:
++ *
++ * Major version number of the library.
++ */
++#define ASN1_VERSION_MAJOR 4
++
++/**
++ * ASN1_VERSION_MINOR:
++ *
++ * Minor version number of the library.
++ */
++#define ASN1_VERSION_MINOR 16
++
++/**
++ * ASN1_VERSION_PATCH:
++ *
++ * Patch version number of the library.
++ */
++#define ASN1_VERSION_PATCH 0
++
++/**
++ * ASN1_VERSION_NUMBER:
++ *
++ * Version number of the library as a number.
++ */
++#define ASN1_VERSION_NUMBER 0x041000
++
++
++#if defined __GNUC__ && !defined ASN1_INTERNAL_BUILD
++# define _ASN1_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
++# if _ASN1_GCC_VERSION >= 30100
++#  define _ASN1_GCC_ATTR_DEPRECATED __attribute__ ((__deprecated__))
++# endif
++#endif
++
++#ifndef _ASN1_GCC_ATTR_DEPRECATED
++#define _ASN1_GCC_ATTR_DEPRECATED
++#endif
++
++/*****************************************/
++/* Errors returned by libtasn1 functions */
++/*****************************************/
++#define ASN1_SUCCESS			0
++#define ASN1_FILE_NOT_FOUND		1
++#define ASN1_ELEMENT_NOT_FOUND		2
++#define ASN1_IDENTIFIER_NOT_FOUND	3
++#define ASN1_DER_ERROR			4
++#define ASN1_VALUE_NOT_FOUND		5
++#define ASN1_GENERIC_ERROR		6
++#define ASN1_VALUE_NOT_VALID		7
++#define ASN1_TAG_ERROR			8
++#define ASN1_TAG_IMPLICIT		9
++#define ASN1_ERROR_TYPE_ANY		10
++#define ASN1_SYNTAX_ERROR		11
++#define ASN1_MEM_ERROR			12
++#define ASN1_MEM_ALLOC_ERROR		13
++#define ASN1_DER_OVERFLOW		14
++#define ASN1_NAME_TOO_LONG		15
++#define ASN1_ARRAY_ERROR		16
++#define ASN1_ELEMENT_NOT_EMPTY		17
++#define ASN1_TIME_ENCODING_ERROR	18
++#define ASN1_RECURSION			19
++
++/*************************************/
++/* Constants used in asn1_visit_tree */
++/*************************************/
++#define ASN1_PRINT_NAME			1
++#define ASN1_PRINT_NAME_TYPE		2
++#define ASN1_PRINT_NAME_TYPE_VALUE	3
++#define ASN1_PRINT_ALL			4
++
++/*****************************************/
++/* Constants returned by asn1_read_tag   */
++/*****************************************/
++#define ASN1_CLASS_UNIVERSAL		0x00	/* old: 1 */
++#define ASN1_CLASS_APPLICATION		0x40	/* old: 2 */
++#define ASN1_CLASS_CONTEXT_SPECIFIC	0x80	/* old: 3 */
++#define ASN1_CLASS_PRIVATE		0xC0	/* old: 4 */
++#define ASN1_CLASS_STRUCTURED		0x20
++
++/*****************************************/
++/* Constants returned by asn1_read_tag   */
++/*****************************************/
++#define ASN1_TAG_BOOLEAN		0x01
++#define ASN1_TAG_INTEGER		0x02
++#define ASN1_TAG_SEQUENCE		0x10
++#define ASN1_TAG_SET			0x11
++#define ASN1_TAG_OCTET_STRING		0x04
++#define ASN1_TAG_BIT_STRING		0x03
++#define ASN1_TAG_UTCTime		0x17
++#define ASN1_TAG_GENERALIZEDTime	0x18
++#define ASN1_TAG_OBJECT_ID		0x06
++#define ASN1_TAG_ENUMERATED		0x0A
++#define ASN1_TAG_NULL			0x05
++#define ASN1_TAG_GENERALSTRING		0x1B
++#define ASN1_TAG_NUMERIC_STRING		0x12
++#define ASN1_TAG_IA5_STRING		0x16
++#define ASN1_TAG_TELETEX_STRING		0x14
++#define ASN1_TAG_PRINTABLE_STRING	0x13
++#define ASN1_TAG_UNIVERSAL_STRING	0x1C
++#define ASN1_TAG_BMP_STRING		0x1E
++#define ASN1_TAG_UTF8_STRING		0x0C
++#define ASN1_TAG_VISIBLE_STRING		0x1A
++
++/**
++ * asn1_node:
++ *
++ * Structure definition used for the node of the tree
++ * that represents an ASN.1 DEFINITION.
++ */
++typedef struct asn1_node_st asn1_node_st;
++
++typedef asn1_node_st *asn1_node;
++typedef const asn1_node_st *asn1_node_const;
++
++/**
++ * ASN1_MAX_NAME_SIZE:
++ *
++ * Maximum number of characters of a name
++ * inside a file with ASN1 definitions.
++ */
++#define ASN1_MAX_NAME_SIZE 64
++
++
++/**
++ * asn1_static_node:
++ * @name: Node name
++ * @type: Node typ
++ * @value: Node value
++ *
++ * For the on-disk format of ASN.1 trees, created by asn1_parser2array().
++ */
++struct asn1_static_node_st
++{
++  const char *name;		/* Node name */
++  unsigned int type;		/* Node type */
++  const void *value;		/* Node value */
++};
++typedef struct asn1_static_node_st asn1_static_node;
++
++/* List of constants for field type of node_asn  */
++#define ASN1_ETYPE_INVALID        0
++#define ASN1_ETYPE_CONSTANT       1
++#define ASN1_ETYPE_IDENTIFIER     2
++#define ASN1_ETYPE_INTEGER        3
++#define ASN1_ETYPE_BOOLEAN        4
++#define ASN1_ETYPE_SEQUENCE       5
++#define ASN1_ETYPE_BIT_STRING     6
++#define ASN1_ETYPE_OCTET_STRING   7
++#define ASN1_ETYPE_TAG            8
++#define ASN1_ETYPE_DEFAULT        9
++#define ASN1_ETYPE_SIZE          10
++#define ASN1_ETYPE_SEQUENCE_OF   11
++#define ASN1_ETYPE_OBJECT_ID     12
++#define ASN1_ETYPE_ANY           13
++#define ASN1_ETYPE_SET           14
++#define ASN1_ETYPE_SET_OF        15
++#define ASN1_ETYPE_DEFINITIONS   16
++#define ASN1_ETYPE_CHOICE        18
++#define ASN1_ETYPE_IMPORTS       19
++#define ASN1_ETYPE_NULL          20
++#define ASN1_ETYPE_ENUMERATED    21
++#define ASN1_ETYPE_GENERALSTRING 27
++#define ASN1_ETYPE_NUMERIC_STRING 28
++#define ASN1_ETYPE_IA5_STRING     29
++#define ASN1_ETYPE_TELETEX_STRING 30
++#define ASN1_ETYPE_PRINTABLE_STRING 31
++#define ASN1_ETYPE_UNIVERSAL_STRING 32
++#define ASN1_ETYPE_BMP_STRING     33
++#define ASN1_ETYPE_UTF8_STRING    34
++#define ASN1_ETYPE_VISIBLE_STRING 35
++#define ASN1_ETYPE_UTC_TIME       36
++#define ASN1_ETYPE_GENERALIZED_TIME 37
++
++/**
++ * ASN1_DELETE_FLAG_ZEROIZE:
++ *
++ * Used by: asn1_delete_structure2()
++ *
++ * Zeroize values prior to deinitialization.
++ */
++#define ASN1_DELETE_FLAG_ZEROIZE 1
++
++/**
++ * ASN1_DECODE_FLAG_ALLOW_PADDING:
++ *
++ * Used by: asn1_der_decoding2()
++ *
++ * This flag would allow arbitrary data past the DER data.
++ */
++#define ASN1_DECODE_FLAG_ALLOW_PADDING 1
++/**
++ * ASN1_DECODE_FLAG_STRICT_DER:
++ *
++ * Used by: asn1_der_decoding2()
++ *
++ * This flag would ensure that no BER decoding takes place.
++ */
++#define ASN1_DECODE_FLAG_STRICT_DER (1<<1)
++/**
++ * ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME:
++ *
++ * Used by: asn1_der_decoding2()
++ *
++ * This flag will tolerate Time encoding errors when in strict DER.
++ */
++#define ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME (1<<2)
++
++
++/**
++ * asn1_data_node_st:
++ * @name: Node name
++ * @value: Node value
++ * @value_len: Node value size
++ * @type: Node value type (ASN1_ETYPE_*)
++ *
++ * Data node inside a #asn1_node structure.
++ */
++struct asn1_data_node_st
++{
++  const char *name;		/* Node name */
++  const void *value;		/* Node value */
++  unsigned int value_len;	/* Node value size */
++  unsigned int type;		/* Node value type (ASN1_ETYPE_*) */
++};
++typedef struct asn1_data_node_st asn1_data_node_st;
++
++/***********************************/
++/*  Fixed constants                */
++/***********************************/
++
++/**
++ * ASN1_MAX_ERROR_DESCRIPTION_SIZE:
++ *
++ * Maximum number of characters
++ * of a description message
++ * (null character included).
++ */
++#define ASN1_MAX_ERROR_DESCRIPTION_SIZE 128
++
++/***********************************/
++/*  Functions definitions          */
++/***********************************/
++
++extern ASN1_API int
++  asn1_parser2tree (const char *file,
++		      asn1_node * definitions, char *error_desc);
++
++extern ASN1_API int
++  asn1_parser2array (const char *inputFileName,
++		       const char *outputFileName,
++		       const char *vectorName, char *error_desc);
++
++extern ASN1_API int
++  asn1_array2tree (const asn1_static_node * array,
++		     asn1_node * definitions, char *errorDescription);
++
++extern ASN1_API void
++  asn1_print_structure (FILE * out, asn1_node_const structure,
++			  const char *name, int mode);
++
++extern ASN1_API int
++  asn1_create_element (asn1_node_const definitions,
++			 const char *source_name, asn1_node * element);
++
++extern ASN1_API int asn1_delete_structure (asn1_node * structure);
++
++extern ASN1_API int asn1_delete_structure2 (asn1_node * structure, unsigned int flags);
++
++extern ASN1_API int
++  asn1_delete_element (asn1_node structure, const char *element_name);
++
++extern ASN1_API int
++  asn1_write_value (asn1_node node_root, const char *name,
++		      const void *ivalue, int len);
++
++extern ASN1_API int
++  asn1_read_value (asn1_node_const root, const char *name,
++		     void *ivalue, int *len);
++
++extern ASN1_API int
++  asn1_read_value_type (asn1_node_const root, const char *name,
++			  void *ivalue, int *len, unsigned int *etype);
++
++extern ASN1_API int
++  asn1_read_node_value (asn1_node_const node, asn1_data_node_st * data);
++
++extern ASN1_API int
++  asn1_number_of_elements (asn1_node_const element, const char *name, int *num);
++
++extern ASN1_API int
++  asn1_der_coding (asn1_node_const element, const char *name,
++		     void *ider, int *len, char *ErrorDescription);
++
++extern ASN1_API int
++  asn1_der_decoding2 (asn1_node *element, const void *ider,
++			int *max_ider_len, unsigned int flags,
++			char *errorDescription);
++
++extern ASN1_API int
++  asn1_der_decoding (asn1_node * element, const void *ider,
++		       int ider_len, char *errorDescription);
++
++/* Do not use. Use asn1_der_decoding() instead. */
++extern ASN1_API int
++  asn1_der_decoding_element (asn1_node * structure,
++			       const char *elementName,
++			       const void *ider, int len,
++			       char *errorDescription) _ASN1_GCC_ATTR_DEPRECATED;
++
++extern ASN1_API int
++  asn1_der_decoding_startEnd (asn1_node element,
++				const void *ider, int ider_len,
++				const char *name_element,
++				int *start, int *end);
++
++extern ASN1_API int
++  asn1_expand_any_defined_by (asn1_node_const definitions, asn1_node * element);
++
++extern ASN1_API int
++  asn1_expand_octet_string (asn1_node_const definitions,
++			      asn1_node * element,
++			      const char *octetName, const char *objectName);
++
++extern ASN1_API int
++  asn1_read_tag (asn1_node_const root, const char *name,
++		   int *tagValue, int *classValue);
++
++extern ASN1_API const char *asn1_find_structure_from_oid (asn1_node_const
++							    definitions,
++							    const char
++							    *oidValue);
++
++__LIBTASN1_PURE__
++extern ASN1_API const char *asn1_check_version (const char *req_version);
++
++__LIBTASN1_PURE__
++extern ASN1_API const char *asn1_strerror (int error);
++
++extern ASN1_API void asn1_perror (int error);
++
++#define ASN1_MAX_TAG_SIZE 4
++#define ASN1_MAX_LENGTH_SIZE 9
++#define ASN1_MAX_TL_SIZE (ASN1_MAX_TAG_SIZE+ASN1_MAX_LENGTH_SIZE)
++extern ASN1_API long
++  asn1_get_length_der (const unsigned char *der, int der_len, int *len);
++
++extern ASN1_API long
++  asn1_get_length_ber (const unsigned char *ber, int ber_len, int *len);
++
++extern ASN1_API void
++  asn1_length_der (unsigned long int len, unsigned char *der, int *der_len);
++
++/* Other utility functions. */
++
++extern ASN1_API
++  int asn1_decode_simple_der (unsigned int etype, const unsigned char *der,
++				unsigned int _der_len,
++				const unsigned char **str,
++				unsigned int *str_len);
++
++extern ASN1_API
++  int asn1_decode_simple_ber (unsigned int etype, const unsigned char *der,
++				unsigned int _der_len,
++				unsigned char **str,
++				unsigned int *str_len,
++				unsigned int *ber_len);
++
++extern ASN1_API int
++  asn1_encode_simple_der (unsigned int etype, const unsigned char *str,
++			    unsigned int str_len, unsigned char *tl,
++			    unsigned int *tl_len);
++
++extern ASN1_API asn1_node
++  asn1_find_node (asn1_node_const pointer, const char *name);
++
++extern ASN1_API int
++  asn1_copy_node (asn1_node dst, const char *dst_name,
++		    asn1_node_const src, const char *src_name);
++extern ASN1_API asn1_node
++  asn1_dup_node (asn1_node_const src, const char *src_name);
++
++/* Internal and low-level DER utility functions. */
++
++extern ASN1_API int
++  asn1_get_tag_der (const unsigned char *der, int der_len,
++		      unsigned char *cls, int *len, unsigned long *tag);
++
++extern ASN1_API void
++  asn1_octet_der (const unsigned char *str, int str_len,
++		    unsigned char *der, int *der_len);
++
++extern ASN1_API int
++  asn1_get_octet_der (const unsigned char *der, int der_len,
++			int *ret_len, unsigned char *str,
++			int str_size, int *str_len);
++
++extern ASN1_API void asn1_bit_der (const unsigned char *str, int bit_len,
++				     unsigned char *der, int *der_len);
++
++extern ASN1_API int
++  asn1_get_bit_der (const unsigned char *der, int der_len,
++		      int *ret_len, unsigned char *str,
++		      int str_size, int *bit_len);
++
++extern ASN1_API int
++  asn1_get_object_id_der (const unsigned char *der,
++                          int der_len, int *ret_len,
++                          char *str, int str_size);
++
++extern ASN1_API int
++  asn1_object_id_der (const char *str, unsigned char *der, int *der_len,
++                      unsigned flags);
++
++/* Compatibility types */
++
++/**
++ * asn1_retCode:
++ *
++ * Type formerly returned by libtasn1 functions.
++ *
++ * Deprecated: 3.0: Use int instead.
++ */
++typedef int asn1_retCode;
++
++/**
++ * node_asn_struct:
++ *
++ * Compat #define.
++ *
++ * Deprecated: 3.0: Use #asn1_node instead.
++ */
++#define node_asn_struct asn1_node_st
++
++/**
++ * node_asn:
++ *
++ * Compat #define.
++ *
++ * Deprecated: 3.0: Use #asn1_node instead.
++ */
++#define node_asn asn1_node_st
++
++/**
++ * ASN1_TYPE:
++ *
++ * Compat #define.
++ *
++ * Deprecated: 3.0: Use #asn1_node instead.
++ */
++#define ASN1_TYPE asn1_node
++
++/**
++ * ASN1_TYPE_EMPTY:
++ *
++ * Compat #define.
++ *
++ * Deprecated: 3.0: Use NULL instead.
++ */
++#define ASN1_TYPE_EMPTY NULL
++
++/**
++ * static_struct_asn:
++ *
++ * Compat #define.
++ *
++ * Deprecated: 3.0: Use #asn1_static_node instead.
++ */
++#define static_struct_asn asn1_static_node_st
++
++/**
++ * ASN1_ARRAY_TYPE:
++ *
++ * Compat #define.
++ *
++ * Deprecated: 3.0: Use #asn1_static_node instead.
++ */
++#define ASN1_ARRAY_TYPE asn1_static_node
++
++/**
++ * asn1_static_node_t:
++ *
++ * Compat #define.
++ *
++ * Deprecated: 3.0: Use #asn1_static_node instead.
++ */
++#define asn1_static_node_t asn1_static_node
++
++/**
++ * node_data_struct:
++ *
++ * Compat #define.
++ *
++ * Deprecated: 3.0: Use #asn1_data_node_st instead.
++ */
++#define node_data_struct asn1_data_node_st
++
++/**
++ * ASN1_DATA_NODE:
++ *
++ * Compat #define.
++ *
++ * Deprecated: 3.0: Use #asn1_data_node_st instead.
++ */
++#define ASN1_DATA_NODE asn1_data_node_st
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif				/* LIBTASN1_H */
+diff --git a/grub-core/lib/libtasn1/LICENSE b/grub-core/lib/libtasn1/LICENSE
+new file mode 100644
+index 00000000000..e8b3628db9b
+--- /dev/null
++++ b/grub-core/lib/libtasn1/LICENSE
+@@ -0,0 +1,16 @@
++LICENSING
++=========
++
++The libtasn1 library is released under the GNU Lesser General Public
++License (LGPL) version 2.1 or later; see [COPYING.LESSER](doc/COPYING.LESSER)
++for the license terms.
++
++The GNU LGPL applies to the main libtasn1 library, while the
++included applications library are under the GNU GPL version 3.
++The libtasn1 library is located in the lib directory, while the applications
++in src/.
++
++The documentation in doc/ is under the GNU FDL license 1.3.
++
++For any copyright year range specified as YYYY-ZZZZ in this package
++note that the range specifies every single year in that closed interval.
+diff --git a/grub-core/lib/libtasn1/README.md b/grub-core/lib/libtasn1/README.md
+new file mode 100644
+index 00000000000..50a8642296c
+--- /dev/null
++++ b/grub-core/lib/libtasn1/README.md
+@@ -0,0 +1,91 @@
++|Branch|CI system|Status|
++|:----:|:-------:|-----:|
++|Master|Gitlab|[![build status](https://gitlab.com/gnutls/libtasn1/badges/master/pipeline.svg)](https://gitlab.com/gnutls/libtasn1/commits/master)[![coverage report](https://gitlab.com/gnutls/libtasn1/badges/master/coverage.svg)](https://gnutls.gitlab.io/libtasn1/coverage)|
++
++# libtasn1
++
++This is GNU Libtasn1, a small ASN.1 library.
++
++The C library (libtasn1.*) is licensed under the GNU Lesser General
++Public License version 2.1 or later.  See the file COPYING.LIB.
++
++The command line tool, self tests, examples, and other auxilliary
++files, are licensed under the GNU General Public License version 3.0
++or later.  See the file COPYING.
++
++## Building the library
++
++We require several tools to build the software, including:
++
++* [Make](https://www.gnu.org/software/make/)
++* [Automake](https://www.gnu.org/software/automake/) (use 1.11.3 or later)
++* [Autoconf](https://www.gnu.org/software/autoconf/)
++* [Libtool](https://www.gnu.org/software/libtool/)
++* [Texinfo](https://www.gnu.org/software/texinfo/)
++* [help2man](http://www.gnu.org/software/help2man/)
++* [Tar](https://www.gnu.org/software/tar/)
++* [Gzip](https://www.gnu.org/software/gzip/)
++* [bison](https://www.gnu.org/software/bison/)
++* [Texlive & epsf](https://www.tug.org/texlive/) (for PDF manual)
++* [GTK-DOC](https://www.gtk.org/gtk-doc/) (for API manual)
++* [Git](https://git-scm.com/)
++* [libabigail](https://pagure.io/libabigail/) (for abi comparison in make dist)
++* [Valgrind](https://valgrind.org/) (optional)
++
++The required software is typically distributed with your operating
++system, and the instructions for installing them differ.  Here are
++some hints:
++
++gNewSense/Debian/Ubuntu:
++```
++sudo apt-get install make git-core autoconf automake libtool
++sudo apt-get install texinfo texlive texlive-generic-recommended texlive-extra-utils
++sudo apt-get install help2man gtk-doc-tools valgrind abigail-tools
++```
++
++The next step is to run autoreconf, ./configure, etc:
++
++```
++$ ./bootstrap
++```
++
++Then build the project normally:
++
++```
++$ make
++$ make check
++```
++
++Happy hacking!
++
++
++## Manual
++
++The manual is in the `doc/` directory of the release.  You can also browse
++the manual online at:
++
++ - https://gnutls.gitlab.io/libtasn1/
++
++
++## Code coverage report
++
++The coverage report is at:
++
++ - https://gnutls.gitlab.io/libtasn1/coverage
++
++
++## Issue trackers
++
++ - [Main issue tracker](https://gitlab.com/gnutls/libtasn1/issues)
++ - [oss-fuzz found issues](https://bugs.chromium.org/p/oss-fuzz/issues/list?q=libtasn1&can=2)
++
++
++## Homepage
++
++The project homepage at the gnu site is at:
++
++http://www.gnu.org/software/libtasn1/
++
++
++For any copyright year range specified as YYYY-ZZZZ in this package
++note that the range specifies every single year in that closed interval.
diff --git a/SOURCES/0187-libtasn1-disable-code-not-needed-in-grub.patch b/SOURCES/0187-libtasn1-disable-code-not-needed-in-grub.patch
new file mode 100644
index 0000000..00f5588
--- /dev/null
+++ b/SOURCES/0187-libtasn1-disable-code-not-needed-in-grub.patch
@@ -0,0 +1,307 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Fri, 1 May 2020 17:12:23 +1000
+Subject: [PATCH] libtasn1: disable code not needed in grub
+
+We don't expect to be able to write ASN.1, only read it,
+so we can disable some code.
+
+Do that with #if 0/#endif, rather than deletion. This means
+that the difference between upstream and grub is smaller,
+which should make updating libtasn1 easier in the future.
+
+With these exclusions we also avoid the need for minmax.h,
+which is convenient because it means we don't have to
+import it from gnulib.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/lib/libtasn1/lib/coding.c    | 12 ++++++++++--
+ grub-core/lib/libtasn1/lib/decoding.c  |  2 ++
+ grub-core/lib/libtasn1/lib/element.c   |  4 ++--
+ grub-core/lib/libtasn1/lib/errors.c    |  3 +++
+ grub-core/lib/libtasn1/lib/structure.c | 10 ++++++----
+ include/grub/libtasn1.h                | 15 +++++++++++++++
+ 6 files changed, 38 insertions(+), 8 deletions(-)
+
+diff --git a/grub-core/lib/libtasn1/lib/coding.c b/grub-core/lib/libtasn1/lib/coding.c
+index 245ea64cf0a..52def598368 100644
+--- a/grub-core/lib/libtasn1/lib/coding.c
++++ b/grub-core/lib/libtasn1/lib/coding.c
+@@ -30,11 +30,11 @@
+ #include "parser_aux.h"
+ #include <gstr.h>
+ #include "element.h"
+-#include "minmax.h"
+ #include <structure.h>
+ 
+ #define MAX_TAG_LEN 16
+ 
++#if 0
+ /******************************************************/
+ /* Function : _asn1_error_description_value_not_found */
+ /* Description: creates the ErrorDescription string   */
+@@ -58,6 +58,7 @@ _asn1_error_description_value_not_found (asn1_node node,
+   Estrcat (ErrorDescription, "' not found");
+ 
+ }
++#endif
+ 
+ /**
+  * asn1_length_der:
+@@ -244,6 +245,7 @@ asn1_encode_simple_der (unsigned int etype, const unsigned char *str,
+   return ASN1_SUCCESS;
+ }
+ 
++#if 0
+ /******************************************************/
+ /* Function : _asn1_time_der                          */
+ /* Description: creates the DER coding for a TIME     */
+@@ -281,7 +283,7 @@ _asn1_time_der (unsigned char *str, int str_len, unsigned char *der,
+ 
+   return ASN1_SUCCESS;
+ }
+-
++#endif
+ 
+ /*
+ void
+@@ -520,6 +522,7 @@ asn1_bit_der (const unsigned char *str, int bit_len,
+ }
+ 
+ 
++#if 0
+ /******************************************************/
+ /* Function : _asn1_complete_explicit_tag             */
+ /* Description: add the length coding to the EXPLICIT */
+@@ -596,6 +599,7 @@ _asn1_complete_explicit_tag (asn1_node node, unsigned char *der,
+ 
+   return ASN1_SUCCESS;
+ }
++#endif
+ 
+ const tag_and_class_st _asn1_tags[] = {
+   [ASN1_ETYPE_GENERALSTRING] =
+@@ -648,6 +652,8 @@ const tag_and_class_st _asn1_tags[] = {
+ 
+ unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]);
+ 
++
++#if 0
+ /******************************************************/
+ /* Function : _asn1_insert_tag_der                    */
+ /* Description: creates the DER coding of tags of one */
+@@ -1413,3 +1419,5 @@ error:
+   asn1_delete_structure (&node);
+   return err;
+ }
++
++#endif
+\ No newline at end of file
+diff --git a/grub-core/lib/libtasn1/lib/decoding.c b/grub-core/lib/libtasn1/lib/decoding.c
+index ff04eb778cb..42f9a92b5d4 100644
+--- a/grub-core/lib/libtasn1/lib/decoding.c
++++ b/grub-core/lib/libtasn1/lib/decoding.c
+@@ -1613,6 +1613,7 @@ asn1_der_decoding (asn1_node * element, const void *ider, int ider_len,
+   return asn1_der_decoding2 (element, ider, &ider_len, 0, errorDescription);
+ }
+ 
++#if 0
+ /**
+  * asn1_der_decoding_element:
+  * @structure: pointer to an ASN1 structure
+@@ -1643,6 +1644,7 @@ asn1_der_decoding_element (asn1_node * structure, const char *elementName,
+ {
+   return asn1_der_decoding(structure, ider, len, errorDescription);
+ }
++#endif
+ 
+ /**
+  * asn1_der_decoding_startEnd:
+diff --git a/grub-core/lib/libtasn1/lib/element.c b/grub-core/lib/libtasn1/lib/element.c
+index 997eb2725dc..539008d8e94 100644
+--- a/grub-core/lib/libtasn1/lib/element.c
++++ b/grub-core/lib/libtasn1/lib/element.c
+@@ -191,7 +191,7 @@ _asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcache)
+   return ASN1_SUCCESS;
+ }
+ 
+-
++#if 0
+ /**
+  * asn1_write_value:
+  * @node_root: pointer to a structure
+@@ -645,7 +645,7 @@ asn1_write_value (asn1_node node_root, const char *name,
+ 
+   return ASN1_SUCCESS;
+ }
+-
++#endif
+ 
+ #define PUT_VALUE( ptr, ptr_size, data, data_size) \
+ 	*len = data_size; \
+diff --git a/grub-core/lib/libtasn1/lib/errors.c b/grub-core/lib/libtasn1/lib/errors.c
+index cee74daf795..42785e8622b 100644
+--- a/grub-core/lib/libtasn1/lib/errors.c
++++ b/grub-core/lib/libtasn1/lib/errors.c
+@@ -57,6 +57,8 @@ static const libtasn1_error_entry error_algorithms[] = {
+   {0, 0}
+ };
+ 
++
++#if 0
+ /**
+  * asn1_perror:
+  * @error: is an error returned by a libtasn1 function.
+@@ -73,6 +75,7 @@ asn1_perror (int error)
+   const char *str = asn1_strerror (error);
+   fprintf (stderr, "LIBTASN1 ERROR: %s\n", str ? str : "(null)");
+ }
++#endif
+ 
+ /**
+  * asn1_strerror:
+diff --git a/grub-core/lib/libtasn1/lib/structure.c b/grub-core/lib/libtasn1/lib/structure.c
+index 8189c56a4c9..fcfde01a393 100644
+--- a/grub-core/lib/libtasn1/lib/structure.c
++++ b/grub-core/lib/libtasn1/lib/structure.c
+@@ -76,7 +76,7 @@ _asn1_find_left (asn1_node_const node)
+   return node->left;
+ }
+ 
+-
++#if 0
+ int
+ _asn1_create_static_structure (asn1_node_const pointer, char *output_file_name,
+ 			       char *vector_name)
+@@ -155,7 +155,7 @@ _asn1_create_static_structure (asn1_node_const pointer, char *output_file_name,
+ 
+   return ASN1_SUCCESS;
+ }
+-
++#endif
+ 
+ /**
+  * asn1_array2tree:
+@@ -718,7 +718,7 @@ asn1_create_element (asn1_node_const definitions, const char *source_name,
+   return res;
+ }
+ 
+-
++#if 0
+ /**
+  * asn1_print_structure:
+  * @out: pointer to the output file (e.g. stdout).
+@@ -1058,7 +1058,7 @@ asn1_print_structure (FILE * out, asn1_node_const structure, const char *name,
+ 	}
+     }
+ }
+-
++#endif
+ 
+ 
+ /**
+@@ -1153,6 +1153,7 @@ asn1_find_structure_from_oid (asn1_node_const definitions, const char *oidValue)
+   return NULL;			/* ASN1_ELEMENT_NOT_FOUND; */
+ }
+ 
++#if 0
+ /**
+  * asn1_copy_node:
+  * @dst: Destination asn1 node.
+@@ -1202,6 +1203,7 @@ asn1_copy_node (asn1_node dst, const char *dst_name,
+ 
+   return result;
+ }
++#endif
+ 
+ /**
+  * asn1_dup_node:
+diff --git a/include/grub/libtasn1.h b/include/grub/libtasn1.h
+index 6fd7a30dc35..785eda2ae3f 100644
+--- a/include/grub/libtasn1.h
++++ b/include/grub/libtasn1.h
+@@ -319,6 +319,8 @@ typedef struct asn1_data_node_st asn1_data_node_st;
+ /*  Functions definitions          */
+ /***********************************/
+ 
++/* These functions are not used in grub and should not be referenced. */
++#if 0
+ extern ASN1_API int
+   asn1_parser2tree (const char *file,
+ 		      asn1_node * definitions, char *error_desc);
+@@ -327,14 +329,17 @@ extern ASN1_API int
+   asn1_parser2array (const char *inputFileName,
+ 		       const char *outputFileName,
+ 		       const char *vectorName, char *error_desc);
++#endif
+ 
+ extern ASN1_API int
+   asn1_array2tree (const asn1_static_node * array,
+ 		     asn1_node * definitions, char *errorDescription);
+ 
++#if 0
+ extern ASN1_API void
+   asn1_print_structure (FILE * out, asn1_node_const structure,
+ 			  const char *name, int mode);
++#endif
+ 
+ extern ASN1_API int
+   asn1_create_element (asn1_node_const definitions,
+@@ -347,9 +352,11 @@ extern ASN1_API int asn1_delete_structure2 (asn1_node * structure, unsigned int
+ extern ASN1_API int
+   asn1_delete_element (asn1_node structure, const char *element_name);
+ 
++#if 0
+ extern ASN1_API int
+   asn1_write_value (asn1_node node_root, const char *name,
+ 		      const void *ivalue, int len);
++#endif
+ 
+ extern ASN1_API int
+   asn1_read_value (asn1_node_const root, const char *name,
+@@ -365,9 +372,11 @@ extern ASN1_API int
+ extern ASN1_API int
+   asn1_number_of_elements (asn1_node_const element, const char *name, int *num);
+ 
++#if 0
+ extern ASN1_API int
+   asn1_der_coding (asn1_node_const element, const char *name,
+ 		     void *ider, int *len, char *ErrorDescription);
++#endif
+ 
+ extern ASN1_API int
+   asn1_der_decoding2 (asn1_node *element, const void *ider,
+@@ -378,12 +387,14 @@ extern ASN1_API int
+   asn1_der_decoding (asn1_node * element, const void *ider,
+ 		       int ider_len, char *errorDescription);
+ 
++#if 0
+ /* Do not use. Use asn1_der_decoding() instead. */
+ extern ASN1_API int
+   asn1_der_decoding_element (asn1_node * structure,
+ 			       const char *elementName,
+ 			       const void *ider, int len,
+ 			       char *errorDescription) _ASN1_GCC_ATTR_DEPRECATED;
++#endif
+ 
+ extern ASN1_API int
+   asn1_der_decoding_startEnd (asn1_node element,
+@@ -408,13 +419,17 @@ extern ASN1_API const char *asn1_find_structure_from_oid (asn1_node_const
+ 							    const char
+ 							    *oidValue);
+ 
++#if 0
+ __LIBTASN1_PURE__
+ extern ASN1_API const char *asn1_check_version (const char *req_version);
++#endif
+ 
+ __LIBTASN1_PURE__
+ extern ASN1_API const char *asn1_strerror (int error);
+ 
++#if 0
+ extern ASN1_API void asn1_perror (int error);
++#endif
+ 
+ #define ASN1_MAX_TAG_SIZE 4
+ #define ASN1_MAX_LENGTH_SIZE 9
diff --git a/SOURCES/0188-libtasn1-changes-for-grub-compatibility.patch b/SOURCES/0188-libtasn1-changes-for-grub-compatibility.patch
new file mode 100644
index 0000000..9b2275c
--- /dev/null
+++ b/SOURCES/0188-libtasn1-changes-for-grub-compatibility.patch
@@ -0,0 +1,202 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Fri, 1 May 2020 20:44:29 +1000
+Subject: [PATCH] libtasn1: changes for grub compatibility
+
+Do a few things to make libtasn1 compile as part of grub:
+
+ - replace strcat. grub removed strcat so replace it with the appropriate
+   calls to memcpy and strlen.
+
+ - replace c_isdigit with grub_isdigit (and don't import c-ctype from
+   gnulib) grub_isdigit provides the same functionality as c_isdigit: it
+   determines if the input is an ASCII digit without regard for locale.
+
+ - replace GL_ATTRIBUTE_PURE with __attribute__((pure)) which been
+   supported since gcc-2.96. This avoids messing around with gnulib.
+
+ - adjust libtasn1.h: drop the ASN1_API logic, it's not needed for our
+   modules. Unconditionally support const and pure attributes and adjust
+   header paths.
+
+ - adjust header paths to "grub/libtasn1.h".
+
+ - replace a 64 bit division with a call to grub_divmod64, preventing
+   creation of __udivdi3 calls on 32 bit platforms.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/lib/libtasn1/lib/decoding.c   | 11 ++++++-----
+ grub-core/lib/libtasn1/lib/element.c    |  3 ++-
+ grub-core/lib/libtasn1/lib/gstr.c       |  4 ++--
+ grub-core/lib/libtasn1/lib/parser_aux.c |  7 ++++---
+ grub-core/lib/libtasn1/lib/int.h        |  4 ++--
+ include/grub/libtasn1.h                 | 26 ++++++--------------------
+ 6 files changed, 22 insertions(+), 33 deletions(-)
+
+diff --git a/grub-core/lib/libtasn1/lib/decoding.c b/grub-core/lib/libtasn1/lib/decoding.c
+index 42f9a92b5d4..7856858b272 100644
+--- a/grub-core/lib/libtasn1/lib/decoding.c
++++ b/grub-core/lib/libtasn1/lib/decoding.c
+@@ -32,7 +32,8 @@
+ #include <element.h>
+ #include <limits.h>
+ #include <intprops.h>
+-#include <c-ctype.h>
++
++#define c_isdigit grub_isdigit
+ 
+ #ifdef DEBUG
+ # define warn() fprintf(stderr, "%s: %d\n", __func__, __LINE__)
+@@ -2008,8 +2009,8 @@ asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element,
+ 	  (p2->type & CONST_ASSIGN))
+ 	{
+ 	  strcpy (name, definitions->name);
+-	  strcat (name, ".");
+-	  strcat (name, p2->name);
++	  memcpy (name + strlen(name), ".", sizeof(" . "));
++	  memcpy (name + strlen(name), p2->name, strlen(p2->name) + 1);
+ 
+ 	  len = sizeof (value);
+ 	  result = asn1_read_value (definitions, name, value, &len);
+@@ -2026,8 +2027,8 @@ asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element,
+ 	      if (p2)
+ 		{
+ 		  strcpy (name, definitions->name);
+-		  strcat (name, ".");
+-		  strcat (name, p2->name);
++		  memcpy (name + strlen(name), ".", sizeof(" . "));
++		  memcpy (name + strlen(name), p2->name, strlen(p2->name) + 1);
+ 
+ 		  result = asn1_create_element (definitions, name, &aux);
+ 		  if (result == ASN1_SUCCESS)
+diff --git a/grub-core/lib/libtasn1/lib/element.c b/grub-core/lib/libtasn1/lib/element.c
+index 539008d8e94..ed761ff56bd 100644
+--- a/grub-core/lib/libtasn1/lib/element.c
++++ b/grub-core/lib/libtasn1/lib/element.c
+@@ -30,9 +30,10 @@
+ #include "parser_aux.h"
+ #include <gstr.h>
+ #include "structure.h"
+-#include "c-ctype.h"
+ #include "element.h"
+ 
++#define c_isdigit grub_isdigit
++
+ void
+ _asn1_hierarchical_name (asn1_node_const node, char *name, int name_size)
+ {
+diff --git a/grub-core/lib/libtasn1/lib/gstr.c b/grub-core/lib/libtasn1/lib/gstr.c
+index e91a3a151c0..e33875c2c7c 100644
+--- a/grub-core/lib/libtasn1/lib/gstr.c
++++ b/grub-core/lib/libtasn1/lib/gstr.c
+@@ -36,13 +36,13 @@ _asn1_str_cat (char *dest, size_t dest_tot_size, const char *src)
+ 
+   if (dest_tot_size - dest_size > str_size)
+     {
+-      strcat (dest, src);
++      memcpy (dest + dest_size, src, str_size + 1);
+     }
+   else
+     {
+       if (dest_tot_size - dest_size > 0)
+ 	{
+-	  strncat (dest, src, (dest_tot_size - dest_size) - 1);
++	  memcpy (dest + dest_size, src, (dest_tot_size - dest_size) - 1);
+ 	  dest[dest_tot_size - 1] = 0;
+ 	}
+     }
+diff --git a/grub-core/lib/libtasn1/lib/parser_aux.c b/grub-core/lib/libtasn1/lib/parser_aux.c
+index d5dbbf8765d..89c9be69dc2 100644
+--- a/grub-core/lib/libtasn1/lib/parser_aux.c
++++ b/grub-core/lib/libtasn1/lib/parser_aux.c
+@@ -26,7 +26,8 @@
+ #include "gstr.h"
+ #include "structure.h"
+ #include "element.h"
+-#include "c-ctype.h"
++
++#define c_isdigit grub_isdigit
+ 
+ char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1];	/* identifier name not found */
+ 
+@@ -40,7 +41,7 @@ char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1];	/* identifier name not fou
+ #ifdef __clang__
+ __attribute__((no_sanitize("integer")))
+ #endif
+-_GL_ATTRIBUTE_PURE
++__attribute__((__pure__))
+ static unsigned int
+ _asn1_hash_name (const char *x)
+ {
+@@ -634,7 +635,7 @@ _asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE])
+   count = 0;
+   do
+     {
+-      d = val / 10;
++      d = grub_divmod64(val, 10, NULL);
+       r = val - d * 10;
+       temp[start + count] = '0' + (char) r;
+       count++;
+diff --git a/grub-core/lib/libtasn1/lib/int.h b/grub-core/lib/libtasn1/lib/int.h
+index ea1625786c1..4a568efee9c 100644
+--- a/grub-core/lib/libtasn1/lib/int.h
++++ b/grub-core/lib/libtasn1/lib/int.h
+@@ -35,7 +35,7 @@
+ #include <sys/types.h>
+ #endif
+ 
+-#include <libtasn1.h>
++#include "grub/libtasn1.h"
+ 
+ #define ASN1_SMALL_VALUE_SIZE 16
+ 
+@@ -115,7 +115,7 @@ extern const tag_and_class_st _asn1_tags[];
+ #define _asn1_strtoul(n,e,b) strtoul((const char *) n, e, b)
+ #define _asn1_strcmp(a,b) strcmp((const char *)a, (const char *)b)
+ #define _asn1_strcpy(a,b) strcpy((char *)a, (const char *)b)
+-#define _asn1_strcat(a,b) strcat((char *)a, (const char *)b)
++#define _asn1_strcat(a,b) memcpy((char *)a + strlen((const char *)a), (const char *)b, strlen((const char *)b) + 1)
+ 
+ #if SIZEOF_UNSIGNED_LONG_INT == 8
+ # define _asn1_strtou64(n,e,b) strtoul((const char *) n, e, b)
+diff --git a/include/grub/libtasn1.h b/include/grub/libtasn1.h
+index 785eda2ae3f..28dbf16c4e0 100644
+--- a/include/grub/libtasn1.h
++++ b/include/grub/libtasn1.h
+@@ -38,29 +38,15 @@
+ #ifndef LIBTASN1_H
+ #define LIBTASN1_H
+ 
+-#ifndef ASN1_API
+-#if defined ASN1_BUILDING && defined HAVE_VISIBILITY && HAVE_VISIBILITY
+-#define ASN1_API __attribute__((__visibility__("default")))
+-#elif defined ASN1_BUILDING && defined _MSC_VER && ! defined ASN1_STATIC
+-#define ASN1_API __declspec(dllexport)
+-#elif defined _MSC_VER && ! defined ASN1_STATIC
+-#define ASN1_API __declspec(dllimport)
+-#else
++/* grub: ASN1_API is not used */
+ #define ASN1_API
+-#endif
+-#endif
+ 
+-#ifdef __GNUC__
+-# define __LIBTASN1_CONST__  __attribute__((const))
+-# define __LIBTASN1_PURE__  __attribute__((pure))
+-#else
+-# define __LIBTASN1_CONST__
+-# define __LIBTASN1_PURE__
+-#endif
++/* grub: all our supported compilers support these attributes */
++#define __LIBTASN1_CONST__  __attribute__((const))
++#define __LIBTASN1_PURE__  __attribute__((pure))
+ 
+-#include <sys/types.h>
+-#include <time.h>
+-#include <stdio.h>		/* for FILE* */
++#include <grub/types.h>
++#include <grub/time.h>
+ 
+ #ifdef __cplusplus
+ extern "C"
diff --git a/SOURCES/0189-libtasn1-compile-into-asn1-module.patch b/SOURCES/0189-libtasn1-compile-into-asn1-module.patch
new file mode 100644
index 0000000..67be4e8
--- /dev/null
+++ b/SOURCES/0189-libtasn1-compile-into-asn1-module.patch
@@ -0,0 +1,70 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Fri, 5 Jun 2020 17:47:25 +1000
+Subject: [PATCH] libtasn1: compile into asn1 module
+
+Create a wrapper file that specifies the module license.
+Set up the makefile so it is built.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/Makefile.core.def        | 15 +++++++++++++++
+ grub-core/lib/libtasn1_wrap/wrap.c | 26 ++++++++++++++++++++++++++
+ 2 files changed, 41 insertions(+)
+ create mode 100644 grub-core/lib/libtasn1_wrap/wrap.c
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 97347ae76f9..21d2c541850 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -2576,3 +2576,18 @@ module = {
+   common = commands/i386/wrmsr.c;
+   enable = x86;
+ };
++
++module = {
++  name = asn1;
++  common = lib/libtasn1/lib/decoding.c;
++  common = lib/libtasn1/lib/coding.c;
++  common = lib/libtasn1/lib/element.c;
++  common = lib/libtasn1/lib/structure.c;
++  common = lib/libtasn1/lib/parser_aux.c;
++  common = lib/libtasn1/lib/gstr.c;
++  common = lib/libtasn1/lib/errors.c;
++  common = lib/libtasn1_wrap/wrap.c;
++  cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
++  // -Wno-type-limits comes from libtasn1's configure.ac
++  cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) -I$(srcdir)/lib/libtasn1/lib -Wno-type-limits';
++};
+diff --git a/grub-core/lib/libtasn1_wrap/wrap.c b/grub-core/lib/libtasn1_wrap/wrap.c
+new file mode 100644
+index 00000000000..622ba942e33
+--- /dev/null
++++ b/grub-core/lib/libtasn1_wrap/wrap.c
+@@ -0,0 +1,26 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2020 IBM Corporation
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/dl.h>
++
++/*
++ * libtasn1 is provided under LGPL2.1+, which is compatible
++ * with GPL3+. As Grub as a whole is under GPL3+, this module
++ * is therefore under GPL3+ also.
++ */
++GRUB_MOD_LICENSE ("GPLv3+");
diff --git a/SOURCES/0190-test_asn1-test-module-for-libtasn1.patch b/SOURCES/0190-test_asn1-test-module-for-libtasn1.patch
new file mode 100644
index 0000000..3a02b24
--- /dev/null
+++ b/SOURCES/0190-test_asn1-test-module-for-libtasn1.patch
@@ -0,0 +1,1457 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Wed, 10 Jun 2020 17:48:42 +1000
+Subject: [PATCH] test_asn1: test module for libtasn1
+
+Import tests from libtasn1 that don't use functionality we don't
+import. I have put them here rather than in the libtasn1 directory
+because:
+
+ -  They need much more significant changes to run in the grub
+    context.
+
+ -  I don't expect they will need to be changed when updating
+    libtasn1: I expect the old tests will usually continue to pass on
+    new versions.
+
+This doesn't test the full decoder but that will be exercised in
+test suites for coming patch sets.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ Makefile.util.def                                  |   6 +
+ grub-core/Makefile.core.def                        |  13 ++
+ .../lib/libtasn1_wrap/tests/CVE-2018-1000654.c     |  61 ++++++
+ grub-core/lib/libtasn1_wrap/tests/Test_overflow.c  | 138 ++++++++++++++
+ grub-core/lib/libtasn1_wrap/tests/Test_simple.c    | 207 ++++++++++++++++++++
+ grub-core/lib/libtasn1_wrap/tests/Test_strings.c   | 150 +++++++++++++++
+ .../lib/libtasn1_wrap/tests/object-id-decoding.c   | 116 +++++++++++
+ .../lib/libtasn1_wrap/tests/object-id-encoding.c   | 120 ++++++++++++
+ grub-core/lib/libtasn1_wrap/tests/octet-string.c   | 211 +++++++++++++++++++++
+ grub-core/lib/libtasn1_wrap/tests/reproducers.c    |  81 ++++++++
+ grub-core/lib/libtasn1_wrap/wrap_tests.c           |  75 ++++++++
+ .../tests/CVE-2018-1000654-1_asn1_tab.h            |  32 ++++
+ .../tests/CVE-2018-1000654-2_asn1_tab.h            |  36 ++++
+ grub-core/lib/libtasn1_wrap/wrap_tests.h           |  38 ++++
+ .gitignore                                         |   1 +
+ tests/test_asn1.in                                 |  12 ++
+ 16 files changed, 1297 insertions(+)
+ create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c
+ create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_overflow.c
+ create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_simple.c
+ create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_strings.c
+ create mode 100644 grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c
+ create mode 100644 grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c
+ create mode 100644 grub-core/lib/libtasn1_wrap/tests/octet-string.c
+ create mode 100644 grub-core/lib/libtasn1_wrap/tests/reproducers.c
+ create mode 100644 grub-core/lib/libtasn1_wrap/wrap_tests.c
+ create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h
+ create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h
+ create mode 100644 grub-core/lib/libtasn1_wrap/wrap_tests.h
+ create mode 100644 tests/test_asn1.in
+
+diff --git a/Makefile.util.def b/Makefile.util.def
+index e1242f54022..8cfbe69a76e 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -1305,6 +1305,12 @@ script = {
+   common = tests/syslinux_test.in;
+ };
+ 
++script = {
++  testcase;
++  name = test_asn1;
++  common = tests/test_asn1.in;
++};
++
+ program = {
+   testcase;
+   name = example_unit_test;
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 21d2c541850..b4aaccf7b57 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -2591,3 +2591,16 @@ module = {
+   // -Wno-type-limits comes from libtasn1's configure.ac
+   cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) -I$(srcdir)/lib/libtasn1/lib -Wno-type-limits';
+ };
++
++module = {
++  name = test_asn1;
++  common = lib/libtasn1_wrap/tests/CVE-2018-1000654.c;
++  common = lib/libtasn1_wrap/tests/object-id-decoding.c;
++  common = lib/libtasn1_wrap/tests/object-id-encoding.c;
++  common = lib/libtasn1_wrap/tests/octet-string.c;
++  common = lib/libtasn1_wrap/tests/reproducers.c;
++  common = lib/libtasn1_wrap/tests/Test_overflow.c;
++  common = lib/libtasn1_wrap/tests/Test_simple.c;
++  common = lib/libtasn1_wrap/tests/Test_strings.c;
++  common = lib/libtasn1_wrap/wrap_tests.c;
++};
+diff --git a/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c
+new file mode 100644
+index 00000000000..534e304521e
+--- /dev/null
++++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c
+@@ -0,0 +1,61 @@
++/*
++ * Copyright (C) 2002-2018 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++/****************************************************************/
++/* Description: reproducer for CVE-2018-1000654			*/
++/****************************************************************/
++
++#include <grub/libtasn1.h>
++#include <grub/err.h>
++#include <grub/mm.h>
++#include <grub/misc.h>
++#include <grub/types.h>
++#include "../wrap_tests.h"
++
++#include "CVE-2018-1000654-1_asn1_tab.h"
++#include "CVE-2018-1000654-2_asn1_tab.h"
++
++void
++test_CVE_2018_1000654 (void)
++{
++  int result;
++  asn1_node definitions = NULL;
++  char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
++
++  result = asn1_array2tree (CVE_2018_1000654_1_asn1_tab, &definitions, errorDescription);
++  if (result != ASN1_RECURSION)
++    {
++      grub_fatal ("Error: %s\nErrorDescription = %s\n\n",
++		  asn1_strerror (result), errorDescription);
++      return;
++    }
++
++  asn1_delete_structure (&definitions);
++
++  result = asn1_array2tree (CVE_2018_1000654_2_asn1_tab, &definitions, errorDescription);
++  if (result != ASN1_RECURSION)
++    {
++      grub_fatal ("Error: %s\nErrorDescription = %s\n\n",
++		  asn1_strerror (result), errorDescription);
++      return;
++    }
++
++  asn1_delete_structure (&definitions);
++}
+diff --git a/grub-core/lib/libtasn1_wrap/tests/Test_overflow.c b/grub-core/lib/libtasn1_wrap/tests/Test_overflow.c
+new file mode 100644
+index 00000000000..f48aea0ef8b
+--- /dev/null
++++ b/grub-core/lib/libtasn1_wrap/tests/Test_overflow.c
+@@ -0,0 +1,138 @@
++/*
++ * Copyright (C) 2012-2014 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++/* Written by Simon Josefsson */
++
++#include <grub/libtasn1.h>
++#include <grub/err.h>
++#include <grub/mm.h>
++#include <grub/misc.h>
++#include <grub/types.h>
++#include "../wrap_tests.h"
++
++void
++test_overflow(void)
++{
++  /* Test that values larger than long are rejected.  This has worked
++     fine with all versions of libtasn1. */
++
++  {
++    unsigned char der[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
++    long l;
++    int len;
++
++    l = asn1_get_length_der (der, sizeof der, &len);
++
++    if (l != -2L)
++      {
++	grub_fatal ("ERROR: asn1_get_length_der bignum (l %ld len %d)\n", l, len);
++	return;
++      }
++  }
++
++  /* Test that values larger than int but smaller than long are
++     rejected.  This limitation was introduced with libtasn1 2.12. */
++#if (GRUB_LONG_MAX > GRUB_INT_MAX)
++    {
++      unsigned long num = ((long) GRUB_UINT_MAX) << 2;
++      unsigned char der[20];
++      int der_len;
++      long l;
++      int len;
++
++      asn1_length_der (num, der, &der_len);
++
++      l = asn1_get_length_der (der, der_len, &len);
++
++      if (l != -2L)
++	{
++	  grub_fatal ("ERROR: asn1_get_length_der intnum (l %ld len %d)\n", l,
++		      len);
++	  return;
++	}
++    }
++#endif
++
++  /* Test that values larger than would fit in the input string are
++     rejected.  This problem was fixed in libtasn1 2.12. */
++  {
++    unsigned long num = 64;
++    unsigned char der[20];
++    int der_len;
++    long l;
++    int len;
++
++    asn1_length_der (num, der, &der_len);
++
++    der_len = sizeof (der);
++    l = asn1_get_length_der (der, der_len, &len);
++
++    if (l != -4L)
++      {
++	grub_fatal ("ERROR: asn1_get_length_der overflow-small (l %ld len %d)\n",
++		    l, len);
++	return;
++      }
++  }
++
++  /* Test that values larger than would fit in the input string are
++     rejected.  This problem was fixed in libtasn1 2.12. */
++  {
++    unsigned long num = 1073741824;
++    unsigned char der[20];
++    int der_len;
++    long l;
++    int len;
++
++    asn1_length_der (num, der, &der_len);
++
++    der_len = sizeof (der);
++    l = asn1_get_length_der (der, der_len, &len);
++
++    if (l != -4L)
++      {
++	grub_fatal ("ERROR: asn1_get_length_der overflow-large1 (l %ld len %d)\n",
++		    l, len);
++	return;
++      }
++  }
++
++  /* Test that values larger than would fit in the input string are
++     rejected.  This problem was fixed in libtasn1 2.12. */
++  {
++    unsigned long num = 2147483649;
++    unsigned char der[20];
++    int der_len;
++    long l;
++    int len;
++
++    asn1_length_der (num, der, &der_len);
++
++    der_len = sizeof (der);
++    l = asn1_get_length_der (der, der_len, &len);
++
++    if (l != -2L)
++      {
++	grub_fatal ("ERROR: asn1_get_length_der overflow-large2 (l %ld len %d)\n",
++		    l, len);
++	return;
++      }
++  }
++}
+diff --git a/grub-core/lib/libtasn1_wrap/tests/Test_simple.c b/grub-core/lib/libtasn1_wrap/tests/Test_simple.c
+new file mode 100644
+index 00000000000..9f01006ddf4
+--- /dev/null
++++ b/grub-core/lib/libtasn1_wrap/tests/Test_simple.c
+@@ -0,0 +1,207 @@
++/*
++ * Copyright (C) 2011-2014 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ *
++ * Written by Simon Josefsson
++ *
++ */
++
++#include <grub/libtasn1.h>
++#include <grub/mm.h>
++#include <grub/misc.h>
++#include <grub/err.h>
++#include "../wrap_tests.h"
++
++struct tv
++{
++  int bitlen;
++  const char *bitstr;
++  int derlen;
++  const char *der;
++};
++
++static const struct tv tv[] = {
++  {0, "", 2, "\x01\x00"},
++  {1, "\x00", 3, "\x02\x07\x00"},
++  {2, "\x00", 3, "\x02\x06\x00"},
++  {3, "\x00", 3, "\x02\x05\x00"},
++  {4, "\x00", 3, "\x02\x04\x00"},
++  {5, "\x00", 3, "\x02\x03\x00"},
++  {6, "\x00", 3, "\x02\x02\x00"},
++  {7, "\x00", 3, "\x02\x01\x00"},
++  {8, "\x00\x00", 3, "\x02\x00\x00"},
++  {9, "\x00\x00", 4, "\x03\x07\x00\x00"},
++  {10, "\x00\x00", 4, "\x03\x06\x00\x00"},
++  {11, "\x00\x00", 4, "\x03\x05\x00\x00"},
++  {12, "\x00\x00", 4, "\x03\x04\x00\x00"},
++  {13, "\x00\x00", 4, "\x03\x03\x00\x00"},
++  {14, "\x00\x00", 4, "\x03\x02\x00\x00"},
++  {15, "\x00\x00", 4, "\x03\x01\x00\x00"},
++  {16, "\x00\x00", 4, "\x03\x00\x00\x00"},
++  {17, "\x00\x00\x00", 5, "\x04\x07\x00\x00\x00"},
++  {18, "\x00\x00\x00", 5, "\x04\x06\x00\x00\x00"},
++  {19, "\x00\x00\x00", 5, "\x04\x05\x00\x00\x00"},
++  {1, "\xFF", 3, "\x02\x07\x80"},
++  {2, "\xFF", 3, "\x02\x06\xc0"},
++  {3, "\xFF", 3, "\x02\x05\xe0"},
++  {4, "\xFF", 3, "\x02\x04\xf0"},
++  {5, "\xFF", 3, "\x02\x03\xf8"},
++  {6, "\xFF", 3, "\x02\x02\xfc"},
++  {7, "\xFF", 3, "\x02\x01\xfe"},
++  {8, "\xFF\xFF", 3, "\x02\x00\xff"},
++  {9, "\xFF\xFF", 4, "\x03\x07\xff\x80"},
++  {10, "\xFF\xFF", 4, "\x03\x06\xff\xc0"},
++  {11, "\xFF\xFF", 4, "\x03\x05\xff\xe0"},
++  {12, "\xFF\xFF", 4, "\x03\x04\xff\xf0"},
++  {13, "\xFF\xFF", 4, "\x03\x03\xff\xf8"},
++  {14, "\xFF\xFF", 4, "\x03\x02\xff\xfc"},
++  {15, "\xFF\xFF", 4, "\x03\x01\xff\xfe"},
++  {16, "\xFF\xFF", 4, "\x03\x00\xff\xff"},
++  {17, "\xFF\xFF\xFF", 5, "\x04\x07\xff\xff\x80"},
++  {18, "\xFF\xFF\xFF", 5, "\x04\x06\xff\xff\xc0"},
++  {19, "\xFF\xFF\xFF", 5, "\x04\x05\xff\xff\xe0"},
++};
++
++void
++test_simple (void)
++{
++  int result;
++  unsigned char der[100];
++  unsigned char str[100];
++  int der_len = sizeof (der);
++  int str_size = sizeof (str);
++  int ret_len, bit_len;
++  grub_size_t i;
++
++  /* Dummy test */
++
++  asn1_bit_der (NULL, 0, der, &der_len);
++  result = asn1_get_bit_der (der, 0, &ret_len, str, str_size, &bit_len);
++  if (result != ASN1_GENERIC_ERROR)
++    {
++      grub_fatal ("asn1_get_bit_der zero\n");
++      return;
++    }
++
++  /* Encode short strings with increasing bit lengths */
++
++  for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
++    {
++      /* Encode */
++
++      asn1_bit_der ((const unsigned char *) tv[i].bitstr, tv[i].bitlen,
++		    der, &der_len);
++
++#if 0
++      {
++	size_t j;
++	for (j = 0; j < der_len; j++)
++	  printf ("\\x%02x", der[j]);
++	printf ("\n");
++      }
++#endif
++
++      if (der_len != tv[i].derlen || grub_memcmp (der, tv[i].der, der_len) != 0)
++	{
++	  grub_fatal ("asn1_bit_der iter %lu\n", (unsigned long) i);
++	  return;
++	}
++
++      /* Decode it */
++
++      result = asn1_get_bit_der (der, der_len, &ret_len, str,
++				 str_size, &bit_len);
++      if (result != ASN1_SUCCESS || ret_len != tv[i].derlen
++	  || bit_len != tv[i].bitlen)
++	{
++	  grub_fatal ("asn1_get_bit_der iter %lu, err: %d\n", (unsigned long) i, result);
++	  return;
++	}
++    }
++
++
++  /* Decode sample from "A Layman's Guide to a Subset of ASN.1, BER,
++     and DER" section 5.4 "BIT STRING": "The BER encoding of the BIT
++     STRING value "011011100101110111" can be any of the following,
++     among others, depending on the choice of padding bits, the form
++     of length octets [...]".
++   */
++
++  /* 03 04 06 6e 5d c0  DER encoding */
++
++  grub_memcpy (der, "\x04\x06\x6e\x5d\xc0", 5);
++  der_len = 5;
++
++  result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len);
++  if (result != ASN1_SUCCESS || ret_len != 5
++      || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xc0", 3) != 0)
++    {
++      grub_fatal ("asn1_get_bit_der example\n");
++      return;
++    }
++
++  der_len = sizeof (der);
++  asn1_bit_der (str, bit_len, der, &der_len);
++  if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0)
++    {
++      grub_fatal ("asn1_bit_der example roundtrip\n");
++      return;
++    }
++
++  /* 03 04 06 6e 5d e0 padded with "100000" */
++
++  grub_memcpy (der, "\x04\x06\x6e\x5d\xe0", 5);
++  der_len = 5;
++
++  result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len);
++  if (result != ASN1_SUCCESS || ret_len != 5
++      || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xe0", 3) != 0)
++    {
++      grub_fatal ("asn1_get_bit_der example padded\n");
++      return;
++    }
++
++  der_len = sizeof (der);
++  asn1_bit_der (str, bit_len, der, &der_len);
++  if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0)
++    {
++      grub_fatal ("asn1_bit_der example roundtrip\n");
++      return;
++    }
++
++  /* 03 81 04 06 6e 5d c0 long form of length octets */
++
++  grub_memcpy (der, "\x81\x04\x06\x6e\x5d\xc0", 6);
++  der_len = 6;
++
++  result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len);
++
++  if (result != ASN1_SUCCESS || ret_len != 6
++      || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xc0", 3) != 0)
++    {
++      grub_fatal ("asn1_get_bit_der example long form\n");
++      return;
++    }
++
++  der_len = sizeof (der);
++  asn1_bit_der (str, bit_len, der, &der_len);
++  if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0)
++    {
++      grub_fatal ("asn1_bit_der example roundtrip\n");
++      return;
++    }
++}
+diff --git a/grub-core/lib/libtasn1_wrap/tests/Test_strings.c b/grub-core/lib/libtasn1_wrap/tests/Test_strings.c
+new file mode 100644
+index 00000000000..dbe1474b204
+--- /dev/null
++++ b/grub-core/lib/libtasn1_wrap/tests/Test_strings.c
+@@ -0,0 +1,150 @@
++/*
++ * Copyright (C) 2012-2014 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ *
++ * Written by Simon Josefsson
++ *
++ */
++
++#include <grub/mm.h>
++#include <grub/err.h>
++#include <grub/misc.h>
++#include <grub/libtasn1.h>
++#include "../wrap_tests.h"
++
++struct tv
++{
++  unsigned int etype;
++  unsigned int str_len;
++  const void *str;
++  unsigned int der_len;
++  const void *der;
++};
++
++static const struct tv tv[] = {
++  {ASN1_ETYPE_IA5_STRING, 20,
++   "\x63\x73\x63\x61\x40\x70\x61\x73\x73\x70\x6f\x72\x74\x2e\x67\x6f\x76\x2e\x67\x72",
++   22,
++   "\x16\x14\x63\x73\x63\x61\x40\x70\x61\x73\x73\x70\x6f\x72\x74\x2e\x67\x6f\x76\x2e\x67\x72"},
++  {ASN1_ETYPE_PRINTABLE_STRING, 5, "\x4e\x69\x6b\x6f\x73",
++   7, "\x13\x05\x4e\x69\x6b\x6f\x73"},
++  {ASN1_ETYPE_UTF8_STRING, 12, "Αττική",
++   14, "\x0c\x0c\xce\x91\xcf\x84\xcf\x84\xce\xb9\xce\xba\xce\xae"},
++  {ASN1_ETYPE_TELETEX_STRING, 15,
++   "\x53\x69\x6d\x6f\x6e\x20\x4a\x6f\x73\x65\x66\x73\x73\x6f\x6e",
++   17,
++   "\x14\x0f\x53\x69\x6d\x6f\x6e\x20\x4a\x6f\x73\x65\x66\x73\x73\x6f\x6e"},
++  {ASN1_ETYPE_OCTET_STRING, 36,
++   "\x30\x22\x80\x0F\x32\x30\x31\x31\x30\x38\x32\x31\x30\x38\x30\x30\x30\x36\x5A\x81\x0F\x32\x30\x31\x31\x30\x38\x32\x33\x32\x30\x35\x39\x35\x39\x5A",
++   38,
++   "\x04\x24\x30\x22\x80\x0F\x32\x30\x31\x31\x30\x38\x32\x31\x30\x38\x30\x30\x30\x36\x5A\x81\x0F\x32\x30\x31\x31\x30\x38\x32\x33\x32\x30\x35\x39\x35\x39\x5A"}
++};
++
++#define SSTR(x) sizeof(x)-1,x
++static const struct tv ber[] = {
++  {ASN1_ETYPE_OCTET_STRING,
++   SSTR("\xa0\xa0"),
++   SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x00\x00")},
++  {ASN1_ETYPE_OCTET_STRING,
++   SSTR("\xa0\xa0\xb0\xb0\xb0"),
++   SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x00\x00")},
++  {ASN1_ETYPE_OCTET_STRING,
++   SSTR("\xa0\xa0\xb0\xb0\xb0\xa1\xa1"),
++   SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x24\x80\x04\x82\x00\x02\xa1\xa1\x00\x00\x00\x00")},
++  {ASN1_ETYPE_OCTET_STRING,
++   SSTR("\xa0\xa0\xb0\xb0\xb0\xa1\xa1\xc1"),
++   SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x24\x80\x04\x82\x00\x02\xa1\xa1\x04\x82\x00\x01\xc1\x00\x00\x00\x00")},
++};
++
++void
++test_strings ()
++{
++  int ret;
++  unsigned char tl[ASN1_MAX_TL_SIZE];
++  unsigned int tl_len, der_len, str_len;
++  const unsigned char *str;
++  unsigned char *b;
++  unsigned int i;
++
++  /* Dummy test */
++
++  for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
++    {
++      /* Encode */
++      tl_len = sizeof (tl);
++      ret = asn1_encode_simple_der (tv[i].etype, tv[i].str, tv[i].str_len,
++				    tl, &tl_len);
++      if (ret != ASN1_SUCCESS)
++	{
++	  grub_fatal ("Encoding error in %u: %s\n", i,
++		   asn1_strerror (ret));
++	  return;
++	}
++      der_len = tl_len + tv[i].str_len;
++
++      if (der_len != tv[i].der_len || grub_memcmp (tl, tv[i].der, tl_len) != 0)
++	{
++	  grub_fatal (
++		   "DER encoding differs in %u! (size: %u, expected: %u)\n",
++		   i, der_len, tv[i].der_len);
++	  return;
++	}
++
++      /* decoding */
++      ret =
++	asn1_decode_simple_der (tv[i].etype, tv[i].der, tv[i].der_len, &str,
++				&str_len);
++      if (ret != ASN1_SUCCESS)
++	{
++	  grub_fatal ("Decoding error in %u: %s\n", i,
++		   asn1_strerror (ret));
++	  return;
++	}
++
++      if (str_len != tv[i].str_len || grub_memcmp (str, tv[i].str, str_len) != 0)
++	{
++	  grub_fatal (
++		   "DER decoded data differ in %u! (size: %u, expected: %u)\n",
++		   i, der_len, tv[i].str_len);
++	  return;
++	}
++    }
++
++  /* BER decoding */
++  for (i = 0; i < sizeof (ber) / sizeof (ber[0]); i++)
++    {
++      /* decoding */
++      ret =
++	asn1_decode_simple_ber (ber[i].etype, ber[i].der, ber[i].der_len, &b,
++				&str_len, NULL);
++      if (ret != ASN1_SUCCESS)
++	{
++	  grub_fatal ("BER decoding error in %u: %s\n", i,
++		   asn1_strerror (ret));
++	  return;
++	}
++
++      if (str_len != ber[i].str_len || grub_memcmp (b, ber[i].str, str_len) != 0)
++	{
++	  grub_fatal (
++		   "BER decoded data differ in %u! (size: %u, expected: %u)\n",
++		   i, str_len, ber[i].str_len);
++	  return;
++	}
++      grub_free(b);
++    }
++}
+diff --git a/grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c b/grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c
+new file mode 100644
+index 00000000000..d367bbfb5a7
+--- /dev/null
++++ b/grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c
+@@ -0,0 +1,116 @@
++/*
++ * Copyright (C) 2016 Red Hat, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include <grub/libtasn1.h>
++#include <grub/types.h>
++#include <grub/misc.h>
++#include <grub/err.h>
++#include "../wrap_tests.h"
++
++struct tv
++{
++  int der_len;
++  const unsigned char *der;
++  const char *oid;
++  int expected_error;
++};
++
++static const struct tv tv[] = {
++  {.der_len = 5,
++   .der = (void *) "\x06\x03\x80\x37\x03",
++   .oid = "2.999.3",
++   .expected_error = ASN1_DER_ERROR /* leading 0x80 */
++  },
++  {.der_len = 12,
++   .der = (void *) "\x06\x0a\x2b\x06\x01\x80\x01\x92\x08\x09\x05\x01",
++   .oid = "1.3.6.1.4.1.2312.9.5.1",
++   .expected_error = ASN1_DER_ERROR /* leading 0x80 */
++  },
++  {.der_len = 6,
++   .der = (void *) "\x06\x04\x01\x02\x03\x04",
++   .oid = "0.1.2.3.4",
++   .expected_error = ASN1_SUCCESS},
++  {.der_len = 5,
++   .der = (void *) "\x06\x03\x51\x02\x03",
++   .oid = "2.1.2.3",
++   .expected_error = ASN1_SUCCESS},
++  {.der_len = 5,
++   .der = (void *) "\x06\x03\x88\x37\x03",
++   .oid = "2.999.3",
++   .expected_error = ASN1_SUCCESS},
++  {.der_len = 12,
++   .der = (void *) "\x06\x0a\x2b\x06\x01\x04\x01\x92\x08\x09\x05\x01",
++   .oid = "1.3.6.1.4.1.2312.9.5.1",
++   .expected_error = ASN1_SUCCESS},
++  {.der_len = 19,
++   .der = (void *) "\x06\x11\xfa\x80\x00\x00\x00\x0e\x01\x0e\xfa\x80\x00\x00\x00\x0e\x63\x6f\x6d",
++   .oid = "2.1998768.0.0.14.1.14.1998848.0.0.14.99.111.109",
++   .expected_error = ASN1_SUCCESS},
++  {.der_len = 19,
++   .der =
++   (void *)
++   "\x06\x11\x2b\x06\x01\x04\x01\x92\x08\x09\x02\xaa\xda\xbe\xbe\xfa\x72\x01\x07",
++   .oid = "1.3.6.1.4.1.2312.9.2.1467399257458.1.7",
++   .expected_error = ASN1_SUCCESS},
++};
++
++void
++test_object_id_decoding (void)
++{
++  char str[128];
++  int ret, ret_len;
++  grub_size_t i;
++
++  for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
++    {
++      /* decode */
++      ret =
++	asn1_get_object_id_der (tv[i].der+1,
++				tv[i].der_len-1, &ret_len, str,
++				sizeof (str));
++      if (ret != tv[i].expected_error)
++	{
++	  grub_fatal (
++		   "%d: asn1_get_object_id_der iter %lu: got '%s' expected %d\n",
++		   __LINE__, (unsigned long) i, asn1_strerror(ret), tv[i].expected_error);
++	  return;
++	}
++
++      if (tv[i].expected_error != ASN1_SUCCESS)
++        continue;
++
++      if (ret_len != tv[i].der_len-1)
++	{
++	  grub_fatal (
++		   "%d: iter %lu: error in DER, length returned is %d, had %d\n",
++		   __LINE__, (unsigned long)i, ret_len, tv[i].der_len-1);
++	  return;
++	}
++
++      if (grub_strcmp (tv[i].oid, str) != 0)
++	{
++	  grub_fatal (
++		   "%d: strcmp iter %lu: got invalid OID: %s, expected: %s\n",
++		   __LINE__, (unsigned long) i, str, tv[i].oid);
++	  return;
++	}
++
++    }
++}
+diff --git a/grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c b/grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c
+new file mode 100644
+index 00000000000..3a83b58c59f
+--- /dev/null
++++ b/grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c
+@@ -0,0 +1,120 @@
++/*
++ * Copyright (C) 2019 Nikos Mavrogiannopoulos
++ *
++ * This file is part of LIBTASN1.
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include <grub/libtasn1.h>
++#include <grub/types.h>
++#include <grub/misc.h>
++#include <grub/err.h>
++#include "../wrap_tests.h"
++
++struct tv
++{
++  int der_len;
++  const unsigned char *der;
++  const char *oid;
++  int expected_error;
++};
++
++static const struct tv tv[] = {
++  {.der_len = 0,
++   .der = (void *) "",
++   .oid = "5.999.3",
++   .expected_error = ASN1_VALUE_NOT_VALID /* cannot start with 5 */
++  },
++  {.der_len = 0,
++   .der = (void *) "",
++   .oid = "0.48.9",
++   .expected_error = ASN1_VALUE_NOT_VALID /* second field cannot be 48 */
++  },
++  {.der_len = 0,
++   .der = (void *) "",
++   .oid = "1.40.9",
++   .expected_error = ASN1_VALUE_NOT_VALID /* second field cannot be 40 */
++  },
++  {.der_len = 4,
++   .der = (void *) "\x06\x02\x4f\x63",
++   .oid = "1.39.99",
++   .expected_error = ASN1_SUCCESS,
++  },
++  {.der_len = 6,
++   .der = (void *) "\x06\x04\x01\x02\x03\x04",
++   .oid = "0.1.2.3.4",
++   .expected_error = ASN1_SUCCESS},
++  {.der_len = 5,
++   .der = (void *) "\x06\x03\x51\x02\x03",
++   .oid = "2.1.2.3",
++   .expected_error = ASN1_SUCCESS},
++  {.der_len = 5,
++   .der = (void *) "\x06\x03\x88\x37\x03",
++   .oid = "2.999.3",
++   .expected_error = ASN1_SUCCESS},
++  {.der_len = 12,
++   .der = (void *) "\x06\x0a\x2b\x06\x01\x04\x01\x92\x08\x09\x05\x01",
++   .oid = "1.3.6.1.4.1.2312.9.5.1",
++   .expected_error = ASN1_SUCCESS},
++  {.der_len = 19,
++   .der = (void *) "\x06\x11\xfa\x80\x00\x00\x00\x0e\x01\x0e\xfa\x80\x00\x00\x00\x0e\x63\x6f\x6d",
++   .oid = "2.1998768.0.0.14.1.14.1998848.0.0.14.99.111.109",
++   .expected_error = ASN1_SUCCESS},
++  {.der_len = 19,
++   .der =
++   (void *)
++   "\x06\x11\x2b\x06\x01\x04\x01\x92\x08\x09\x02\xaa\xda\xbe\xbe\xfa\x72\x01\x07",
++   .oid = "1.3.6.1.4.1.2312.9.2.1467399257458.1.7",
++   .expected_error = ASN1_SUCCESS},
++};
++
++void
++test_object_id_encoding(void)
++{
++  unsigned char der[128];
++  int ret, der_len, i;
++
++  for (i = 0; i < (int)(sizeof (tv) / sizeof (tv[0])); i++)
++    {
++      der_len = sizeof(der);
++      ret = asn1_object_id_der(tv[i].oid, der, &der_len, 0);
++      if (ret != ASN1_SUCCESS)
++	{
++	  if (ret == tv[i].expected_error)
++	    continue;
++	  grub_fatal (
++		   "%d: iter %lu, encoding of OID failed: %s\n",
++		   __LINE__, (unsigned long) i, asn1_strerror(ret));
++	  return;
++	}
++      else if (ret != tv[i].expected_error)
++        {
++	  grub_fatal (
++		   "%d: iter %lu, encoding of OID %s succeeded when expecting failure\n",
++		   __LINE__, (unsigned long) i, tv[i].oid);
++          return;
++        }
++
++      if (der_len != tv[i].der_len || grub_memcmp(der, tv[i].der, der_len) != 0)
++	{
++	  grub_fatal (
++		   "%d: iter %lu, re-encoding of OID %s resulted to different string (%d vs %d bytes)\n",
++		   __LINE__, (unsigned long) i, tv[i].oid, der_len, tv[i].der_len);
++
++	  return;
++	}
++    }
++}
+diff --git a/grub-core/lib/libtasn1_wrap/tests/octet-string.c b/grub-core/lib/libtasn1_wrap/tests/octet-string.c
+new file mode 100644
+index 00000000000..d8a049e8df0
+--- /dev/null
++++ b/grub-core/lib/libtasn1_wrap/tests/octet-string.c
+@@ -0,0 +1,211 @@
++/*
++ * Copyright (C) 2011-2020 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ *
++ * Written by Simon Josefsson and Nikos Mavrogiannopoulos
++ *
++ */
++
++#include <grub/libtasn1.h>
++#include <grub/err.h>
++#include <grub/mm.h>
++#include <grub/misc.h>
++#include "../wrap_tests.h"
++
++
++struct tv
++{
++  const char *name;
++  int der_len;
++  const unsigned char *der_str;
++  int len;
++  const unsigned char *string;
++  int expected_error;
++  int ber;
++};
++
++static const struct tv tv[] = {
++  {.name = "primitive octet strings",
++   .der_len = 10,
++   .der_str =
++   (void*)"\x04\x08\x01\x23\x45\x67\x89\xab\xcd\xef",
++   .len = 8,
++   .string =
++   (void*)"\x01\x23\x45\x67\x89\xab\xcd\xef",
++   .ber = 0},
++  {.der_len = 22,
++   .der_str =
++   (void*)"\x04\x14\x13\x00\xd9\xa8\x47\xf7\xf2\x1c\xf4\xb0\xec\x5f\xc1\x73\xe5\x1b\x25\xc2\x62\x27",
++   .len = 20,
++   .string =
++   (void*)"\x13\x00\xD9\xA8\x47\xF7\xF2\x1C\xF4\xB0\xEC\x5F\xC1\x73\xE5\x1B\x25\xC2\x62\x27"},
++
++  {.name = "long type of length",
++   .der_len = 23,
++   .der_str =
++   (void*)"\x04\x81\x14\x13\x00\xd9\xa8\x47\xf7\xf2\x1c\xf4\xb0\xec\x5f\xc1\x73\xe5\x1b\x25\xc2\x62\x27",
++   .len = 20,
++   .string =
++   (void*)"\x13\x00\xD9\xA8\x47\xF7\xF2\x1C\xF4\xB0\xEC\x5F\xC1\x73\xE5\x1B\x25\xC2\x62\x27",
++   .ber = 1},
++  {.der_len = 11,
++   .der_str =
++   (void*)"\x04\x81\x08\x01\x23\x45\x67\x89\xab\xcd\xef",
++   .len = 8,
++   .string =
++   (void*)"\x01\x23\x45\x67\x89\xab\xcd\xef",
++   .ber = 1},
++
++  {.name = "constructed - indefinite",
++   .der_len = 11,
++   .der_str = (void*)"\x24\x80\x04\x05\x01\x02\x03\x04\x05\x00\x00",
++   .len = 5,
++   .string = (void*)"\x01\x02\x03\x04\x05",
++   .ber = 1,
++   },
++
++  {.name = "constructed - definite - concat",
++   .der_len = 12,
++   .der_str = (void*)"\x24\x0a\x04\x04\x0a\x0b\x0c\x0d\x04\x02\x0e\x0f",
++   .len = 6,
++   .string = (void*)"\x0a\x0b\x0c\x0d\x0e\x0f",
++   .ber = 1,
++   },
++  {.name = "constructed - definite - recursive",
++   .der_len = 15,
++   .der_str = (void*)"\x24\x0d\x04\x04\x0a\x0b\x0c\x0d\x24\x05\x04\x00\x04\x01\x0f",
++   .len = 5,
++   .string = (void*)"\x0a\x0b\x0c\x0d\x0f",
++   .ber = 1,
++   },
++  {.name = "constructed - definite - single",
++   .der_len = 7,
++   .der_str = (void*)"\x24\x05\x04\x03\x01\x02\x03",
++   .len = 3,
++   .string = (void*)"\x01\x02\x03",
++   .ber = 1,
++   },
++
++  /* a large amount of recursive indefinite encoding */
++  {.name = "a large amount of recursive indefinite encoding",
++   .der_len = 29325,
++   .der_str = (void*)"\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80",
++   .len = 0,
++   .ber = 1,
++   .expected_error = ASN1_DER_ERROR
++   }
++};
++
++void
++test_octet_string (void)
++{
++  unsigned char str[100];
++  unsigned char der[100];
++  int der_len = sizeof (der);
++  int str_size = sizeof (str);
++  unsigned char *tmp = NULL;
++  int ret, ret_len;
++  grub_size_t i;
++
++  for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
++    {
++      /* Decode */
++
++      if (tv[i].ber == 0)
++	{
++	  str_size = sizeof (str);
++	  ret =
++	    asn1_get_octet_der (tv[i].der_str + 1,
++				tv[i].der_len - 1, &ret_len, str,
++				sizeof (str), &str_size);
++	  if (ret != tv[i].expected_error)
++	    {
++	      grub_fatal (
++		       "%d: asn1_get_octet_der: %s: got %d expected %d\n",
++		       __LINE__, tv[i].name, ret,
++		       tv[i].expected_error);
++	      return;
++	    }
++	  if (tv[i].expected_error)
++	    continue;
++
++	  if (ret_len != tv[i].der_len - 1)
++	    {
++	      grub_fatal (
++		       "%d: error in DER, length returned is %d, had %d\n",
++		       __LINE__, ret_len, tv[i].der_len - 1);
++	      return;
++	    }
++
++	  if (str_size != tv[i].len
++	      || grub_memcmp (tv[i].string, str, tv[i].len) != 0)
++	    {
++	      grub_fatal (
++		       "%d: memcmp: %s: got invalid decoding\n",
++		       __LINE__, tv[i].name);
++
++              return;
++	    }
++
++	  /* Encode */
++	  der_len = sizeof (der);
++	  asn1_octet_der (str, str_size, der, &der_len);
++
++	  if (der_len != tv[i].der_len - 1
++	      || grub_memcmp (tv[i].der_str + 1, der, tv[i].der_len - 1) != 0)
++	    {
++	      grub_fatal (
++		       "encoding: %s: got invalid encoding\n",
++		       tv[i].name);
++	      return;
++	    }
++	}
++
++      ret =
++	asn1_decode_simple_ber (ASN1_ETYPE_OCTET_STRING,
++				tv[i].der_str, tv[i].der_len,
++				&tmp, (unsigned int*)&str_size, (unsigned int*)&der_len);
++      if (ret != tv[i].expected_error)
++	{
++	  grub_fatal (
++		   "%d: asn1_decode_simple_ber: %s: got %s expected %s\n",
++		   __LINE__, tv[i].name, asn1_strerror(ret), asn1_strerror(tv[i].expected_error));
++	  return;
++	}
++      if (tv[i].expected_error)
++        continue;
++
++      if (der_len != tv[i].der_len)
++	{
++	  grub_fatal (
++		   "%d: error: %s: DER, length returned is %d, had %d\n",
++		   __LINE__, tv[i].name, der_len, tv[i].der_len);
++	  return;
++	}
++
++      if (str_size != tv[i].len || grub_memcmp (tv[i].string, tmp, tv[i].len) != 0)
++	{
++	  grub_fatal (
++		   "%d: memcmp: %s: got invalid decoding\n",
++		   __LINE__, tv[i].name);
++          return;
++	}
++      grub_free (tmp);
++      tmp = NULL;
++
++    }
++}
+diff --git a/grub-core/lib/libtasn1_wrap/tests/reproducers.c b/grub-core/lib/libtasn1_wrap/tests/reproducers.c
+new file mode 100644
+index 00000000000..dc7268d4c6c
+--- /dev/null
++++ b/grub-core/lib/libtasn1_wrap/tests/reproducers.c
+@@ -0,0 +1,81 @@
++/*
++ * Copyright (C) 2019 Free Software Foundation, Inc.
++ *
++ * This file is part of LIBTASN1.
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++/****************************************************************/
++/* Description: run reproducers for several fixed issues        */
++/****************************************************************/
++
++#include <grub/libtasn1.h>
++#include <grub/err.h>
++#include <grub/mm.h>
++#include "../wrap_tests.h"
++
++#define CONST_DOWN        (1U<<29)
++
++/* produces endless loop (fixed by d4b624b2):
++ * The following translates into a single node with all pointers
++ * (right,left,down) set to NULL. */
++const asn1_static_node endless_asn1_tab[] = {
++  { "TEST_TREE", 536875024, NULL },
++  { NULL, 0, NULL }
++};
++
++/* produces memory leak (fixed by f16d1ff9):
++ * 152 bytes in 1 blocks are definitely lost in loss record 1 of 1
++ *    at 0x4837B65: calloc (vg_replace_malloc.c:762)
++ *    by 0x4851C0D: _asn1_add_static_node (parser_aux.c:71)
++ *    by 0x4853AAC: asn1_array2tree (structure.c:200)
++ *    by 0x10923B: main (single_node.c:67)
++ */
++const asn1_static_node tab[] = {
++{ "a", CONST_DOWN, "" },
++{ "b", 0, "" },
++{ "c", 0, "" },
++{ NULL, 0, NULL }
++};
++
++void
++test_reproducers (void)
++{
++  int result;
++  asn1_node definitions = NULL;
++  char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
++
++  result = asn1_array2tree (endless_asn1_tab, &definitions, errorDescription);
++  if (result != ASN1_SUCCESS)
++    {
++      grub_fatal ("Error: %s\nErrorDescription = %s\n\n",
++		  asn1_strerror (result), errorDescription);
++      return;
++    }
++
++  asn1_delete_structure (&definitions);
++
++  definitions = NULL;
++  result = asn1_array2tree (tab, &definitions, errorDescription);
++  if (result != ASN1_SUCCESS)
++    {
++      grub_fatal ("Error: %s\nErrorDescription = %s\n\n",
++		  asn1_strerror (result), errorDescription);
++      return;
++    }
++
++  asn1_delete_structure (&definitions);
++}
+diff --git a/grub-core/lib/libtasn1_wrap/wrap_tests.c b/grub-core/lib/libtasn1_wrap/wrap_tests.c
+new file mode 100644
+index 00000000000..75fcd21f0d5
+--- /dev/null
++++ b/grub-core/lib/libtasn1_wrap/wrap_tests.c
+@@ -0,0 +1,75 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2020 IBM Corporation
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/dl.h>
++#include <grub/command.h>
++#include <grub/mm.h>
++#include "wrap_tests.h"
++
++/*
++ * libtasn1 tests - from which this is derived - are provided under GPL3+.
++ */
++GRUB_MOD_LICENSE ("GPLv3+");
++
++static grub_command_t cmd;
++
++static grub_err_t
++grub_cmd_asn1test (grub_command_t cmdd __attribute__((unused)),
++		   int argc __attribute__((unused)),
++		   char **args __attribute__((unused)))
++{
++  grub_printf ("test_CVE_2018_1000654\n");
++  test_CVE_2018_1000654 ();
++
++  grub_printf ("test_object_id_decoding\n");
++  test_object_id_decoding ();
++
++  grub_printf ("test_object_id_encoding\n");
++  test_object_id_encoding ();
++
++  grub_printf ("test_octet_string\n");
++  test_octet_string ();
++
++  grub_printf ("test_overflow\n");
++  test_overflow ();
++
++  grub_printf ("test_reproducers\n");
++  test_overflow ();
++
++  grub_printf ("test_simple\n");
++  test_simple ();
++
++  grub_printf ("test_strings\n");
++  test_strings ();
++
++  grub_printf ("ASN.1 self-tests passed\n");
++
++  return GRUB_ERR_NONE;
++}
++
++
++GRUB_MOD_INIT(test_asn1)
++{
++  cmd = grub_register_command ("test_asn1", grub_cmd_asn1test, NULL,
++			       "Run self-tests for the ASN.1 parser.");
++}
++
++GRUB_MOD_FINI(test_asn1)
++{
++  grub_unregister_command (cmd);
++}
+diff --git a/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h
+new file mode 100644
+index 00000000000..1e7d3d64f55
+--- /dev/null
++++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h
+@@ -0,0 +1,32 @@
++#if HAVE_CONFIG_H
++# include "config.h"
++#endif
++
++#include <grub/libtasn1.h>
++
++const asn1_static_node CVE_2018_1000654_1_asn1_tab[] = {
++  { "TEST_TREE", 536875024, NULL },
++  { NULL, 1610612748, NULL },
++  { "iso", 1073741825, "1"},
++  { "identified-organization", 1073741825, "3"},
++  { "dod", 1073741825, "6"},
++  { "internet", 1073741825, "1"},
++  { "security", 1073741825, "5"},
++  { "mechanisms", 1073741825, "5"},
++  { "pkix", 1073741825, "7"},
++  { "id-mod", 1073741825, "0"},
++  { "id-pkix1-implicit-88", 1, "2"},
++  { "id-xnyTest", 1879048204, NULL },
++  { NULL, 1073741825, "id-ix"},
++  { NULL, 1073741825, "29"},
++  { NULL, 1, "1"},
++  { "id-ix", 1880096780, "OBJECR"},
++  { NULL, 1073741825, "id-ix"},
++  { NULL, 1073741825, "29"},
++  { NULL, 1, "2"},
++  { "id-xnyTest", 805306380, NULL },
++  { NULL, 1073741825, "id-ix"},
++  { NULL, 1073741825, "29"},
++  { NULL, 1, "1"},
++  { NULL, 0, NULL }
++};
+diff --git a/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h
+new file mode 100644
+index 00000000000..e2561e5ec6d
+--- /dev/null
++++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h
+@@ -0,0 +1,36 @@
++#if HAVE_CONFIG_H
++# include "config.h"
++#endif
++
++#include <grub/libtasn1.h>
++
++const asn1_static_node CVE_2018_1000654_2_asn1_tab[] = {
++  { "TEST_TREE", 536875024, NULL },
++  { NULL, 1610612748, NULL },
++  { "iso", 1073741825, "1"},
++  { "identified-organization", 1073741825, "3"},
++  { "dod", 1073741825, "6"},
++  { "internet", 1073741825, "1"},
++  { "security", 1073741825, "5"},
++  { "mechanisms", 1073741825, "5"},
++  { "pkix", 1073741825, "7"},
++  { "id-mod", 1073741825, "0"},
++  { "id-pkix1-implicit-88", 1, "2"},
++  { "id-oneTest", 1879048204, NULL },
++  { NULL, 1073741825, "id-two"},
++  { NULL, 1073741825, "9"},
++  { NULL, 1, "1"},
++  { "id-two", 1879048204, NULL },
++  { NULL, 1073741825, "id-three"},
++  { NULL, 1073741825, "2"},
++  { NULL, 1, "2"},
++  { "id-three", 1879048204, NULL },
++  { NULL, 1073741825, "id-four"},
++  { NULL, 1073741825, "3"},
++  { NULL, 1, "3"},
++  { "id-four", 805306380, NULL },
++  { NULL, 1073741825, "id-two"},
++  { NULL, 1073741825, "3"},
++  { NULL, 1, "3"},
++  { NULL, 0, NULL }
++};
+diff --git a/grub-core/lib/libtasn1_wrap/wrap_tests.h b/grub-core/lib/libtasn1_wrap/wrap_tests.h
+new file mode 100644
+index 00000000000..555e56dd202
+--- /dev/null
++++ b/grub-core/lib/libtasn1_wrap/wrap_tests.h
+@@ -0,0 +1,38 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2020 IBM Corporation
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef LIBTASN1_WRAP_TESTS_H
++#define LIBTASN1_WRAP_TESTS_H
++
++void test_CVE_2018_1000654 (void);
++
++void test_object_id_encoding (void);
++
++void test_object_id_decoding (void);
++
++void test_octet_string (void);
++
++void test_overflow (void);
++
++void test_reproducers (void);
++
++void test_simple (void);
++
++void test_strings (void);
++
++#endif
+diff --git a/.gitignore b/.gitignore
+index 594d0134d33..856e69bc5c1 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -264,6 +264,7 @@ widthspec.bin
+ /stamp-h1
+ /syslinux_test
+ /tar_test
++/test_asn1
+ /test_sha512sum
+ /test_unset
+ /tests/syslinux/ubuntu10.04_grub.cfg
+diff --git a/tests/test_asn1.in b/tests/test_asn1.in
+new file mode 100644
+index 00000000000..8173c5c270e
+--- /dev/null
++++ b/tests/test_asn1.in
+@@ -0,0 +1,12 @@
++#! @BUILD_SHEBANG@
++set -e
++
++. "@builddir@/grub-core/modinfo.sh"
++
++out=`echo test_asn1 | @builddir@/grub-shell`
++
++if [ "$(echo "$out" | tail -n 1)" != "ASN.1 self-tests passed" ]; then
++  echo "ASN.1 test failure: $out"
++  exit 1
++fi
++
diff --git a/SOURCES/0191-grub-install-support-embedding-x509-certificates.patch b/SOURCES/0191-grub-install-support-embedding-x509-certificates.patch
new file mode 100644
index 0000000..04722ad
--- /dev/null
+++ b/SOURCES/0191-grub-install-support-embedding-x509-certificates.patch
@@ -0,0 +1,252 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Alastair D'Silva <alastair@d-silva.org>
+Date: Mon, 6 Jul 2020 13:33:04 +1000
+Subject: [PATCH] grub-install: support embedding x509 certificates
+
+To support verification of appended signatures, we need a way to
+embed the necessary public keys. Existing appended signature schemes
+in the Linux kernel use X.509 certificates, so allow certificates to
+be embedded in the grub core image in the same way as PGP keys.
+
+Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/commands/pgp.c    |  2 +-
+ util/grub-install-common.c  | 22 +++++++++++++++++++++-
+ util/grub-mkimage.c         | 15 +++++++++++++--
+ util/mkimage.c              | 38 ++++++++++++++++++++++++++++++++++++--
+ include/grub/kernel.h       |  4 +++-
+ include/grub/util/install.h |  7 +++++--
+ 6 files changed, 79 insertions(+), 9 deletions(-)
+
+diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c
+index 355a43844ac..b81ac0ae46c 100644
+--- a/grub-core/commands/pgp.c
++++ b/grub-core/commands/pgp.c
+@@ -944,7 +944,7 @@ GRUB_MOD_INIT(pgp)
+     grub_memset (&pseudo_file, 0, sizeof (pseudo_file));
+ 
+     /* Not an ELF module, skip.  */
+-    if (header->type != OBJ_TYPE_PUBKEY)
++    if (header->type != OBJ_TYPE_GPG_PUBKEY)
+       continue;
+ 
+     pseudo_file.fs = &pseudo_fs;
+diff --git a/util/grub-install-common.c b/util/grub-install-common.c
+index aab2a941f85..422f82362c7 100644
+--- a/util/grub-install-common.c
++++ b/util/grub-install-common.c
+@@ -460,6 +460,8 @@ static char **pubkeys;
+ static size_t npubkeys;
+ static char *sbat;
+ static int disable_shim_lock;
++static char **x509keys;
++static size_t nx509keys;
+ static grub_compression_t compression;
+ static size_t appsig_size;
+ 
+@@ -500,6 +502,11 @@ grub_install_parse (int key, char *arg)
+       return 1;
+     case GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK:
+       disable_shim_lock = 1;
++    case 'x':
++      x509keys = xrealloc (x509keys,
++			  sizeof (x509keys[0])
++			  * (nx509keys + 1));
++      x509keys[nx509keys++] = xstrdup (arg);
+       return 1;
+ 
+     case GRUB_INSTALL_OPTIONS_VERBOSITY:
+@@ -627,6 +634,9 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
+   for (pk = pubkeys; pk < pubkeys + npubkeys; pk++)
+     slen += 20 + grub_strlen (*pk);
+ 
++  for (pk = x509keys; pk < x509keys + nx509keys; pk++)
++    slen += 10 + grub_strlen (*pk);
++
+   for (md = modules.entries; *md; md++)
+     {
+       slen += 10 + grub_strlen (*md);
+@@ -655,6 +665,14 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
+       *p++ = ' ';
+     }
+ 
++  for (pk = x509keys; pk < x509keys + nx509keys; pk++)
++    {
++      p = grub_stpcpy (p, "--x509 '");
++      p = grub_stpcpy (p, *pk);
++      *p++ = '\'';
++      *p++ = ' ';
++    }
++
+   for (md = modules.entries; *md; md++)
+     {
+       *p++ = '\'';
+@@ -688,7 +706,9 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
+ 
+   grub_install_generate_image (dir, prefix, fp, outname,
+ 			       modules.entries, memdisk_path,
+-			       pubkeys, npubkeys, config_path, tgt,
++			       pubkeys, npubkeys,
++			       x509keys, nx509keys,
++			       config_path, tgt,
+ 			       note, appsig_size, compression, dtb, sbat,
+ 			       disable_shim_lock);
+   while (dc--)
+diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c
+index 26d1ecbf74e..c5cdda498eb 100644
+--- a/util/grub-mkimage.c
++++ b/util/grub-mkimage.c
+@@ -75,7 +75,8 @@ static struct argp_option options[] = {
+    /* TRANSLATORS: "embed" is a verb (command description).  "*/
+   {"config",   'c', N_("FILE"), 0, N_("embed FILE as an early config"), 0},
+    /* TRANSLATORS: "embed" is a verb (command description).  "*/
+-  {"pubkey",   'k', N_("FILE"), 0, N_("embed FILE as public key for signature checking"), 0},
++  {"pubkey",   'k', N_("FILE"), 0, N_("embed FILE as public key for PGP signature checking"), 0},
++  {"x509",     'x', N_("FILE"), 0, N_("embed FILE as an x509 certificate for appended signature checking"), 0},
+   /* TRANSLATORS: NOTE is a name of segment.  */
+   {"note",   'n', 0, 0, N_("add NOTE segment for CHRP IEEE1275"), 0},
+   {"output",  'o', N_("FILE"), 0, N_("output a generated image to FILE [default=stdout]"), 0},
+@@ -124,6 +125,8 @@ struct arguments
+   char *dtb;
+   char **pubkeys;
+   size_t npubkeys;
++  char **x509keys;
++  size_t nx509keys;
+   char *font;
+   char *config;
+   char *sbat;
+@@ -206,6 +209,13 @@ argp_parser (int key, char *arg, struct argp_state *state)
+       arguments->pubkeys[arguments->npubkeys++] = xstrdup (arg);
+       break;
+ 
++    case 'x':
++      arguments->x509keys = xrealloc (arguments->x509keys,
++				      sizeof (arguments->x509keys[0])
++				      * (arguments->nx509keys + 1));
++      arguments->x509keys[arguments->nx509keys++] = xstrdup (arg);
++      break;
++
+     case 'c':
+       if (arguments->config)
+ 	free (arguments->config);
+@@ -332,7 +342,8 @@ main (int argc, char *argv[])
+   grub_install_generate_image (arguments.dir, arguments.prefix, fp,
+ 			       arguments.output, arguments.modules,
+ 			       arguments.memdisk, arguments.pubkeys,
+-			       arguments.npubkeys, arguments.config,
++			       arguments.npubkeys, arguments.x509keys,
++			       arguments.nx509keys, arguments.config,
+ 			       arguments.image_target, arguments.note,
+ 
+ 			       arguments.comp, arguments.appsig_size,
+diff --git a/util/mkimage.c b/util/mkimage.c
+index bab12276010..8319e8dfbde 100644
+--- a/util/mkimage.c
++++ b/util/mkimage.c
+@@ -867,7 +867,8 @@ void
+ grub_install_generate_image (const char *dir, const char *prefix,
+ 			     FILE *out, const char *outname, char *mods[],
+ 			     char *memdisk_path, char **pubkey_paths,
+-			     size_t npubkeys, char *config_path,
++			     size_t npubkeys, char **x509key_paths,
++			     size_t nx509keys, char *config_path,
+ 			     const struct grub_install_image_target_desc *image_target,
+ 			     int note, size_t appsig_size, grub_compression_t comp,
+ 			     const char *dtb_path, const char *sbat_path,
+@@ -913,6 +914,19 @@ grub_install_generate_image (const char *dir, const char *prefix,
+       }
+   }
+ 
++  {
++    size_t i;
++    for (i = 0; i < nx509keys; i++)
++      {
++	size_t curs;
++	curs = ALIGN_ADDR (grub_util_get_image_size (x509key_paths[i]));
++	grub_util_info ("the size of x509 public key %u is 0x%"
++			GRUB_HOST_PRIxLONG_LONG,
++			(unsigned) i, (unsigned long long) curs);
++	total_module_size += curs + sizeof (struct grub_module_header);
++      }
++  }
++
+   if (memdisk_path)
+     {
+       memdisk_size = ALIGN_UP(grub_util_get_image_size (memdisk_path), 512);
+@@ -1034,7 +1048,7 @@ grub_install_generate_image (const char *dir, const char *prefix,
+ 	curs = grub_util_get_image_size (pubkey_paths[i]);
+ 
+ 	header = (struct grub_module_header *) (kernel_img + offset);
+-	header->type = grub_host_to_target32 (OBJ_TYPE_PUBKEY);
++	header->type = grub_host_to_target32 (OBJ_TYPE_GPG_PUBKEY);
+ 	header->size = grub_host_to_target32 (curs + sizeof (*header));
+ 	offset += sizeof (*header);
+ 
+@@ -1043,6 +1057,26 @@ grub_install_generate_image (const char *dir, const char *prefix,
+       }
+   }
+ 
++  {
++    size_t i;
++    for (i = 0; i < nx509keys; i++)
++      {
++	size_t curs;
++	struct grub_module_header *header;
++
++	curs = grub_util_get_image_size (x509key_paths[i]);
++
++	header = (struct grub_module_header *) (kernel_img + offset);
++	header->type = grub_host_to_target32 (OBJ_TYPE_X509_PUBKEY);
++	header->size = grub_host_to_target32 (curs + sizeof (*header));
++	offset += sizeof (*header);
++
++	grub_util_load_image (x509key_paths[i], kernel_img + offset);
++	offset += ALIGN_ADDR (curs);
++      }
++  }
++
++
+   if (memdisk_path)
+     {
+       struct grub_module_header *header;
+diff --git a/include/grub/kernel.h b/include/grub/kernel.h
+index 55849777eaa..98edc0863f6 100644
+--- a/include/grub/kernel.h
++++ b/include/grub/kernel.h
+@@ -30,7 +30,9 @@ enum
+   OBJ_TYPE_PREFIX,
+   OBJ_TYPE_PUBKEY,
+   OBJ_TYPE_DTB,
+-  OBJ_TYPE_DISABLE_SHIM_LOCK
++  OBJ_TYPE_DISABLE_SHIM_LOCK,
++  OBJ_TYPE_GPG_PUBKEY,
++  OBJ_TYPE_X509_PUBKEY,
+ };
+ 
+ /* The module header.  */
+diff --git a/include/grub/util/install.h b/include/grub/util/install.h
+index cf4531e02b6..51f3b13ac13 100644
+--- a/include/grub/util/install.h
++++ b/include/grub/util/install.h
+@@ -67,6 +67,8 @@
+       N_("SBAT metadata"), 0 },						\
+   { "disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0,	\
+       N_("disable shim_lock verifier"), 0 },				\
++  { "x509key",   'x', N_("FILE"), 0,					\
++      N_("embed FILE as an x509 certificate for signature checking"), 0}, \
+   { "appended-signature-size", GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE,\
+     "SIZE", 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), \
+     1},                                                                 \
+@@ -188,8 +190,9 @@ void
+ grub_install_generate_image (const char *dir, const char *prefix,
+ 			     FILE *out,
+ 			     const char *outname, char *mods[],
+-			     char *memdisk_path, char **pubkey_paths,
+-			     size_t npubkeys,
++			     char *memdisk_path,
++			     char **pubkey_paths, size_t npubkeys,
++			     char **x509key_paths, size_t nx509keys,
+ 			     char *config_path,
+ 			     const struct grub_install_image_target_desc *image_target,
+ 			     int note, size_t appsig_size,
diff --git a/SOURCES/0192-appended-signatures-import-GNUTLS-s-ASN.1-descriptio.patch b/SOURCES/0192-appended-signatures-import-GNUTLS-s-ASN.1-descriptio.patch
new file mode 100644
index 0000000..7b7d70c
--- /dev/null
+++ b/SOURCES/0192-appended-signatures-import-GNUTLS-s-ASN.1-descriptio.patch
@@ -0,0 +1,639 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Thu, 30 Jul 2020 01:35:10 +1000
+Subject: [PATCH] appended signatures: import GNUTLS's ASN.1 description files
+
+In order to parse PKCS#7 messages and X.509 certificates with libtasn1,
+we need some information about how they are encoded.
+
+We get these from GNUTLS, which has the benefit that they support the
+features we need and are well tested.
+
+The GNUTLS license is LGPLv2.1+, which is GPLv3 compatible, allowing
+us to import it without issue.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/commands/appendedsig/gnutls_asn1_tab.c | 121 ++++++
+ grub-core/commands/appendedsig/pkix_asn1_tab.c   | 484 +++++++++++++++++++++++
+ 2 files changed, 605 insertions(+)
+ create mode 100644 grub-core/commands/appendedsig/gnutls_asn1_tab.c
+ create mode 100644 grub-core/commands/appendedsig/pkix_asn1_tab.c
+
+diff --git a/grub-core/commands/appendedsig/gnutls_asn1_tab.c b/grub-core/commands/appendedsig/gnutls_asn1_tab.c
+new file mode 100644
+index 00000000000..ddd1314e63b
+--- /dev/null
++++ b/grub-core/commands/appendedsig/gnutls_asn1_tab.c
+@@ -0,0 +1,121 @@
++#include <grub/mm.h>
++#include <grub/libtasn1.h>
++
++const asn1_static_node gnutls_asn1_tab[] = {
++  { "GNUTLS", 536872976, NULL },
++  { NULL, 1073741836, NULL },
++  { "RSAPublicKey", 1610612741, NULL },
++  { "modulus", 1073741827, NULL },
++  { "publicExponent", 3, NULL },
++  { "RSAPrivateKey", 1610612741, NULL },
++  { "version", 1073741827, NULL },
++  { "modulus", 1073741827, NULL },
++  { "publicExponent", 1073741827, NULL },
++  { "privateExponent", 1073741827, NULL },
++  { "prime1", 1073741827, NULL },
++  { "prime2", 1073741827, NULL },
++  { "exponent1", 1073741827, NULL },
++  { "exponent2", 1073741827, NULL },
++  { "coefficient", 1073741827, NULL },
++  { "otherPrimeInfos", 16386, "OtherPrimeInfos"},
++  { "ProvableSeed", 1610612741, NULL },
++  { "algorithm", 1073741836, NULL },
++  { "seed", 7, NULL },
++  { "OtherPrimeInfos", 1612709899, NULL },
++  { "MAX", 1074266122, "1"},
++  { NULL, 2, "OtherPrimeInfo"},
++  { "OtherPrimeInfo", 1610612741, NULL },
++  { "prime", 1073741827, NULL },
++  { "exponent", 1073741827, NULL },
++  { "coefficient", 3, NULL },
++  { "AlgorithmIdentifier", 1610612741, NULL },
++  { "algorithm", 1073741836, NULL },
++  { "parameters", 541081613, NULL },
++  { "algorithm", 1, NULL },
++  { "DigestInfo", 1610612741, NULL },
++  { "digestAlgorithm", 1073741826, "DigestAlgorithmIdentifier"},
++  { "digest", 7, NULL },
++  { "DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"},
++  { "DSAPublicKey", 1073741827, NULL },
++  { "DSAParameters", 1610612741, NULL },
++  { "p", 1073741827, NULL },
++  { "q", 1073741827, NULL },
++  { "g", 3, NULL },
++  { "DSASignatureValue", 1610612741, NULL },
++  { "r", 1073741827, NULL },
++  { "s", 3, NULL },
++  { "DSAPrivateKey", 1610612741, NULL },
++  { "version", 1073741827, NULL },
++  { "p", 1073741827, NULL },
++  { "q", 1073741827, NULL },
++  { "g", 1073741827, NULL },
++  { "Y", 1073741827, NULL },
++  { "priv", 3, NULL },
++  { "DHParameter", 1610612741, NULL },
++  { "prime", 1073741827, NULL },
++  { "base", 1073741827, NULL },
++  { "privateValueLength", 16387, NULL },
++  { "ECParameters", 1610612754, NULL },
++  { "namedCurve", 12, NULL },
++  { "ECPrivateKey", 1610612741, NULL },
++  { "Version", 1073741827, NULL },
++  { "privateKey", 1073741831, NULL },
++  { "parameters", 1610637314, "ECParameters"},
++  { NULL, 2056, "0"},
++  { "publicKey", 536895494, NULL },
++  { NULL, 2056, "1"},
++  { "PrincipalName", 1610612741, NULL },
++  { "name-type", 1610620931, NULL },
++  { NULL, 2056, "0"},
++  { "name-string", 536879115, NULL },
++  { NULL, 1073743880, "1"},
++  { NULL, 27, NULL },
++  { "KRB5PrincipalName", 1610612741, NULL },
++  { "realm", 1610620955, NULL },
++  { NULL, 2056, "0"},
++  { "principalName", 536879106, "PrincipalName"},
++  { NULL, 2056, "1"},
++  { "RSAPSSParameters", 1610612741, NULL },
++  { "hashAlgorithm", 1610637314, "AlgorithmIdentifier"},
++  { NULL, 2056, "0"},
++  { "maskGenAlgorithm", 1610637314, "AlgorithmIdentifier"},
++  { NULL, 2056, "1"},
++  { "saltLength", 1610653699, NULL },
++  { NULL, 1073741833, "20"},
++  { NULL, 2056, "2"},
++  { "trailerField", 536911875, NULL },
++  { NULL, 1073741833, "1"},
++  { NULL, 2056, "3"},
++  { "GOSTParameters", 1610612741, NULL },
++  { "publicKeyParamSet", 1073741836, NULL },
++  { "digestParamSet", 16396, NULL },
++  { "GOSTParametersOld", 1610612741, NULL },
++  { "publicKeyParamSet", 1073741836, NULL },
++  { "digestParamSet", 1073741836, NULL },
++  { "encryptionParamSet", 16396, NULL },
++  { "GOSTPrivateKey", 1073741831, NULL },
++  { "GOSTPrivateKeyOld", 1073741827, NULL },
++  { "IssuerSignTool", 1610612741, NULL },
++  { "signTool", 1073741858, NULL },
++  { "cATool", 1073741858, NULL },
++  { "signToolCert", 1073741858, NULL },
++  { "cAToolCert", 34, NULL },
++  { "Gost28147-89-EncryptedKey", 1610612741, NULL },
++  { "encryptedKey", 1073741831, NULL },
++  { "maskKey", 1610637319, NULL },
++  { NULL, 4104, "0"},
++  { "macKey", 7, NULL },
++  { "SubjectPublicKeyInfo", 1610612741, NULL },
++  { "algorithm", 1073741826, "AlgorithmIdentifier"},
++  { "subjectPublicKey", 6, NULL },
++  { "GostR3410-TransportParameters", 1610612741, NULL },
++  { "encryptionParamSet", 1073741836, NULL },
++  { "ephemeralPublicKey", 1610637314, "SubjectPublicKeyInfo"},
++  { NULL, 4104, "0"},
++  { "ukm", 7, NULL },
++  { "GostR3410-KeyTransport", 536870917, NULL },
++  { "sessionEncryptedKey", 1073741826, "Gost28147-89-EncryptedKey"},
++  { "transportParameters", 536895490, "GostR3410-TransportParameters"},
++  { NULL, 4104, "0"},
++  { NULL, 0, NULL }
++};
+diff --git a/grub-core/commands/appendedsig/pkix_asn1_tab.c b/grub-core/commands/appendedsig/pkix_asn1_tab.c
+new file mode 100644
+index 00000000000..adef69d95ce
+--- /dev/null
++++ b/grub-core/commands/appendedsig/pkix_asn1_tab.c
+@@ -0,0 +1,484 @@
++#include <grub/mm.h>
++#include <grub/libtasn1.h>
++
++const asn1_static_node pkix_asn1_tab[] = {
++  { "PKIX1", 536875024, NULL },
++  { NULL, 1073741836, NULL },
++  { "PrivateKeyUsagePeriod", 1610612741, NULL },
++  { "notBefore", 1610637349, NULL },
++  { NULL, 4104, "0"},
++  { "notAfter", 536895525, NULL },
++  { NULL, 4104, "1"},
++  { "AuthorityKeyIdentifier", 1610612741, NULL },
++  { "keyIdentifier", 1610637319, NULL },
++  { NULL, 4104, "0"},
++  { "authorityCertIssuer", 1610637314, "GeneralNames"},
++  { NULL, 4104, "1"},
++  { "authorityCertSerialNumber", 536895490, "CertificateSerialNumber"},
++  { NULL, 4104, "2"},
++  { "SubjectKeyIdentifier", 1073741831, NULL },
++  { "KeyUsage", 1073741830, NULL },
++  { "DirectoryString", 1610612754, NULL },
++  { "teletexString", 1612709918, NULL },
++  { "MAX", 524298, "1"},
++  { "printableString", 1612709919, NULL },
++  { "MAX", 524298, "1"},
++  { "universalString", 1612709920, NULL },
++  { "MAX", 524298, "1"},
++  { "utf8String", 1612709922, NULL },
++  { "MAX", 524298, "1"},
++  { "bmpString", 1612709921, NULL },
++  { "MAX", 524298, "1"},
++  { "ia5String", 538968093, NULL },
++  { "MAX", 524298, "1"},
++  { "SubjectAltName", 1073741826, "GeneralNames"},
++  { "GeneralNames", 1612709899, NULL },
++  { "MAX", 1074266122, "1"},
++  { NULL, 2, "GeneralName"},
++  { "GeneralName", 1610612754, NULL },
++  { "otherName", 1610620930, "AnotherName"},
++  { NULL, 4104, "0"},
++  { "rfc822Name", 1610620957, NULL },
++  { NULL, 4104, "1"},
++  { "dNSName", 1610620957, NULL },
++  { NULL, 4104, "2"},
++  { "x400Address", 1610620941, NULL },
++  { NULL, 4104, "3"},
++  { "directoryName", 1610620939, NULL },
++  { NULL, 1073743880, "4"},
++  { NULL, 2, "RelativeDistinguishedName"},
++  { "ediPartyName", 1610620941, NULL },
++  { NULL, 4104, "5"},
++  { "uniformResourceIdentifier", 1610620957, NULL },
++  { NULL, 4104, "6"},
++  { "iPAddress", 1610620935, NULL },
++  { NULL, 4104, "7"},
++  { "registeredID", 536879116, NULL },
++  { NULL, 4104, "8"},
++  { "AnotherName", 1610612741, NULL },
++  { "type-id", 1073741836, NULL },
++  { "value", 541073421, NULL },
++  { NULL, 1073743880, "0"},
++  { "type-id", 1, NULL },
++  { "IssuerAltName", 1073741826, "GeneralNames"},
++  { "BasicConstraints", 1610612741, NULL },
++  { "cA", 1610645508, NULL },
++  { NULL, 131081, NULL },
++  { "pathLenConstraint", 537411587, NULL },
++  { "0", 10, "MAX"},
++  { "CRLDistributionPoints", 1612709899, NULL },
++  { "MAX", 1074266122, "1"},
++  { NULL, 2, "DistributionPoint"},
++  { "DistributionPoint", 1610612741, NULL },
++  { "distributionPoint", 1610637314, "DistributionPointName"},
++  { NULL, 2056, "0"},
++  { "reasons", 1610637314, "ReasonFlags"},
++  { NULL, 4104, "1"},
++  { "cRLIssuer", 536895490, "GeneralNames"},
++  { NULL, 4104, "2"},
++  { "DistributionPointName", 1610612754, NULL },
++  { "fullName", 1610620930, "GeneralNames"},
++  { NULL, 4104, "0"},
++  { "nameRelativeToCRLIssuer", 536879106, "RelativeDistinguishedName"},
++  { NULL, 4104, "1"},
++  { "ReasonFlags", 1073741830, NULL },
++  { "ExtKeyUsageSyntax", 1612709899, NULL },
++  { "MAX", 1074266122, "1"},
++  { NULL, 12, NULL },
++  { "AuthorityInfoAccessSyntax", 1612709899, NULL },
++  { "MAX", 1074266122, "1"},
++  { NULL, 2, "AccessDescription"},
++  { "AccessDescription", 1610612741, NULL },
++  { "accessMethod", 1073741836, NULL },
++  { "accessLocation", 2, "GeneralName"},
++  { "Attribute", 1610612741, NULL },
++  { "type", 1073741836, NULL },
++  { "values", 536870927, NULL },
++  { NULL, 13, NULL },
++  { "AttributeTypeAndValue", 1610612741, NULL },
++  { "type", 1073741836, NULL },
++  { "value", 13, NULL },
++  { "Name", 1610612754, NULL },
++  { "rdnSequence", 536870923, NULL },
++  { NULL, 2, "RelativeDistinguishedName"},
++  { "DistinguishedName", 1610612747, NULL },
++  { NULL, 2, "RelativeDistinguishedName"},
++  { "RelativeDistinguishedName", 1612709903, NULL },
++  { "MAX", 1074266122, "1"},
++  { NULL, 2, "AttributeTypeAndValue"},
++  { "Certificate", 1610612741, NULL },
++  { "tbsCertificate", 1073741826, "TBSCertificate"},
++  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
++  { "signature", 6, NULL },
++  { "TBSCertificate", 1610612741, NULL },
++  { "version", 1610653699, NULL },
++  { NULL, 1073741833, "0"},
++  { NULL, 2056, "0"},
++  { "serialNumber", 1073741826, "CertificateSerialNumber"},
++  { "signature", 1073741826, "AlgorithmIdentifier"},
++  { "issuer", 1073741826, "Name"},
++  { "validity", 1073741826, "Validity"},
++  { "subject", 1073741826, "Name"},
++  { "subjectPublicKeyInfo", 1073741826, "SubjectPublicKeyInfo"},
++  { "issuerUniqueID", 1610637314, "UniqueIdentifier"},
++  { NULL, 4104, "1"},
++  { "subjectUniqueID", 1610637314, "UniqueIdentifier"},
++  { NULL, 4104, "2"},
++  { "extensions", 536895490, "Extensions"},
++  { NULL, 2056, "3"},
++  { "CertificateSerialNumber", 1073741827, NULL },
++  { "Validity", 1610612741, NULL },
++  { "notBefore", 1073741826, "Time"},
++  { "notAfter", 2, "Time"},
++  { "Time", 1610612754, NULL },
++  { "utcTime", 1073741860, NULL },
++  { "generalTime", 37, NULL },
++  { "UniqueIdentifier", 1073741830, NULL },
++  { "SubjectPublicKeyInfo", 1610612741, NULL },
++  { "algorithm", 1073741826, "AlgorithmIdentifier"},
++  { "subjectPublicKey", 6, NULL },
++  { "Extensions", 1612709899, NULL },
++  { "MAX", 1074266122, "1"},
++  { NULL, 2, "Extension"},
++  { "Extension", 1610612741, NULL },
++  { "extnID", 1073741836, NULL },
++  { "critical", 1610645508, NULL },
++  { NULL, 131081, NULL },
++  { "extnValue", 7, NULL },
++  { "CertificateList", 1610612741, NULL },
++  { "tbsCertList", 1073741826, "TBSCertList"},
++  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
++  { "signature", 6, NULL },
++  { "TBSCertList", 1610612741, NULL },
++  { "version", 1073758211, NULL },
++  { "signature", 1073741826, "AlgorithmIdentifier"},
++  { "issuer", 1073741826, "Name"},
++  { "thisUpdate", 1073741826, "Time"},
++  { "nextUpdate", 1073758210, "Time"},
++  { "revokedCertificates", 1610629131, NULL },
++  { NULL, 536870917, NULL },
++  { "userCertificate", 1073741826, "CertificateSerialNumber"},
++  { "revocationDate", 1073741826, "Time"},
++  { "crlEntryExtensions", 16386, "Extensions"},
++  { "crlExtensions", 536895490, "Extensions"},
++  { NULL, 2056, "0"},
++  { "AlgorithmIdentifier", 1610612741, NULL },
++  { "algorithm", 1073741836, NULL },
++  { "parameters", 541081613, NULL },
++  { "algorithm", 1, NULL },
++  { "Dss-Sig-Value", 1610612741, NULL },
++  { "r", 1073741827, NULL },
++  { "s", 3, NULL },
++  { "Dss-Parms", 1610612741, NULL },
++  { "p", 1073741827, NULL },
++  { "q", 1073741827, NULL },
++  { "g", 3, NULL },
++  { "pkcs-7-ContentInfo", 1610612741, NULL },
++  { "contentType", 1073741836, NULL },
++  { "content", 541073421, NULL },
++  { NULL, 1073743880, "0"},
++  { "contentType", 1, NULL },
++  { "pkcs-7-DigestInfo", 1610612741, NULL },
++  { "digestAlgorithm", 1073741826, "AlgorithmIdentifier"},
++  { "digest", 7, NULL },
++  { "pkcs-7-SignedData", 1610612741, NULL },
++  { "version", 1073741827, NULL },
++  { "digestAlgorithms", 1073741826, "pkcs-7-DigestAlgorithmIdentifiers"},
++  { "encapContentInfo", 1073741826, "pkcs-7-EncapsulatedContentInfo"},
++  { "certificates", 1610637314, "pkcs-7-CertificateSet"},
++  { NULL, 4104, "0"},
++  { "crls", 1610637314, "pkcs-7-CertificateRevocationLists"},
++  { NULL, 4104, "1"},
++  { "signerInfos", 2, "pkcs-7-SignerInfos"},
++  { "pkcs-7-DigestAlgorithmIdentifiers", 1610612751, NULL },
++  { NULL, 2, "AlgorithmIdentifier"},
++  { "pkcs-7-EncapsulatedContentInfo", 1610612741, NULL },
++  { "eContentType", 1073741836, NULL },
++  { "eContent", 536895501, NULL },
++  { NULL, 2056, "0"},
++  { "pkcs-7-CertificateRevocationLists", 1610612751, NULL },
++  { NULL, 13, NULL },
++  { "pkcs-7-CertificateChoices", 1610612754, NULL },
++  { "certificate", 13, NULL },
++  { "pkcs-7-CertificateSet", 1610612751, NULL },
++  { NULL, 2, "pkcs-7-CertificateChoices"},
++  { "IssuerAndSerialNumber", 1610612741, NULL },
++  { "issuer", 1073741826, "Name"},
++  { "serialNumber", 2, "CertificateSerialNumber"},
++  { "pkcs-7-SignerInfo", 1610612741, NULL },
++  { "version", 1073741827, NULL },
++  { "sid", 1073741826, "SignerIdentifier"},
++  { "digestAlgorithm", 1073741826, "AlgorithmIdentifier"},
++  { "signedAttrs", 1610637314, "SignedAttributes"},
++  { NULL, 4104, "0"},
++  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
++  { "signature", 1073741831, NULL },
++  { "unsignedAttrs", 536895490, "SignedAttributes"},
++  { NULL, 4104, "1"},
++  { "SignedAttributes", 1612709903, NULL },
++  { "MAX", 1074266122, "1"},
++  { NULL, 2, "Attribute"},
++  { "SignerIdentifier", 1610612754, NULL },
++  { "issuerAndSerialNumber", 1073741826, "IssuerAndSerialNumber"},
++  { "subjectKeyIdentifier", 536879111, NULL },
++  { NULL, 4104, "0"},
++  { "pkcs-7-SignerInfos", 1610612751, NULL },
++  { NULL, 2, "pkcs-7-SignerInfo"},
++  { "pkcs-10-CertificationRequestInfo", 1610612741, NULL },
++  { "version", 1073741827, NULL },
++  { "subject", 1073741826, "Name"},
++  { "subjectPKInfo", 1073741826, "SubjectPublicKeyInfo"},
++  { "attributes", 536879106, "Attributes"},
++  { NULL, 4104, "0"},
++  { "Attributes", 1610612751, NULL },
++  { NULL, 2, "Attribute"},
++  { "pkcs-10-CertificationRequest", 1610612741, NULL },
++  { "certificationRequestInfo", 1073741826, "pkcs-10-CertificationRequestInfo"},
++  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
++  { "signature", 6, NULL },
++  { "pkcs-9-at-challengePassword", 1879048204, NULL },
++  { "iso", 1073741825, "1"},
++  { "member-body", 1073741825, "2"},
++  { "us", 1073741825, "840"},
++  { "rsadsi", 1073741825, "113549"},
++  { "pkcs", 1073741825, "1"},
++  { NULL, 1073741825, "9"},
++  { NULL, 1, "7"},
++  { "pkcs-9-challengePassword", 1610612754, NULL },
++  { "printableString", 1073741855, NULL },
++  { "utf8String", 34, NULL },
++  { "pkcs-9-localKeyId", 1073741831, NULL },
++  { "pkcs-8-PrivateKeyInfo", 1610612741, NULL },
++  { "version", 1073741827, NULL },
++  { "privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"},
++  { "privateKey", 1073741831, NULL },
++  { "attributes", 536895490, "Attributes"},
++  { NULL, 4104, "0"},
++  { "pkcs-8-EncryptedPrivateKeyInfo", 1610612741, NULL },
++  { "encryptionAlgorithm", 1073741826, "AlgorithmIdentifier"},
++  { "encryptedData", 2, "pkcs-8-EncryptedData"},
++  { "pkcs-8-EncryptedData", 1073741831, NULL },
++  { "pkcs-5-des-CBC-params", 1612709895, NULL },
++  { NULL, 1048586, "8"},
++  { "pkcs-5-des-EDE3-CBC-params", 1612709895, NULL },
++  { NULL, 1048586, "8"},
++  { "pkcs-5-aes128-CBC-params", 1612709895, NULL },
++  { NULL, 1048586, "16"},
++  { "pkcs-5-aes192-CBC-params", 1612709895, NULL },
++  { NULL, 1048586, "16"},
++  { "pkcs-5-aes256-CBC-params", 1612709895, NULL },
++  { NULL, 1048586, "16"},
++  { "Gost28147-89-Parameters", 1610612741, NULL },
++  { "iv", 1073741831, NULL },
++  { "encryptionParamSet", 12, NULL },
++  { "pkcs-5-PBE-params", 1610612741, NULL },
++  { "salt", 1073741831, NULL },
++  { "iterationCount", 3, NULL },
++  { "pkcs-5-PBES2-params", 1610612741, NULL },
++  { "keyDerivationFunc", 1073741826, "AlgorithmIdentifier"},
++  { "encryptionScheme", 2, "AlgorithmIdentifier"},
++  { "pkcs-5-PBKDF2-params", 1610612741, NULL },
++  { "salt", 1610612754, NULL },
++  { "specified", 1073741831, NULL },
++  { "otherSource", 2, "AlgorithmIdentifier"},
++  { "iterationCount", 1611137027, NULL },
++  { "1", 10, "MAX"},
++  { "keyLength", 1611153411, NULL },
++  { "1", 10, "MAX"},
++  { "prf", 16386, "AlgorithmIdentifier"},
++  { "pkcs-12-PFX", 1610612741, NULL },
++  { "version", 1610874883, NULL },
++  { "v3", 1, "3"},
++  { "authSafe", 1073741826, "pkcs-7-ContentInfo"},
++  { "macData", 16386, "pkcs-12-MacData"},
++  { "pkcs-12-PbeParams", 1610612741, NULL },
++  { "salt", 1073741831, NULL },
++  { "iterations", 3, NULL },
++  { "pkcs-12-MacData", 1610612741, NULL },
++  { "mac", 1073741826, "pkcs-7-DigestInfo"},
++  { "macSalt", 1073741831, NULL },
++  { "iterations", 536903683, NULL },
++  { NULL, 9, "1"},
++  { "pkcs-12-AuthenticatedSafe", 1610612747, NULL },
++  { NULL, 2, "pkcs-7-ContentInfo"},
++  { "pkcs-12-SafeContents", 1610612747, NULL },
++  { NULL, 2, "pkcs-12-SafeBag"},
++  { "pkcs-12-SafeBag", 1610612741, NULL },
++  { "bagId", 1073741836, NULL },
++  { "bagValue", 1614815245, NULL },
++  { NULL, 1073743880, "0"},
++  { "badId", 1, NULL },
++  { "bagAttributes", 536887311, NULL },
++  { NULL, 2, "Attribute"},
++  { "pkcs-12-CertBag", 1610612741, NULL },
++  { "certId", 1073741836, NULL },
++  { "certValue", 541073421, NULL },
++  { NULL, 1073743880, "0"},
++  { "certId", 1, NULL },
++  { "pkcs-12-CRLBag", 1610612741, NULL },
++  { "crlId", 1073741836, NULL },
++  { "crlValue", 541073421, NULL },
++  { NULL, 1073743880, "0"},
++  { "crlId", 1, NULL },
++  { "pkcs-12-SecretBag", 1610612741, NULL },
++  { "secretTypeId", 1073741836, NULL },
++  { "secretValue", 541073421, NULL },
++  { NULL, 1073743880, "0"},
++  { "secretTypeId", 1, NULL },
++  { "pkcs-7-Data", 1073741831, NULL },
++  { "pkcs-7-EncryptedData", 1610612741, NULL },
++  { "version", 1073741827, NULL },
++  { "encryptedContentInfo", 1073741826, "pkcs-7-EncryptedContentInfo"},
++  { "unprotectedAttrs", 536895490, "pkcs-7-UnprotectedAttributes"},
++  { NULL, 4104, "1"},
++  { "pkcs-7-EncryptedContentInfo", 1610612741, NULL },
++  { "contentType", 1073741836, NULL },
++  { "contentEncryptionAlgorithm", 1073741826, "pkcs-7-ContentEncryptionAlgorithmIdentifier"},
++  { "encryptedContent", 536895495, NULL },
++  { NULL, 4104, "0"},
++  { "pkcs-7-ContentEncryptionAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"},
++  { "pkcs-7-UnprotectedAttributes", 1612709903, NULL },
++  { "MAX", 1074266122, "1"},
++  { NULL, 2, "Attribute"},
++  { "ProxyCertInfo", 1610612741, NULL },
++  { "pCPathLenConstraint", 1611153411, NULL },
++  { "0", 10, "MAX"},
++  { "proxyPolicy", 2, "ProxyPolicy"},
++  { "ProxyPolicy", 1610612741, NULL },
++  { "policyLanguage", 1073741836, NULL },
++  { "policy", 16391, NULL },
++  { "certificatePolicies", 1612709899, NULL },
++  { "MAX", 1074266122, "1"},
++  { NULL, 2, "PolicyInformation"},
++  { "PolicyInformation", 1610612741, NULL },
++  { "policyIdentifier", 1073741836, NULL },
++  { "policyQualifiers", 538984459, NULL },
++  { "MAX", 1074266122, "1"},
++  { NULL, 2, "PolicyQualifierInfo"},
++  { "PolicyQualifierInfo", 1610612741, NULL },
++  { "policyQualifierId", 1073741836, NULL },
++  { "qualifier", 541065229, NULL },
++  { "policyQualifierId", 1, NULL },
++  { "CPSuri", 1073741853, NULL },
++  { "UserNotice", 1610612741, NULL },
++  { "noticeRef", 1073758210, "NoticeReference"},
++  { "explicitText", 16386, "DisplayText"},
++  { "NoticeReference", 1610612741, NULL },
++  { "organization", 1073741826, "DisplayText"},
++  { "noticeNumbers", 536870923, NULL },
++  { NULL, 3, NULL },
++  { "DisplayText", 1610612754, NULL },
++  { "ia5String", 1612709917, NULL },
++  { "200", 524298, "1"},
++  { "visibleString", 1612709923, NULL },
++  { "200", 524298, "1"},
++  { "bmpString", 1612709921, NULL },
++  { "200", 524298, "1"},
++  { "utf8String", 538968098, NULL },
++  { "200", 524298, "1"},
++  { "OCSPRequest", 1610612741, NULL },
++  { "tbsRequest", 1073741826, "TBSRequest"},
++  { "optionalSignature", 536895490, "Signature"},
++  { NULL, 2056, "0"},
++  { "TBSRequest", 1610612741, NULL },
++  { "version", 1610653699, NULL },
++  { NULL, 1073741833, "0"},
++  { NULL, 2056, "0"},
++  { "requestorName", 1610637314, "GeneralName"},
++  { NULL, 2056, "1"},
++  { "requestList", 1610612747, NULL },
++  { NULL, 2, "Request"},
++  { "requestExtensions", 536895490, "Extensions"},
++  { NULL, 2056, "2"},
++  { "Signature", 1610612741, NULL },
++  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
++  { "signature", 1073741830, NULL },
++  { "certs", 536895499, NULL },
++  { NULL, 1073743880, "0"},
++  { NULL, 2, "Certificate"},
++  { "Request", 1610612741, NULL },
++  { "reqCert", 1073741826, "CertID"},
++  { "singleRequestExtensions", 536895490, "Extensions"},
++  { NULL, 2056, "0"},
++  { "CertID", 1610612741, NULL },
++  { "hashAlgorithm", 1073741826, "AlgorithmIdentifier"},
++  { "issuerNameHash", 1073741831, NULL },
++  { "issuerKeyHash", 1073741831, NULL },
++  { "serialNumber", 2, "CertificateSerialNumber"},
++  { "OCSPResponse", 1610612741, NULL },
++  { "responseStatus", 1073741826, "OCSPResponseStatus"},
++  { "responseBytes", 536895490, "ResponseBytes"},
++  { NULL, 2056, "0"},
++  { "OCSPResponseStatus", 1610874901, NULL },
++  { "successful", 1073741825, "0"},
++  { "malformedRequest", 1073741825, "1"},
++  { "internalError", 1073741825, "2"},
++  { "tryLater", 1073741825, "3"},
++  { "sigRequired", 1073741825, "5"},
++  { "unauthorized", 1, "6"},
++  { "ResponseBytes", 1610612741, NULL },
++  { "responseType", 1073741836, NULL },
++  { "response", 7, NULL },
++  { "BasicOCSPResponse", 1610612741, NULL },
++  { "tbsResponseData", 1073741826, "ResponseData"},
++  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
++  { "signature", 1073741830, NULL },
++  { "certs", 536895499, NULL },
++  { NULL, 1073743880, "0"},
++  { NULL, 2, "Certificate"},
++  { "ResponseData", 1610612741, NULL },
++  { "version", 1610653699, NULL },
++  { NULL, 1073741833, "0"},
++  { NULL, 2056, "0"},
++  { "responderID", 1073741826, "ResponderID"},
++  { "producedAt", 1073741861, NULL },
++  { "responses", 1610612747, NULL },
++  { NULL, 2, "SingleResponse"},
++  { "responseExtensions", 536895490, "Extensions"},
++  { NULL, 2056, "1"},
++  { "ResponderID", 1610612754, NULL },
++  { "byName", 1610620939, NULL },
++  { NULL, 1073743880, "1"},
++  { NULL, 2, "RelativeDistinguishedName"},
++  { "byKey", 536879111, NULL },
++  { NULL, 2056, "2"},
++  { "SingleResponse", 1610612741, NULL },
++  { "certID", 1073741826, "CertID"},
++  { "certStatus", 1073741826, "CertStatus"},
++  { "thisUpdate", 1073741861, NULL },
++  { "nextUpdate", 1610637349, NULL },
++  { NULL, 2056, "0"},
++  { "singleExtensions", 536895490, "Extensions"},
++  { NULL, 2056, "1"},
++  { "CertStatus", 1610612754, NULL },
++  { "good", 1610620948, NULL },
++  { NULL, 4104, "0"},
++  { "revoked", 1610620930, "RevokedInfo"},
++  { NULL, 4104, "1"},
++  { "unknown", 536879106, "UnknownInfo"},
++  { NULL, 4104, "2"},
++  { "RevokedInfo", 1610612741, NULL },
++  { "revocationTime", 1073741861, NULL },
++  { "revocationReason", 537157653, NULL },
++  { NULL, 1073743880, "0"},
++  { "unspecified", 1, "0"},
++  { "UnknownInfo", 1073741844, NULL },
++  { "NameConstraints", 1610612741, NULL },
++  { "permittedSubtrees", 1610637314, "GeneralSubtrees"},
++  { NULL, 4104, "0"},
++  { "excludedSubtrees", 536895490, "GeneralSubtrees"},
++  { NULL, 4104, "1"},
++  { "GeneralSubtrees", 1612709899, NULL },
++  { "MAX", 1074266122, "1"},
++  { NULL, 2, "GeneralSubtree"},
++  { "GeneralSubtree", 1610612741, NULL },
++  { "base", 1073741826, "GeneralName"},
++  { "minimum", 1610653699, NULL },
++  { NULL, 1073741833, "0"},
++  { NULL, 4104, "0"},
++  { "maximum", 536895491, NULL },
++  { NULL, 4104, "1"},
++  { "TlsFeatures", 536870923, NULL },
++  { NULL, 3, NULL },
++  { NULL, 0, NULL }
++};
diff --git a/SOURCES/0193-appended-signatures-parse-PKCS-7-signedData-and-X.50.patch b/SOURCES/0193-appended-signatures-parse-PKCS-7-signedData-and-X.50.patch
new file mode 100644
index 0000000..04bf1df
--- /dev/null
+++ b/SOURCES/0193-appended-signatures-parse-PKCS-7-signedData-and-X.50.patch
@@ -0,0 +1,1528 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Thu, 30 Jul 2020 01:33:46 +1000
+Subject: [PATCH] appended signatures: parse PKCS#7 signedData and X.509
+ certificates
+
+This code allows us to parse:
+
+ - PKCS#7 signedData messages. Only a single signerInfo is supported,
+   which is all that the Linux sign-file utility supports creating
+   out-of-the-box. Only RSA, SHA-256 and SHA-512 are supported.
+   Any certificate embedded in the PKCS#7 message will be ignored.
+
+ - X.509 certificates: at least enough to verify the signatures on the
+   PKCS#7 messages. We expect that the certificates embedded in grub will
+   be leaf certificates, not CA certificates. The parser enforces this.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/commands/appendedsig/asn1util.c    | 102 +++
+ grub-core/commands/appendedsig/pkcs7.c       | 305 +++++++++
+ grub-core/commands/appendedsig/x509.c        | 958 +++++++++++++++++++++++++++
+ grub-core/commands/appendedsig/appendedsig.h | 110 +++
+ 4 files changed, 1475 insertions(+)
+ create mode 100644 grub-core/commands/appendedsig/asn1util.c
+ create mode 100644 grub-core/commands/appendedsig/pkcs7.c
+ create mode 100644 grub-core/commands/appendedsig/x509.c
+ create mode 100644 grub-core/commands/appendedsig/appendedsig.h
+
+diff --git a/grub-core/commands/appendedsig/asn1util.c b/grub-core/commands/appendedsig/asn1util.c
+new file mode 100644
+index 00000000000..eff095a9df2
+--- /dev/null
++++ b/grub-core/commands/appendedsig/asn1util.c
+@@ -0,0 +1,102 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2020  IBM Corporation.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/libtasn1.h>
++#include <grub/types.h>
++#include <grub/err.h>
++#include <grub/mm.h>
++#include <grub/crypto.h>
++#include <grub/gcrypt/gcrypt.h>
++
++#include "appendedsig.h"
++
++asn1_node _gnutls_gnutls_asn = ASN1_TYPE_EMPTY;
++asn1_node _gnutls_pkix_asn = ASN1_TYPE_EMPTY;
++
++extern const ASN1_ARRAY_TYPE gnutls_asn1_tab[];
++extern const ASN1_ARRAY_TYPE pkix_asn1_tab[];
++
++/*
++ * Read a value from an ASN1 node, allocating memory to store it.
++ *
++ * It will work for anything where the size libtasn1 returns is right:
++ *  - Integers
++ *  - Octet strings
++ *  - DER encoding of other structures
++ * It will _not_ work for things where libtasn1 size requires adjustment:
++ *  - Strings that require an extra NULL byte at the end
++ *  - Bit strings because libtasn1 returns the length in bits, not bytes.
++ *
++ * If the function returns a non-NULL value, the caller must free it.
++ */
++void *
++grub_asn1_allocate_and_read (asn1_node node, const char *name,
++			     const char *friendly_name, int *content_size)
++{
++  int result;
++  grub_uint8_t *tmpstr = NULL;
++  int tmpstr_size = 0;
++
++  result = asn1_read_value (node, name, NULL, &tmpstr_size);
++  if (result != ASN1_MEM_ERROR)
++    {
++      grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
++		     _
++		     ("Reading size of %s did not return expected status: %s"),
++		     friendly_name, asn1_strerror (result));
++      grub_errno = GRUB_ERR_BAD_FILE_TYPE;
++      return NULL;
++    }
++
++  tmpstr = grub_malloc (tmpstr_size);
++  if (tmpstr == NULL)
++    {
++      grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
++		     "Could not allocate memory to store %s", friendly_name);
++      grub_errno = GRUB_ERR_OUT_OF_MEMORY;
++      return NULL;
++    }
++
++  result = asn1_read_value (node, name, tmpstr, &tmpstr_size);
++  if (result != ASN1_SUCCESS)
++    {
++      grub_free (tmpstr);
++      grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
++		     "Error reading %s: %s",
++		     friendly_name, asn1_strerror (result));
++      grub_errno = GRUB_ERR_BAD_FILE_TYPE;
++      return NULL;
++    }
++
++  *content_size = tmpstr_size;
++
++  return tmpstr;
++}
++
++int
++asn1_init (void)
++{
++  int res;
++  res = asn1_array2tree (gnutls_asn1_tab, &_gnutls_gnutls_asn, NULL);
++  if (res != ASN1_SUCCESS)
++    {
++      return res;
++    }
++  res = asn1_array2tree (pkix_asn1_tab, &_gnutls_pkix_asn, NULL);
++  return res;
++}
+diff --git a/grub-core/commands/appendedsig/pkcs7.c b/grub-core/commands/appendedsig/pkcs7.c
+new file mode 100644
+index 00000000000..dc6afe203f7
+--- /dev/null
++++ b/grub-core/commands/appendedsig/pkcs7.c
+@@ -0,0 +1,305 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2020  IBM Corporation.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include "appendedsig.h"
++#include <grub/misc.h>
++#include <grub/crypto.h>
++#include <grub/gcrypt/gcrypt.h>
++
++
++static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
++
++/*
++ * RFC 5652 s 5.1
++ */
++const char *signedData_oid = "1.2.840.113549.1.7.2";
++
++/*
++ * RFC 4055 s 2.1
++ */
++const char *sha256_oid = "2.16.840.1.101.3.4.2.1";
++const char *sha512_oid = "2.16.840.1.101.3.4.2.3";
++
++static grub_err_t
++process_content (grub_uint8_t * content, int size,
++		 struct pkcs7_signedData *msg)
++{
++  int res;
++  asn1_node signed_part;
++  grub_err_t err = GRUB_ERR_NONE;
++  char algo_oid[MAX_OID_LEN];
++  int algo_oid_size = sizeof (algo_oid);
++  int algo_count;
++  char version;
++  int version_size = sizeof (version);
++  grub_uint8_t *result_buf;
++  int result_size = 0;
++  int crls_size = 0;
++  gcry_error_t gcry_err;
++
++  res = asn1_create_element (_gnutls_pkix_asn, "PKIX1.pkcs-7-SignedData",
++			     &signed_part);
++  if (res != ASN1_SUCCESS)
++    {
++      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++			 "Could not create ASN.1 structure for PKCS#7 signed part.");
++    }
++
++  res = asn1_der_decoding2 (&signed_part, content, &size,
++			    ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
++  if (res != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_SIGNATURE,
++		    "Error reading PKCS#7 signed data: %s", asn1_error);
++      goto cleanup_signed_part;
++    }
++
++  /* SignedData ::= SEQUENCE {
++   *     version CMSVersion,
++   *     digestAlgorithms DigestAlgorithmIdentifiers,
++   *     encapContentInfo EncapsulatedContentInfo,
++   *     certificates [0] IMPLICIT CertificateSet OPTIONAL,
++   *     crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
++   *     signerInfos SignerInfos }
++   */
++
++  /* version per the algo in 5.1, must be 1 */
++  res = asn1_read_value (signed_part, "version", &version, &version_size);
++  if (res != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_SIGNATURE,
++		    "Error reading signedData version: %s",
++		    asn1_strerror (res));
++      goto cleanup_signed_part;
++    }
++
++  if (version != 1)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_SIGNATURE,
++		    "Unexpected signature version v%d, only v1 supported",
++		    version);
++      goto cleanup_signed_part;
++    }
++
++  /*
++   * digestAlgorithms DigestAlgorithmIdentifiers
++   *
++   * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
++   * DigestAlgorithmIdentifer is an X.509 AlgorithmIdentifier (10.1.1)
++   * 
++   * RFC 4055 s 2.1:
++   * sha256Identifier  AlgorithmIdentifier  ::=  { id-sha256, NULL }
++   * sha512Identifier  AlgorithmIdentifier  ::=  { id-sha512, NULL }
++   *
++   * We only support 1 element in the set, and we do not check parameters atm.
++   */
++  res =
++    asn1_number_of_elements (signed_part, "digestAlgorithms", &algo_count);
++  if (res != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_SIGNATURE,
++		    "Error counting number of digest algorithms: %s",
++		    asn1_strerror (res));
++      goto cleanup_signed_part;
++    }
++
++  if (algo_count != 1)
++    {
++      err =
++	grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
++		    "Only 1 digest algorithm is supported");
++      goto cleanup_signed_part;
++    }
++
++  res =
++    asn1_read_value (signed_part, "digestAlgorithms.?1.algorithm", algo_oid,
++		     &algo_oid_size);
++  if (res != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_SIGNATURE,
++		    "Error reading digest algorithm: %s",
++		    asn1_strerror (res));
++      goto cleanup_signed_part;
++    }
++
++  if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0)
++    {
++      msg->hash = grub_crypto_lookup_md_by_name ("sha512");
++    }
++  else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0)
++    {
++      msg->hash = grub_crypto_lookup_md_by_name ("sha256");
++    }
++  else
++    {
++      err =
++	grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
++		    "Only SHA-256 and SHA-512 hashes are supported, found OID %s",
++		    algo_oid);
++      goto cleanup_signed_part;
++    }
++
++  if (!msg->hash)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_SIGNATURE,
++		    "Hash algorithm for OID %s not loaded", algo_oid);
++      goto cleanup_signed_part;
++    }
++
++  /*
++   * We ignore the certificates, but we don't permit CRLs.
++   * A CRL entry might be revoking the certificate we're using, and we have
++   * no way of dealing with that at the moment.
++   */
++  res = asn1_read_value (signed_part, "crls", NULL, &crls_size);
++  if (res != ASN1_ELEMENT_NOT_FOUND)
++    {
++      err =
++	grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
++		    "PKCS#7 messages with embedded CRLs are not supported");
++      goto cleanup_signed_part;
++    }
++
++  /* read the signature */
++  result_buf =
++    grub_asn1_allocate_and_read (signed_part, "signerInfos.?1.signature",
++				 "signature data", &result_size);
++  if (!result_buf)
++    {
++      err = grub_errno;
++      goto cleanup_signed_part;
++    }
++
++  gcry_err =
++    gcry_mpi_scan (&(msg->sig_mpi), GCRYMPI_FMT_USG, result_buf, result_size,
++		   NULL);
++  if (gcry_err != GPG_ERR_NO_ERROR)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_SIGNATURE,
++		    "Error loading signature into MPI structure: %d",
++		    gcry_err);
++      goto cleanup_result;
++    }
++
++cleanup_result:
++  grub_free (result_buf);
++cleanup_signed_part:
++  asn1_delete_structure (&signed_part);
++
++  return err;
++}
++
++grub_err_t
++parse_pkcs7_signedData (void *sigbuf, grub_size_t data_size,
++			struct pkcs7_signedData *msg)
++{
++  int res;
++  asn1_node content_info;
++  grub_err_t err = GRUB_ERR_NONE;
++  char content_oid[MAX_OID_LEN];
++  grub_uint8_t *content;
++  int content_size;
++  int content_oid_size = sizeof (content_oid);
++  int size;
++
++  if (data_size > GRUB_INT_MAX)
++    return grub_error (GRUB_ERR_OUT_OF_RANGE,
++		       "Cannot parse a PKCS#7 message where data size > INT_MAX");
++  size = (int) data_size;
++
++  res = asn1_create_element (_gnutls_pkix_asn,
++			     "PKIX1.pkcs-7-ContentInfo", &content_info);
++  if (res != ASN1_SUCCESS)
++    {
++      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++			 "Could not create ASN.1 structure for PKCS#7 data: %s",
++			 asn1_strerror (res));
++    }
++
++  res = asn1_der_decoding2 (&content_info, sigbuf, &size,
++			    ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
++  if (res != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_SIGNATURE,
++		    "Error decoding PKCS#7 message DER: %s", asn1_error);
++      goto cleanup;
++    }
++
++  /*
++   * ContentInfo ::= SEQUENCE {
++   *     contentType ContentType,
++   *     content [0] EXPLICIT ANY DEFINED BY contentType }
++   *
++   * ContentType ::= OBJECT IDENTIFIER
++   */
++  res =
++    asn1_read_value (content_info, "contentType", content_oid,
++		     &content_oid_size);
++  if (res != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_SIGNATURE,
++		    "Error reading PKCS#7 content type: %s",
++		    asn1_strerror (res));
++      goto cleanup;
++    }
++
++  /* OID for SignedData defined in 5.1 */
++  if (grub_strncmp (signedData_oid, content_oid, content_oid_size) != 0)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_SIGNATURE,
++		    "Unexpected content type in PKCS#7 message: OID %s",
++		    content_oid);
++      goto cleanup;
++    }
++
++  content =
++    grub_asn1_allocate_and_read (content_info, "content",
++				 "PKCS#7 message content", &content_size);
++  if (!content)
++    {
++      err = grub_errno;
++      goto cleanup;
++    }
++
++  err = process_content (content, content_size, msg);
++  grub_free (content);
++
++cleanup:
++  asn1_delete_structure (&content_info);
++  return err;
++}
++
++/*
++ * Release all the storage associated with the PKCS#7 message.
++ * If the caller dynamically allocated the message, it must free it.
++ */
++void
++pkcs7_signedData_release (struct pkcs7_signedData *msg)
++{
++  gcry_mpi_release (msg->sig_mpi);
++}
+diff --git a/grub-core/commands/appendedsig/x509.c b/grub-core/commands/appendedsig/x509.c
+new file mode 100644
+index 00000000000..2b38b3670a2
+--- /dev/null
++++ b/grub-core/commands/appendedsig/x509.c
+@@ -0,0 +1,958 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2020  IBM Corporation.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/libtasn1.h>
++#include <grub/types.h>
++#include <grub/err.h>
++#include <grub/mm.h>
++#include <grub/crypto.h>
++#include <grub/gcrypt/gcrypt.h>
++
++#include "appendedsig.h"
++
++static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
++
++/*
++ * RFC 3279 2.3.1  RSA Keys
++ */
++const char *rsaEncryption_oid = "1.2.840.113549.1.1.1";
++
++/*
++ * RFC 5280 Appendix A
++ */
++const char *commonName_oid = "2.5.4.3";
++
++/*
++ * RFC 5280 4.2.1.3 Key Usage
++ */
++const char *keyUsage_oid = "2.5.29.15";
++
++/*
++ * RFC 5280 4.2.1.9 Basic Constraints
++ */
++const char *basicConstraints_oid = "2.5.29.19";
++
++/*
++ * RFC 3279 2.3.1
++ *
++ *  The RSA public key MUST be encoded using the ASN.1 type RSAPublicKey:
++ *
++ *     RSAPublicKey ::= SEQUENCE {
++ *        modulus            INTEGER,    -- n
++ *        publicExponent     INTEGER  }  -- e
++ *
++ *  where modulus is the modulus n, and publicExponent is the public
++ *  exponent e.
++ */
++static grub_err_t
++grub_parse_rsa_pubkey (grub_uint8_t * der, int dersize,
++		       struct x509_certificate *certificate)
++{
++  int result;
++  asn1_node spk = ASN1_TYPE_EMPTY;
++  grub_uint8_t *m_data, *e_data;
++  int m_size, e_size;
++  grub_err_t err = GRUB_ERR_NONE;
++  gcry_error_t gcry_err;
++
++  result =
++    asn1_create_element (_gnutls_gnutls_asn, "GNUTLS.RSAPublicKey", &spk);
++  if (result != ASN1_SUCCESS)
++    {
++      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++			 "Cannot create storage for public key ASN.1 data");
++    }
++
++  result = asn1_der_decoding2 (&spk, der, &dersize,
++			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
++  if (result != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		    "Cannot decode certificate public key DER: %s",
++		    asn1_error);
++      goto cleanup;
++    }
++
++  m_data =
++    grub_asn1_allocate_and_read (spk, "modulus", "RSA modulus", &m_size);
++  if (!m_data)
++    {
++      err = grub_errno;
++      goto cleanup;
++    }
++
++  e_data =
++    grub_asn1_allocate_and_read (spk, "publicExponent", "RSA public exponent",
++				 &e_size);
++  if (!e_data)
++    {
++      err = grub_errno;
++      goto cleanup_m_data;
++    }
++
++  /*
++   * convert m, e to mpi
++   *
++   * nscanned is not set for FMT_USG, it's only set for FMT_PGP, 
++   * so we can't verify it
++   */
++  gcry_err =
++    gcry_mpi_scan (&certificate->mpis[0], GCRYMPI_FMT_USG, m_data, m_size,
++		   NULL);
++  if (gcry_err != GPG_ERR_NO_ERROR)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		    "Error loading RSA modulus into MPI structure: %d",
++		    gcry_err);
++      goto cleanup_e_data;
++    }
++
++  gcry_err =
++    gcry_mpi_scan (&certificate->mpis[1], GCRYMPI_FMT_USG, e_data, e_size,
++		   NULL);
++  if (gcry_err != GPG_ERR_NO_ERROR)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		    "Error loading RSA exponent into MPI structure: %d",
++		    gcry_err);
++      goto cleanup_m_mpi;
++    }
++
++  grub_free (e_data);
++  grub_free (m_data);
++  asn1_delete_structure (&spk);
++  return GRUB_ERR_NONE;
++
++cleanup_m_mpi:
++  gcry_mpi_release (certificate->mpis[0]);
++cleanup_e_data:
++  grub_free (e_data);
++cleanup_m_data:
++  grub_free (m_data);
++cleanup:
++  asn1_delete_structure (&spk);
++  return err;
++}
++
++
++/*
++ * RFC 5280:
++ *   SubjectPublicKeyInfo  ::=  SEQUENCE  {
++ *       algorithm            AlgorithmIdentifier,
++ *       subjectPublicKey     BIT STRING  }
++ *
++ * AlgorithmIdentifiers come from RFC 3279, we are not strictly compilant as we
++ * only support RSA Encryption.
++ */
++
++static grub_err_t
++grub_x509_read_subject_public_key (asn1_node asn,
++				   struct x509_certificate *results)
++{
++  int result;
++  grub_err_t err;
++  const char *algo_name =
++    "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm";
++  const char *params_name =
++    "tbsCertificate.subjectPublicKeyInfo.algorithm.parameters";
++  const char *pk_name =
++    "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey";
++  char algo_oid[MAX_OID_LEN];
++  int algo_size = sizeof (algo_oid);
++  char params_value[2];
++  int params_size = sizeof (params_value);
++  grub_uint8_t *key_data = NULL;
++  int key_size = 0;
++  unsigned int key_type;
++
++  /* algorithm: see notes for rsaEncryption_oid */
++  result = asn1_read_value (asn, algo_name, algo_oid, &algo_size);
++  if (result != ASN1_SUCCESS)
++    {
++      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			 "Error reading x509 public key algorithm: %s",
++			 asn1_strerror (result));
++    }
++
++  if (grub_strncmp (algo_oid, rsaEncryption_oid, sizeof (rsaEncryption_oid))
++      != 0)
++    {
++      return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
++			 "Unsupported x509 public key algorithm: %s",
++			 algo_oid);
++    }
++
++  /* 
++   * RFC 3279 2.3.1
++   * The rsaEncryption OID is intended to be used in the algorithm field
++   * of a value of type AlgorithmIdentifier.  The parameters field MUST
++   * have ASN.1 type NULL for this algorithm identifier.
++   */
++  result = asn1_read_value (asn, params_name, params_value, &params_size);
++  if (result != ASN1_SUCCESS)
++    {
++      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			 "Error reading x509 public key parameters: %s",
++			 asn1_strerror (result));
++    }
++
++  if (params_value[0] != ASN1_TAG_NULL)
++    {
++      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			 "Invalid x509 public key parameters: expected NULL");
++    }
++
++  /*
++   * RFC 3279 2.3.1:  The DER encoded RSAPublicKey is the value of the BIT
++   * STRING subjectPublicKey.
++   */
++  result = asn1_read_value_type (asn, pk_name, NULL, &key_size, &key_type);
++  if (result != ASN1_MEM_ERROR)
++    {
++      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			 "Error reading size of x509 public key: %s",
++			 asn1_strerror (result));
++    }
++  if (key_type != ASN1_ETYPE_BIT_STRING)
++    {
++      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			 "Unexpected ASN.1 type when reading x509 public key: %x",
++			 key_type);
++    }
++
++  /* length is in bits */
++  key_size = (key_size + 7) / 8;
++
++  key_data = grub_malloc (key_size);
++  if (!key_data)
++    {
++      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++			 "Out of memory for x509 public key");
++    }
++
++  result = asn1_read_value (asn, pk_name, key_data, &key_size);
++  if (result != ASN1_SUCCESS)
++    {
++      grub_free (key_data);
++      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			 "Error reading public key data");
++    }
++  key_size = (key_size + 7) / 8;
++
++  err = grub_parse_rsa_pubkey (key_data, key_size, results);
++  grub_free (key_data);
++
++  return err;
++}
++
++/* Decode a string as defined in Appendix A */
++static grub_err_t
++decode_string (char *der, int der_size, char **string,
++	       grub_size_t * string_size)
++{
++  asn1_node strasn;
++  int result;
++  char *choice;
++  int choice_size = 0;
++  int tmp_size = 0;
++  grub_err_t err = GRUB_ERR_NONE;
++
++  result =
++    asn1_create_element (_gnutls_pkix_asn, "PKIX1.DirectoryString", &strasn);
++  if (result != ASN1_SUCCESS)
++    {
++      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++			 "Could not create ASN.1 structure for certificate: %s",
++			 asn1_strerror (result));
++    }
++
++  result = asn1_der_decoding2 (&strasn, der, &der_size,
++			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
++  if (result != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		    "Could not parse DER for DirectoryString: %s",
++		    asn1_error);
++      goto cleanup;
++    }
++
++  choice =
++    grub_asn1_allocate_and_read (strasn, "", "DirectoryString choice",
++				 &choice_size);
++  if (!choice)
++    {
++      err = grub_errno;
++      goto cleanup;
++    }
++
++  if (grub_strncmp ("utf8String", choice, choice_size))
++    {
++      err =
++	grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
++		    "Only UTF-8 DirectoryStrings are supported, got %s",
++		    choice);
++      goto cleanup_choice;
++    }
++
++  result = asn1_read_value (strasn, "utf8String", NULL, &tmp_size);
++  if (result != ASN1_MEM_ERROR)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		    "Error reading size of UTF-8 string: %s",
++		    asn1_strerror (result));
++      goto cleanup_choice;
++    }
++
++  /* read size does not include trailing null */
++  tmp_size++;
++
++  *string = grub_malloc (tmp_size);
++  if (!*string)
++    {
++      err =
++	grub_error (GRUB_ERR_OUT_OF_MEMORY,
++		    "Cannot allocate memory for DirectoryString contents");
++      goto cleanup_choice;
++    }
++
++  result = asn1_read_value (strasn, "utf8String", *string, &tmp_size);
++  if (result != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		    "Error reading out UTF-8 string in DirectoryString: %s",
++		    asn1_strerror (result));
++      grub_free (*string);
++      goto cleanup_choice;
++    }
++  *string_size = tmp_size + 1;
++  (*string)[tmp_size] = '\0';
++
++cleanup_choice:
++  grub_free (choice);
++cleanup:
++  asn1_delete_structure (&strasn);
++  return err;
++}
++
++/*
++ * TBSCertificate  ::=  SEQUENCE  {
++ *       version         [0]  EXPLICIT Version DEFAULT v1,
++ * ...
++ * 
++ * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
++ */
++static grub_err_t
++check_version (asn1_node certificate)
++{
++  int rc;
++  const char *name = "tbsCertificate.version";
++  grub_uint8_t version;
++  int len = 1;
++
++  rc = asn1_read_value (certificate, name, &version, &len);
++
++  /* require version 3 */
++  if (rc != ASN1_SUCCESS || len != 1)
++    return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		       "Error reading certificate version");
++
++  if (version != 0x02)
++    return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		       "Invalid x509 certificate version, expected v3 (0x02), got 0x%02x",
++		       version);
++
++  return GRUB_ERR_NONE;
++}
++
++/*
++ * This is an X.501 Name, which is complex.
++ *
++ * For simplicity, we extract only the CN.
++ */
++static grub_err_t
++read_name (asn1_node asn, const char *name_path, char **name,
++	   grub_size_t * name_size)
++{
++  int seq_components, set_components;
++  int result;
++  int i, j;
++  char *top_path, *set_path, *type_path, *val_path;
++  char type[MAX_OID_LEN];
++  int type_len = sizeof (type);
++  int string_size = 0;
++  char *string_der;
++  grub_err_t err;
++
++  *name = NULL;
++
++  top_path = grub_xasprintf ("%s.rdnSequence", name_path);
++  if (!top_path)
++    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++		       "Could not allocate memory for %s name parsing path",
++		       name_path);
++
++  result = asn1_number_of_elements (asn, top_path, &seq_components);
++  if (result != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		    "Error counting name components: %s",
++		    asn1_strerror (result));
++      goto cleanup;
++    }
++
++  for (i = 1; i <= seq_components; i++)
++    {
++      set_path = grub_xasprintf ("%s.?%d", top_path, i);
++      if (!set_path)
++	{
++	  err =
++	    grub_error (GRUB_ERR_OUT_OF_MEMORY,
++			"Could not allocate memory for %s name set parsing path",
++			name_path);
++	  goto cleanup_set;
++	}
++      /* this brings us, hopefully, to a set */
++      result = asn1_number_of_elements (asn, set_path, &set_components);
++      if (result != ASN1_SUCCESS)
++	{
++	  err =
++	    grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			"Error counting name sub-components components (element %d): %s",
++			i, asn1_strerror (result));
++	  goto cleanup_set;
++	}
++      for (j = 1; j <= set_components; j++)
++	{
++	  type_path = grub_xasprintf ("%s.?%d.?%d.type", top_path, i, j);
++	  if (!type_path)
++	    {
++	      err =
++		grub_error (GRUB_ERR_OUT_OF_MEMORY,
++			    "Could not allocate memory for %s name component type path",
++			    name_path);
++	      goto cleanup_set;
++	    }
++	  type_len = sizeof (type);
++	  result = asn1_read_value (asn, type_path, type, &type_len);
++	  if (result != ASN1_SUCCESS)
++	    {
++	      err =
++		grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			    "Error reading %s name component type: %s",
++			    name_path, asn1_strerror (result));
++	      goto cleanup_type;
++	    }
++
++	  if (grub_strncmp (type, commonName_oid, type_len) != 0)
++	    {
++	      grub_free (type_path);
++	      continue;
++	    }
++
++	  val_path = grub_xasprintf ("%s.?%d.?%d.value", top_path, i, j);
++	  if (!val_path)
++	    {
++	      err =
++		grub_error (GRUB_ERR_OUT_OF_MEMORY,
++			    "Could not allocate memory for %s name component value path",
++			    name_path);
++	      goto cleanup_set;
++	    }
++
++	  string_der =
++	    grub_asn1_allocate_and_read (asn, val_path, name_path,
++					 &string_size);
++	  if (!string_der)
++	    {
++	      err = grub_errno;
++	      goto cleanup_val_path;
++	    }
++
++	  err = decode_string (string_der, string_size, name, name_size);
++	  if (err)
++	    goto cleanup_string;
++
++	  grub_free (string_der);
++	  grub_free (type_path);
++	  grub_free (val_path);
++	  break;
++	}
++      grub_free (set_path);
++
++      if (*name)
++	break;
++    }
++
++  return GRUB_ERR_NONE;
++
++cleanup_string:
++  grub_free (string_der);
++cleanup_val_path:
++  grub_free (val_path);
++cleanup_type:
++  grub_free (type_path);
++cleanup_set:
++  grub_free (set_path);
++cleanup:
++  grub_free (top_path);
++  return err;
++}
++
++/*
++ * details here
++ */
++static grub_err_t
++verify_key_usage (grub_uint8_t * value, int value_size)
++{
++  asn1_node usageasn;
++  int result;
++  grub_err_t err = GRUB_ERR_NONE;
++  grub_uint8_t usage = 0xff;
++  int usage_size = 1;
++
++  result =
++    asn1_create_element (_gnutls_pkix_asn, "PKIX1.KeyUsage", &usageasn);
++  if (result != ASN1_SUCCESS)
++    {
++      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++			 "Could not create ASN.1 structure for key usage");
++    }
++
++  result = asn1_der_decoding2 (&usageasn, value, &value_size,
++			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
++  if (result != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		    "Error parsing DER for Key Usage: %s", asn1_error);
++      goto cleanup;
++    }
++
++  result = asn1_read_value (usageasn, "", &usage, &usage_size);
++  if (result != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		    "Error reading Key Usage value: %s",
++		    asn1_strerror (result));
++      goto cleanup;
++    }
++
++  /* Only the first bit is permitted to be set */
++  if (usage != 0x80)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unexpected Key Usage value: %x",
++		    usage);
++      goto cleanup;
++    }
++
++cleanup:
++  asn1_delete_structure (&usageasn);
++  return err;
++}
++
++/*
++ * BasicConstraints ::= SEQUENCE {
++ *       cA                      BOOLEAN DEFAULT FALSE,
++ *       pathLenConstraint       INTEGER (0..MAX) OPTIONAL }
++ */
++static grub_err_t
++verify_basic_constraints (grub_uint8_t * value, int value_size)
++{
++  asn1_node basicasn;
++  int result;
++  grub_err_t err = GRUB_ERR_NONE;
++  char cA[6];			/* FALSE or TRUE */
++  int cA_size = sizeof (cA);
++
++  result =
++    asn1_create_element (_gnutls_pkix_asn, "PKIX1.BasicConstraints",
++			 &basicasn);
++  if (result != ASN1_SUCCESS)
++    {
++      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++			 "Could not create ASN.1 structure for Basic Constraints");
++    }
++
++  result = asn1_der_decoding2 (&basicasn, value, &value_size,
++			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
++  if (result != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		    "Error parsing DER for Basic Constraints: %s",
++		    asn1_error);
++      goto cleanup;
++    }
++
++  result = asn1_read_value (basicasn, "cA", cA, &cA_size);
++  if (result == ASN1_ELEMENT_NOT_FOUND)
++    {
++      /* Not present, default is False, so this is OK */
++      err = GRUB_ERR_NONE;
++      goto cleanup;
++    }
++  else if (result != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		    "Error reading Basic Constraints cA value: %s",
++		    asn1_strerror (result));
++      goto cleanup;
++    }
++
++  /* The certificate must not be a CA certificate */
++  if (grub_strncmp ("FALSE", cA, cA_size) != 0)
++    {
++      err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unexpected CA value: %s",
++			cA);
++      goto cleanup;
++    }
++
++cleanup:
++  asn1_delete_structure (&basicasn);
++  return err;
++}
++
++
++/*
++ * Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
++ *
++ * Extension  ::=  SEQUENCE  {
++ *      extnID      OBJECT IDENTIFIER,
++ *      critical    BOOLEAN DEFAULT FALSE,
++ *      extnValue   OCTET STRING
++ *                  -- contains the DER encoding of an ASN.1 value
++ *                  -- corresponding to the extension type identified
++ *                  -- by extnID
++ * }
++ *
++ * We require that a certificate:
++ *  - contain the Digital Signature usage only
++ *  - not be a CA
++ *  - MUST not contain any other critical extensions (RFC 5280 s 4.2)
++ */
++static grub_err_t
++verify_extensions (asn1_node cert)
++{
++  int result;
++  int ext, num_extensions = 0;
++  int usage_present = 0, constraints_present = 0;
++  char *oid_path, *critical_path, *value_path;
++  char extnID[MAX_OID_LEN];
++  int extnID_size;
++  grub_err_t err;
++  char critical[6];		/* we get either "TRUE" or "FALSE" */
++  int critical_size;
++  grub_uint8_t *value;
++  int value_size;
++
++  result =
++    asn1_number_of_elements (cert, "tbsCertificate.extensions",
++			     &num_extensions);
++  if (result != ASN1_SUCCESS)
++    {
++      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			 "Error counting number of extensions: %s",
++			 asn1_strerror (result));
++    }
++
++  if (num_extensions < 2)
++    {
++      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			 "Insufficient number of extensions for certificate, need at least 2, got %d",
++			 num_extensions);
++    }
++
++  for (ext = 1; ext <= num_extensions; ext++)
++    {
++      oid_path = grub_xasprintf ("tbsCertificate.extensions.?%d.extnID", ext);
++
++      extnID_size = sizeof (extnID);
++      result = asn1_read_value (cert, oid_path, extnID, &extnID_size);
++      if (result != GRUB_ERR_NONE)
++	{
++	  err =
++	    grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			"Error reading extension OID: %s",
++			asn1_strerror (result));
++	  goto cleanup_oid_path;
++	}
++
++      critical_path =
++	grub_xasprintf ("tbsCertificate.extensions.?%d.critical", ext);
++      critical_size = sizeof (critical);
++      result =
++	asn1_read_value (cert, critical_path, critical, &critical_size);
++      if (result == ASN1_ELEMENT_NOT_FOUND)
++	{
++	  critical[0] = '\0';
++	}
++      else if (result != ASN1_SUCCESS)
++	{
++	  err =
++	    grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			"Error reading extension criticality: %s",
++			asn1_strerror (result));
++	  goto cleanup_critical_path;
++	}
++
++      value_path =
++	grub_xasprintf ("tbsCertificate.extensions.?%d.extnValue", ext);
++      value =
++	grub_asn1_allocate_and_read (cert, value_path,
++				     "certificate extension value",
++				     &value_size);
++      if (!value)
++	{
++	  err = grub_errno;
++	  goto cleanup_value_path;
++	}
++
++      /*
++       * Now we must see if we recognise the OID.
++       * If we have an unrecognised critical extension we MUST bail.
++       */
++      if (grub_strncmp (keyUsage_oid, extnID, extnID_size) == 0)
++	{
++	  err = verify_key_usage (value, value_size);
++	  if (err != GRUB_ERR_NONE)
++	    {
++	      goto cleanup_value;
++	    }
++	  usage_present++;
++	}
++      else if (grub_strncmp (basicConstraints_oid, extnID, extnID_size) == 0)
++	{
++	  err = verify_basic_constraints (value, value_size);
++	  if (err != GRUB_ERR_NONE)
++	    {
++	      goto cleanup_value;
++	    }
++	  constraints_present++;
++	}
++      else if (grub_strncmp ("TRUE", critical, critical_size) == 0)
++	{
++	  /*
++	   * per the RFC, we must not process a certificate with
++	   * a critical extension we do not understand.
++	   */
++	  err =
++	    grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			"Unhandled critical x509 extension with OID %s",
++			extnID);
++	  goto cleanup_value;
++	}
++
++      grub_free (value);
++      grub_free (value_path);
++      grub_free (critical_path);
++      grub_free (oid_path);
++    }
++
++  if (usage_present != 1)
++    {
++      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			 "Unexpected number of Key Usage extensions - expected 1, got %d",
++			 usage_present);
++    }
++  if (constraints_present != 1)
++    {
++      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			 "Unexpected number of basic constraints extensions - expected 1, got %d",
++			 constraints_present);
++    }
++  return GRUB_ERR_NONE;
++
++cleanup_value:
++  grub_free (value);
++cleanup_value_path:
++  grub_free (value_path);
++cleanup_critical_path:
++  grub_free (critical_path);
++cleanup_oid_path:
++  grub_free (oid_path);
++  return err;
++}
++
++/*
++ * Parse a certificate whose DER-encoded form is in @data, of size @data_size.
++ * Return the results in @results, which must point to an allocated x509 certificate.
++ */
++grub_err_t
++certificate_import (void *data, grub_size_t data_size,
++		    struct x509_certificate *results)
++{
++  int result = 0;
++  asn1_node cert;
++  grub_err_t err;
++  int size;
++  int tmp_size;
++
++  if (data_size > GRUB_INT_MAX)
++    return grub_error (GRUB_ERR_OUT_OF_RANGE,
++		       "Cannot parse a certificate where data size > INT_MAX");
++  size = (int) data_size;
++
++  result = asn1_create_element (_gnutls_pkix_asn, "PKIX1.Certificate", &cert);
++  if (result != ASN1_SUCCESS)
++    {
++      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++			 "Could not create ASN.1 structure for certificate: %s",
++			 asn1_strerror (result));
++    }
++
++  result = asn1_der_decoding2 (&cert, data, &size,
++			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
++  if (result != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		    "Could not parse DER for certificate: %s", asn1_error);
++      goto cleanup;
++    }
++
++  /* 
++   * TBSCertificate  ::=  SEQUENCE {
++   *     version         [0]  EXPLICIT Version DEFAULT v1
++   */
++  err = check_version (cert);
++  if (err != GRUB_ERR_NONE)
++    {
++      goto cleanup;
++    }
++
++  /*
++   * serialNumber         CertificateSerialNumber,
++   *
++   * CertificateSerialNumber  ::=  INTEGER
++   */
++  results->serial =
++    grub_asn1_allocate_and_read (cert, "tbsCertificate.serialNumber",
++				 "certificate serial number", &tmp_size);
++  if (!results->serial)
++    {
++      err = grub_errno;
++      goto cleanup;
++    }
++  /*
++   * It's safe to cast the signed int to an unsigned here, we know
++   * length is non-negative
++   */
++  results->serial_len = tmp_size;
++
++  /* 
++   * signature            AlgorithmIdentifier,
++   *
++   * We don't load the signature or issuer at the moment,
++   * as we don't attempt x509 verification.
++   */
++
++  /*
++   * issuer               Name,
++   *
++   * The RFC only requires the serial number to be unique within
++   * issuers, so to avoid ambiguity we _technically_ ought to make
++   * this available.
++   */
++
++  /*
++   * validity             Validity,
++   *
++   * Validity ::= SEQUENCE {
++   *     notBefore      Time,
++   *     notAfter       Time }
++   *
++   * We can't validate this reasonably, we have no true time source on several
++   * platforms. For now we do not parse them.
++   */
++
++  /*
++   * subject              Name,
++   * 
++   * This is an X501 name, we parse out just the CN.
++   */
++  err =
++    read_name (cert, "tbsCertificate.subject", &results->subject,
++	       &results->subject_len);
++  if (err != GRUB_ERR_NONE)
++    goto cleanup_serial;
++
++  /*
++   * TBSCertificate  ::=  SEQUENCE  {
++   *    ...
++   *    subjectPublicKeyInfo SubjectPublicKeyInfo,
++   *    ...
++   */
++  err = grub_x509_read_subject_public_key (cert, results);
++  if (err != GRUB_ERR_NONE)
++    goto cleanup_name;
++
++  /*
++   * TBSCertificate  ::=  SEQUENCE  {
++   *    ...
++   *    extensions      [3]  EXPLICIT Extensions OPTIONAL
++   *                         -- If present, version MUST be v3
++   * }
++   */
++
++  err = verify_extensions (cert);
++  if (err != GRUB_ERR_NONE)
++    goto cleanup_name;
++
++
++  /*
++   * We do not read or check the signature on the certificate:
++   * as discussed we do not try to validate the certificate but trust
++   * it implictly.
++   */
++
++  asn1_delete_structure (&cert);
++  return GRUB_ERR_NONE;
++
++
++cleanup_name:
++  grub_free (results->subject);
++cleanup_serial:
++  grub_free (results->serial);
++cleanup:
++  asn1_delete_structure (&cert);
++  return err;
++}
++
++/*
++ * Release all the storage associated with the x509 certificate.
++ * If the caller dynamically allocated the certificate, it must free it.
++ * The caller is also responsible for maintenance of the linked list.
++ */
++void
++certificate_release (struct x509_certificate *cert)
++{
++  grub_free (cert->subject);
++  grub_free (cert->serial);
++  gcry_mpi_release (cert->mpis[0]);
++  gcry_mpi_release (cert->mpis[1]);
++}
+diff --git a/grub-core/commands/appendedsig/appendedsig.h b/grub-core/commands/appendedsig/appendedsig.h
+new file mode 100644
+index 00000000000..9792ef3901e
+--- /dev/null
++++ b/grub-core/commands/appendedsig/appendedsig.h
+@@ -0,0 +1,110 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2020  IBM Corporation.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/crypto.h>
++#include <grub/libtasn1.h>
++
++extern asn1_node _gnutls_gnutls_asn;
++extern asn1_node _gnutls_pkix_asn;
++
++#define MAX_OID_LEN 32
++
++/*
++ * One or more x509 certificates.
++ *
++ * We do limited parsing: extracting only the serial, CN and RSA public key.
++ */
++struct x509_certificate
++{
++  struct x509_certificate *next;
++
++  grub_uint8_t *serial;
++  grub_size_t serial_len;
++
++  char *subject;
++  grub_size_t subject_len;
++
++  /* We only support RSA public keys. This encodes [modulus, publicExponent] */
++  gcry_mpi_t mpis[2];
++};
++
++/*
++ * A PKCS#7 signedData message.
++ *
++ * We make no attempt to match intelligently, so we don't save any info about
++ * the signer. We also support only 1 signerInfo, so we only store a single
++ * MPI for the signature.
++ */
++struct pkcs7_signedData
++{
++  const gcry_md_spec_t *hash;
++  gcry_mpi_t sig_mpi;
++};
++
++
++/* Do libtasn1 init */
++int asn1_init (void);
++
++/*
++ * Import a DER-encoded certificate at 'data', of size 'size'.
++ *
++ * Place the results into 'results', which must be already allocated.
++ */
++grub_err_t
++certificate_import (void *data, grub_size_t size,
++		    struct x509_certificate *results);
++
++/*
++ * Release all the storage associated with the x509 certificate.
++ * If the caller dynamically allocated the certificate, it must free it.
++ * The caller is also responsible for maintenance of the linked list.
++ */
++void certificate_release (struct x509_certificate *cert);
++
++/*
++ * Parse a PKCS#7 message, which must be a signedData message.
++ *
++ * The message must be in 'sigbuf' and of size 'data_size'. The result is
++ * placed in 'msg', which must already be allocated.
++ */
++grub_err_t
++parse_pkcs7_signedData (void *sigbuf, grub_size_t data_size,
++			struct pkcs7_signedData *msg);
++
++/*
++ * Release all the storage associated with the PKCS#7 message.
++ * If the caller dynamically allocated the message, it must free it.
++ */
++void pkcs7_signedData_release (struct pkcs7_signedData *msg);
++
++/*
++ * Read a value from an ASN1 node, allocating memory to store it.
++ *
++ * It will work for anything where the size libtasn1 returns is right:
++ *  - Integers
++ *  - Octet strings
++ *  - DER encoding of other structures
++ * It will _not_ work for things where libtasn1 size requires adjustment:
++ *  - Strings that require an extra NULL byte at the end
++ *  - Bit strings because libtasn1 returns the length in bits, not bytes.
++ *
++ * If the function returns a non-NULL value, the caller must free it.
++ */
++void *grub_asn1_allocate_and_read (asn1_node node, const char *name,
++				   const char *friendly_name,
++				   int *content_size);
diff --git a/SOURCES/0194-appended-signatures-support-verifying-appended-signa.patch b/SOURCES/0194-appended-signatures-support-verifying-appended-signa.patch
new file mode 100644
index 0000000..262efc0
--- /dev/null
+++ b/SOURCES/0194-appended-signatures-support-verifying-appended-signa.patch
@@ -0,0 +1,716 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Thu, 30 Jul 2020 01:35:43 +1000
+Subject: [PATCH] appended signatures: support verifying appended signatures
+
+Building on the parsers and the ability to embed x509 certificates, as
+well as the existing gcrypt functionality, add a module for verifying
+appended signatures.
+
+This includes a verifier that requires that Linux kernels and grub modules
+have appended signatures, and commands to manage the list of trusted
+certificates for verification.
+
+Verification must be enabled by setting check_appended_signatures. If
+GRUB is locked down when the module is loaded, verification will be
+enabled and locked automatically.
+
+As with the PGP verifier, it is not a complete secure-boot solution:
+other mechanisms, such as a password or lockdown, must be used to ensure
+that a user cannot drop to the grub shell and disable verification.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/Makefile.core.def                  |  12 +
+ grub-core/commands/appendedsig/appendedsig.c | 645 +++++++++++++++++++++++++++
+ include/grub/file.h                          |   2 +
+ 3 files changed, 659 insertions(+)
+ create mode 100644 grub-core/commands/appendedsig/appendedsig.c
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index b4aaccf7b57..77321d218c8 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -980,6 +980,18 @@ module = {
+   cppflags = '-I$(srcdir)/lib/posix_wrap';
+ };
+ 
++module = {
++  name = appendedsig;
++  common = commands/appendedsig/appendedsig.c;
++  common = commands/appendedsig/x509.c;
++  common = commands/appendedsig/pkcs7.c;
++  common = commands/appendedsig/asn1util.c;
++  common = commands/appendedsig/gnutls_asn1_tab.c;
++  common = commands/appendedsig/pkix_asn1_tab.c;
++  cflags = '$(CFLAGS_POSIX)';
++  cppflags = '-I$(srcdir)/lib/posix_wrap';
++};
++
+ module = {
+   name = hdparm;
+   common = commands/hdparm.c;
+diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c
+new file mode 100644
+index 00000000000..dc294cd339e
+--- /dev/null
++++ b/grub-core/commands/appendedsig/appendedsig.c
+@@ -0,0 +1,645 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2020-2021  IBM Corporation.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/types.h>
++#include <grub/misc.h>
++#include <grub/mm.h>
++#include <grub/err.h>
++#include <grub/dl.h>
++#include <grub/file.h>
++#include <grub/command.h>
++#include <grub/crypto.h>
++#include <grub/pkcs1_v15.h>
++#include <grub/i18n.h>
++#include <grub/gcrypt/gcrypt.h>
++#include <grub/kernel.h>
++#include <grub/extcmd.h>
++#include <grub/verify.h>
++#include <grub/libtasn1.h>
++#include <grub/env.h>
++#include <grub/lockdown.h>
++
++#include "appendedsig.h"
++
++GRUB_MOD_LICENSE ("GPLv3+");
++
++const char magic[] = "~Module signature appended~\n";
++
++/*
++ * This structure is extracted from scripts/sign-file.c in the linux kernel
++ * source. It was licensed as LGPLv2.1+, which is GPLv3+ compatible.
++ */
++struct module_signature
++{
++  grub_uint8_t algo;		/* Public-key crypto algorithm [0] */
++  grub_uint8_t hash;		/* Digest algorithm [0] */
++  grub_uint8_t id_type;		/* Key identifier type [PKEY_ID_PKCS7] */
++  grub_uint8_t signer_len;	/* Length of signer's name [0] */
++  grub_uint8_t key_id_len;	/* Length of key identifier [0] */
++  grub_uint8_t __pad[3];
++  grub_uint32_t sig_len;	/* Length of signature data */
++} GRUB_PACKED;
++
++
++/* This represents an entire, parsed, appended signature */
++struct grub_appended_signature
++{
++  grub_size_t signature_len;		/* Length of PKCS#7 data +
++                                         * metadata + magic */
++
++  struct module_signature sig_metadata;	/* Module signature metadata */
++  struct pkcs7_signedData pkcs7;	/* Parsed PKCS#7 data */
++};
++
++/* Trusted certificates for verifying appended signatures */
++struct x509_certificate *grub_trusted_key;
++
++/*
++ * Force gcry_rsa to be a module dependency.
++ *
++ * If we use grub_crypto_pk_rsa, then then the gcry_rsa module won't be built
++ * in if you add 'appendedsig' to grub-install --modules. You would need to
++ * add 'gcry_rsa' too. That's confusing and seems suboptimal, especially when
++ * we only support RSA.
++ *
++ * Dynamic loading also causes some concerns. We can't load gcry_rsa from the
++ * the filesystem after we install the verifier - we won't be able to verify
++ * it without having it already present. We also shouldn't load it before we
++ * install the verifier, because that would mean it wouldn't be verified - an
++ * attacker could insert any code they wanted into the module.
++ *
++ * So instead, reference the internal symbol from gcry_rsa. That creates a
++ * direct dependency on gcry_rsa, so it will be built in when this module
++ * is built in. Being built in (assuming the core image is itself signed!)
++ * also resolves our concerns about loading from the filesystem.
++ */
++extern gcry_pk_spec_t _gcry_pubkey_spec_rsa;
++
++static int check_sigs = 0;
++
++static const char *
++grub_env_read_sec (struct grub_env_var *var __attribute__ ((unused)),
++                   const char *val __attribute__ ((unused)))
++{
++  if (check_sigs == 2)
++    return "forced";
++  else if (check_sigs == 1)
++    return "enforce";
++  else
++    return "no";
++}
++
++static char *
++grub_env_write_sec (struct grub_env_var *var __attribute__((unused)),
++		    const char *val)
++{
++  /* Do not allow the value to be changed if set to forced */
++  if (check_sigs == 2)
++    return grub_strdup ("forced");
++
++  if ((*val == '2') || (*val == 'f'))
++    check_sigs = 2;
++  else if ((*val == '1') || (*val == 'e'))
++    check_sigs = 1;
++  else if ((*val == '0') || (*val == 'n'))
++    check_sigs = 0;
++
++  return grub_strdup (grub_env_read_sec (NULL, NULL));
++}
++
++static grub_err_t
++read_cert_from_file (grub_file_t f, struct x509_certificate *certificate)
++{
++  grub_err_t err;
++  grub_uint8_t *buf = NULL;
++  grub_ssize_t read_size;
++  grub_off_t total_read_size = 0;
++  grub_off_t file_size = grub_file_size (f);
++
++
++  if (file_size == GRUB_FILE_SIZE_UNKNOWN)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT,
++		       N_("Cannot parse a certificate file of unknown size"));
++
++  buf = grub_zalloc (file_size);
++  if (!buf)
++    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++		       N_("Could not allocate buffer for certificate file contents"));
++
++  while (total_read_size < file_size)
++    {
++      read_size =
++	grub_file_read (f, &buf[total_read_size],
++			file_size - total_read_size);
++      if (read_size < 0)
++	{
++	  err = grub_error (GRUB_ERR_READ_ERROR,
++			    N_("Error reading certificate file"));
++	  goto cleanup_buf;
++	}
++      total_read_size += read_size;
++    }
++
++  err = certificate_import (buf, total_read_size, certificate);
++  if (err != GRUB_ERR_NONE)
++    goto cleanup_buf;
++
++  return GRUB_ERR_NONE;
++
++cleanup_buf:
++  grub_free (buf);
++  return err;
++}
++
++static grub_err_t
++extract_appended_signature (grub_uint8_t * buf, grub_size_t bufsize,
++			    struct grub_appended_signature *sig)
++{
++  grub_err_t err;
++  grub_size_t pkcs7_size;
++  grub_size_t remaining_len;
++  grub_uint8_t *appsigdata = buf + bufsize - grub_strlen (magic);
++
++  if (bufsize < grub_strlen (magic))
++    return grub_error (GRUB_ERR_BAD_SIGNATURE,
++		       N_("File too short for signature magic"));
++
++  if (grub_memcmp (appsigdata, (grub_uint8_t *) magic, grub_strlen (magic)))
++    return grub_error (GRUB_ERR_BAD_SIGNATURE,
++		       N_("Missing or invalid signature magic"));
++
++  remaining_len = bufsize - grub_strlen (magic);
++
++  if (remaining_len < sizeof (struct module_signature))
++    return grub_error (GRUB_ERR_BAD_SIGNATURE,
++		       N_("File too short for signature metadata"));
++
++  appsigdata -= sizeof (struct module_signature);
++
++  /* extract the metadata */
++  grub_memcpy (&(sig->sig_metadata), appsigdata,
++	       sizeof (struct module_signature));
++
++  remaining_len -= sizeof (struct module_signature);
++
++  if (sig->sig_metadata.id_type != 2)
++    return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("Wrong signature type"));
++
++#ifdef GRUB_TARGET_WORDS_BIGENDIAN
++  pkcs7_size = sig->sig_metadata.sig_len;
++#else
++  pkcs7_size = __builtin_bswap32 (sig->sig_metadata.sig_len);
++#endif
++
++  if (pkcs7_size > remaining_len)
++    return grub_error (GRUB_ERR_BAD_SIGNATURE,
++		       N_("File too short for PKCS#7 message"));
++
++  grub_dprintf ("appendedsig", "sig len %" PRIuGRUB_SIZE "\n", pkcs7_size);
++
++  sig->signature_len =
++    grub_strlen (magic) + sizeof (struct module_signature) + pkcs7_size;
++
++  /* rewind pointer and parse pkcs7 data */
++  appsigdata -= pkcs7_size;
++
++  err = parse_pkcs7_signedData (appsigdata, pkcs7_size, &sig->pkcs7);
++  if (err != GRUB_ERR_NONE)
++    return err;
++
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_verify_appended_signature (grub_uint8_t * buf, grub_size_t bufsize)
++{
++  grub_err_t err = GRUB_ERR_NONE;
++  grub_size_t datasize;
++  void *context;
++  unsigned char *hash;
++  gcry_mpi_t hashmpi;
++  gcry_err_code_t rc;
++  struct x509_certificate *pk;
++  struct grub_appended_signature sig;
++
++  if (!grub_trusted_key)
++    return grub_error (GRUB_ERR_BAD_SIGNATURE,
++		       N_("No trusted keys to verify against"));
++
++  err = extract_appended_signature (buf, bufsize, &sig);
++  if (err != GRUB_ERR_NONE)
++    return err;
++
++  datasize = bufsize - sig.signature_len;
++
++  context = grub_zalloc (sig.pkcs7.hash->contextsize);
++  if (!context)
++    return grub_errno;
++
++  sig.pkcs7.hash->init (context);
++  sig.pkcs7.hash->write (context, buf, datasize);
++  sig.pkcs7.hash->final (context);
++  hash = sig.pkcs7.hash->read (context);
++  grub_dprintf ("appendedsig",
++		"data size %" PRIxGRUB_SIZE ", hash %02x%02x%02x%02x...\n",
++		datasize, hash[0], hash[1], hash[2], hash[3]);
++
++  err = GRUB_ERR_BAD_SIGNATURE;
++  for (pk = grub_trusted_key; pk; pk = pk->next)
++    {
++      rc = grub_crypto_rsa_pad (&hashmpi, hash, sig.pkcs7.hash, pk->mpis[0]);
++      if (rc)
++	{
++	  err = grub_error (GRUB_ERR_BAD_SIGNATURE,
++			    N_("Error padding hash for RSA verification: %d"),
++			    rc);
++	  goto cleanup;
++	}
++
++      rc = _gcry_pubkey_spec_rsa.verify (0, hashmpi, &sig.pkcs7.sig_mpi,
++					 pk->mpis, NULL, NULL);
++      gcry_mpi_release (hashmpi);
++
++      if (rc == 0)
++	{
++	  grub_dprintf ("appendedsig", "verify with key '%s' succeeded\n",
++			pk->subject);
++	  err = GRUB_ERR_NONE;
++	  break;
++	}
++
++      grub_dprintf ("appendedsig", "verify with key '%s' failed with %d\n",
++		    pk->subject, rc);
++    }
++
++  /* If we didn't verify, provide a neat message */
++  if (err != GRUB_ERR_NONE)
++      err = grub_error (GRUB_ERR_BAD_SIGNATURE,
++			N_("Failed to verify signature against a trusted key"));
++
++cleanup:
++  grub_free (context);
++  pkcs7_signedData_release (&sig.pkcs7);
++
++  return err;
++}
++
++static grub_err_t
++grub_cmd_verify_signature (grub_command_t cmd __attribute__((unused)),
++			   int argc, char **args)
++{
++  grub_file_t f;
++  grub_err_t err = GRUB_ERR_NONE;
++  grub_uint8_t *data;
++  grub_ssize_t read_size;
++  grub_off_t file_size, total_read_size = 0;
++
++  if (argc < 1)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
++
++  grub_dprintf ("appendedsig", "verifying %s\n", args[0]);
++
++  f = grub_file_open (args[0], GRUB_FILE_TYPE_VERIFY_SIGNATURE);
++  if (!f)
++    {
++      err = grub_errno;
++      goto cleanup;
++    }
++
++  file_size = grub_file_size (f);
++  if (file_size == GRUB_FILE_SIZE_UNKNOWN)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT,
++		       N_("Cannot verify the signature of a file of unknown size"));
++
++  data = grub_malloc (file_size);
++  if (!data)
++    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++		       N_("Could not allocate data buffer size "
++		       PRIuGRUB_UINT64_T " for verification"), file_size);
++
++  while (total_read_size < file_size)
++    {
++      read_size =
++	grub_file_read (f, &data[total_read_size],
++			file_size - total_read_size);
++      if (read_size < 0)
++	{
++	  err = grub_error (GRUB_ERR_READ_ERROR,
++			    N_("Error reading file to verify"));
++	  goto cleanup_data;
++	}
++      total_read_size += read_size;
++    }
++
++  err = grub_verify_appended_signature (data, file_size);
++
++cleanup_data:
++  grub_free (data);
++cleanup:
++  if (f)
++    grub_file_close (f);
++  return err;
++}
++
++static grub_err_t
++grub_cmd_distrust (grub_command_t cmd __attribute__((unused)),
++		   int argc, char **args)
++{
++  unsigned long cert_num, i;
++  struct x509_certificate *cert, *prev;
++
++  if (argc != 1)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("One argument expected"));
++
++  grub_errno = GRUB_ERR_NONE;
++  cert_num = grub_strtoul (args[0], NULL, 10);
++  if (grub_errno != GRUB_ERR_NONE)
++    return grub_errno;
++
++  if (cert_num < 1)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT,
++		       N_("Certificate number too small - numbers start at 1"));
++
++  if (cert_num == 1)
++    {
++      cert = grub_trusted_key;
++      grub_trusted_key = cert->next;
++
++      certificate_release (cert);
++      grub_free (cert);
++      return GRUB_ERR_NONE;
++    }
++  i = 2;
++  prev = grub_trusted_key;
++  cert = grub_trusted_key->next;
++  while (cert)
++    {
++      if (i == cert_num)
++	{
++	  prev->next = cert->next;
++	  certificate_release (cert);
++	  grub_free (cert);
++	  return GRUB_ERR_NONE;
++	}
++      i++;
++      prev = cert;
++      cert = cert->next;
++    }
++
++  return grub_error (GRUB_ERR_BAD_ARGUMENT,
++		     N_("No certificate number %d found - only %d certificates in the store"),
++		     cert_num, i - 1);
++}
++
++static grub_err_t
++grub_cmd_trust (grub_command_t cmd __attribute__((unused)),
++		int argc, char **args)
++{
++  grub_file_t certf;
++  struct x509_certificate *cert = NULL;
++  grub_err_t err;
++
++  if (argc != 1)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
++
++  certf = grub_file_open (args[0],
++			  GRUB_FILE_TYPE_CERTIFICATE_TRUST
++			  | GRUB_FILE_TYPE_NO_DECOMPRESS);
++  if (!certf)
++    return grub_errno;
++
++
++  cert = grub_zalloc (sizeof (struct x509_certificate));
++  if (!cert)
++    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++		       N_("Could not allocate memory for certificate"));
++
++  err = read_cert_from_file (certf, cert);
++  grub_file_close (certf);
++  if (err != GRUB_ERR_NONE)
++    {
++      grub_free (cert);
++      return err;
++    }
++  grub_dprintf ("appendedsig", "Loaded certificate with CN: %s\n",
++		cert->subject);
++
++  cert->next = grub_trusted_key;
++  grub_trusted_key = cert;
++
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_cmd_list (grub_command_t cmd __attribute__((unused)),
++	       int argc __attribute__((unused)),
++	       char **args __attribute__((unused)))
++{
++  struct x509_certificate *cert;
++  int cert_num = 1;
++  grub_size_t i;
++
++  for (cert = grub_trusted_key; cert; cert = cert->next)
++    {
++      grub_printf (N_("Certificate %d:\n"), cert_num);
++
++      grub_printf (N_("\tSerial: "));
++      for (i = 0; i < cert->serial_len - 1; i++)
++	{
++	  grub_printf ("%02x:", cert->serial[i]);
++	}
++      grub_printf ("%02x\n", cert->serial[cert->serial_len - 1]);
++
++      grub_printf ("\tCN: %s\n\n", cert->subject);
++      cert_num++;
++
++    }
++
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++appendedsig_init (grub_file_t io __attribute__((unused)),
++		  enum grub_file_type type,
++		  void **context __attribute__((unused)),
++		  enum grub_verify_flags *flags)
++{
++  if (!check_sigs)
++    {
++      *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
++      return GRUB_ERR_NONE;
++    }
++
++  switch (type & GRUB_FILE_TYPE_MASK)
++    {
++    case GRUB_FILE_TYPE_CERTIFICATE_TRUST:
++      /*
++       * This is a certificate to add to trusted keychain.
++       *
++       * This needs to be verified or blocked. Ideally we'd write an x509
++       * verifier, but we lack the hubris required to take this on. Instead,
++       * require that it have an appended signature.
++       */
++
++      /* Fall through */
++
++    case GRUB_FILE_TYPE_LINUX_KERNEL:
++    case GRUB_FILE_TYPE_GRUB_MODULE:
++      /*
++       * Appended signatures are only defined for ELF binaries.
++       * Out of an abundance of caution, we only verify Linux kernels and
++       * GRUB modules at this point.
++       */
++      *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK;
++      return GRUB_ERR_NONE;
++
++    case GRUB_FILE_TYPE_ACPI_TABLE:
++    case GRUB_FILE_TYPE_DEVICE_TREE_IMAGE:
++      /*
++       * It is possible to use appended signature verification without
++       * lockdown - like the PGP verifier. When combined with an embedded
++       * config file in a signed grub binary, this could still be a meaningful
++       * secure-boot chain - so long as it isn't subverted by something like a
++       * rouge ACPI table or DT image. Defer them explicitly.
++       */
++      *flags = GRUB_VERIFY_FLAGS_DEFER_AUTH;
++      return GRUB_ERR_NONE;
++
++    default:
++      *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
++      return GRUB_ERR_NONE;
++    }
++}
++
++static grub_err_t
++appendedsig_write (void *ctxt __attribute__((unused)),
++		   void *buf, grub_size_t size)
++{
++  return grub_verify_appended_signature (buf, size);
++}
++
++struct grub_file_verifier grub_appendedsig_verifier = {
++  .name = "appendedsig",
++  .init = appendedsig_init,
++  .write = appendedsig_write,
++};
++
++static grub_ssize_t
++pseudo_read (struct grub_file *file, char *buf, grub_size_t len)
++{
++  grub_memcpy (buf, (grub_uint8_t *) file->data + file->offset, len);
++  return len;
++}
++
++/* Filesystem descriptor.  */
++static struct grub_fs pseudo_fs = {
++  .name = "pseudo",
++  .fs_read = pseudo_read
++};
++
++static grub_command_t cmd_verify, cmd_list, cmd_distrust, cmd_trust;
++
++GRUB_MOD_INIT (appendedsig)
++{
++  int rc;
++  struct grub_module_header *header;
++
++  /* If in lockdown, immediately enter forced mode */
++  if (grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED)
++    check_sigs = 2;
++
++  grub_trusted_key = NULL;
++
++  grub_register_variable_hook ("check_appended_signatures",
++  			       grub_env_read_sec,
++			       grub_env_write_sec);
++  grub_env_export ("check_appended_signatures");
++
++  rc = asn1_init ();
++  if (rc)
++    grub_fatal ("Error initing ASN.1 data structures: %d: %s\n", rc,
++		asn1_strerror (rc));
++
++  FOR_MODULES (header)
++  {
++    struct grub_file pseudo_file;
++    struct x509_certificate *pk = NULL;
++    grub_err_t err;
++
++    /* Not an ELF module, skip.  */
++    if (header->type != OBJ_TYPE_X509_PUBKEY)
++      continue;
++
++    grub_memset (&pseudo_file, 0, sizeof (pseudo_file));
++    pseudo_file.fs = &pseudo_fs;
++    pseudo_file.size = header->size - sizeof (struct grub_module_header);
++    pseudo_file.data = (char *) header + sizeof (struct grub_module_header);
++
++    grub_dprintf ("appendedsig",
++		  "Found an x509 key, size=%" PRIuGRUB_UINT64_T "\n",
++		  pseudo_file.size);
++
++    pk = grub_zalloc (sizeof (struct x509_certificate));
++    if (!pk)
++      {
++	grub_fatal ("Out of memory loading initial certificates");
++      }
++
++    err = read_cert_from_file (&pseudo_file, pk);
++    if (err != GRUB_ERR_NONE)
++      grub_fatal ("Error loading initial key: %s", grub_errmsg);
++
++    grub_dprintf ("appendedsig", "loaded certificate CN='%s'\n", pk->subject);
++
++    pk->next = grub_trusted_key;
++    grub_trusted_key = pk;
++  }
++
++  cmd_trust =
++    grub_register_command ("trust_certificate", grub_cmd_trust,
++			   N_("X509_CERTIFICATE"),
++			   N_("Add X509_CERTIFICATE to trusted certificates."));
++  cmd_list =
++    grub_register_command ("list_certificates", grub_cmd_list, 0,
++			   N_("Show the list of trusted x509 certificates."));
++  cmd_verify =
++    grub_register_command ("verify_appended", grub_cmd_verify_signature,
++			   N_("FILE"),
++			   N_("Verify FILE against the trusted x509 certificates."));
++  cmd_distrust =
++    grub_register_command ("distrust_certificate", grub_cmd_distrust,
++			   N_("CERT_NUMBER"),
++			   N_("Remove CERT_NUMBER (as listed by list_certificates) from trusted certificates."));
++
++  grub_verifier_register (&grub_appendedsig_verifier);
++  grub_dl_set_persistent (mod);
++}
++
++GRUB_MOD_FINI (appendedsig)
++{
++  /*
++   * grub_dl_set_persistent should prevent this from actually running, but
++   * it does still run under emu.
++   */
++
++  grub_verifier_unregister (&grub_appendedsig_verifier);
++  grub_unregister_command (cmd_verify);
++  grub_unregister_command (cmd_list);
++  grub_unregister_command (cmd_trust);
++  grub_unregister_command (cmd_distrust);
++}
+diff --git a/include/grub/file.h b/include/grub/file.h
+index 31567483ccf..96827a4f896 100644
+--- a/include/grub/file.h
++++ b/include/grub/file.h
+@@ -80,6 +80,8 @@ enum grub_file_type
+     GRUB_FILE_TYPE_PUBLIC_KEY,
+     /* File holding public key to add to trused keys.  */
+     GRUB_FILE_TYPE_PUBLIC_KEY_TRUST,
++    /* File holding x509 certificiate to add to trusted keys.  */
++    GRUB_FILE_TYPE_CERTIFICATE_TRUST,
+     /* File of which we intend to print a blocklist to the user.  */
+     GRUB_FILE_TYPE_PRINT_BLOCKLIST,
+     /* File we intend to use for test loading or testing speed.  */
diff --git a/SOURCES/0195-appended-signatures-verification-tests.patch b/SOURCES/0195-appended-signatures-verification-tests.patch
new file mode 100644
index 0000000..9f6c3c6
--- /dev/null
+++ b/SOURCES/0195-appended-signatures-verification-tests.patch
@@ -0,0 +1,897 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Thu, 30 Jul 2020 01:31:02 +1000
+Subject: [PATCH] appended signatures: verification tests
+
+These tests are run through all_functional_test and test a range
+of commands and behaviours.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/Makefile.core.def               |   6 +
+ grub-core/tests/appended_signature_test.c | 281 +++++++++++++++
+ grub-core/tests/lib/functional_test.c     |   1 +
+ grub-core/tests/appended_signatures.h     | 557 ++++++++++++++++++++++++++++++
+ 4 files changed, 845 insertions(+)
+ create mode 100644 grub-core/tests/appended_signature_test.c
+ create mode 100644 grub-core/tests/appended_signatures.h
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 77321d218c8..6bddc841b85 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -2161,6 +2161,12 @@ module = {
+   common = tests/setjmp_test.c;
+ };
+ 
++module = {
++  name = appended_signature_test;
++  common = tests/appended_signature_test.c;
++  common = tests/appended_signatures.h;
++};
++
+ module = {
+   name = signature_test;
+   common = tests/signature_test.c;
+diff --git a/grub-core/tests/appended_signature_test.c b/grub-core/tests/appended_signature_test.c
+new file mode 100644
+index 00000000000..88a485200d8
+--- /dev/null
++++ b/grub-core/tests/appended_signature_test.c
+@@ -0,0 +1,281 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2020  IBM Corporation.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/time.h>
++#include <grub/misc.h>
++#include <grub/dl.h>
++#include <grub/command.h>
++#include <grub/env.h>
++#include <grub/test.h>
++#include <grub/mm.h>
++#include <grub/procfs.h>
++#include <grub/file.h>
++
++#include "appended_signatures.h"
++
++GRUB_MOD_LICENSE ("GPLv3+");
++
++#define DEFINE_TEST_CASE(case_name) \
++static char * \
++get_ ## case_name (grub_size_t *sz) \
++{ \
++  char *ret; \
++  *sz = case_name ## _len; \
++  ret = grub_malloc (*sz); \
++  if (ret) \
++    grub_memcpy (ret, case_name, *sz); \
++  return ret; \
++} \
++\
++static struct grub_procfs_entry case_name ## _entry = \
++{ \
++  .name = #case_name, \
++  .get_contents = get_ ## case_name \
++}
++
++#define DO_TEST(case_name, is_valid) \
++{ \
++  grub_procfs_register (#case_name, &case_name ## _entry); \
++  do_verify ("(proc)/" #case_name, is_valid); \
++  grub_procfs_unregister (&case_name ## _entry); \
++}
++
++
++DEFINE_TEST_CASE (hi_signed);
++DEFINE_TEST_CASE (hi_signed_sha256);
++DEFINE_TEST_CASE (hj_signed);
++DEFINE_TEST_CASE (short_msg);
++DEFINE_TEST_CASE (unsigned_msg);
++DEFINE_TEST_CASE (hi_signed_2nd);
++
++static char *
++get_certificate_der (grub_size_t * sz)
++{
++  char *ret;
++  *sz = certificate_der_len;
++  ret = grub_malloc (*sz);
++  if (ret)
++    grub_memcpy (ret, certificate_der, *sz);
++  return ret;
++}
++
++static struct grub_procfs_entry certificate_der_entry = {
++  .name = "certificate.der",
++  .get_contents = get_certificate_der
++};
++
++static char *
++get_certificate2_der (grub_size_t * sz)
++{
++  char *ret;
++  *sz = certificate2_der_len;
++  ret = grub_malloc (*sz);
++  if (ret)
++    grub_memcpy (ret, certificate2_der, *sz);
++  return ret;
++}
++
++static struct grub_procfs_entry certificate2_der_entry = {
++  .name = "certificate2.der",
++  .get_contents = get_certificate2_der
++};
++
++static char *
++get_certificate_printable_der (grub_size_t * sz)
++{
++  char *ret;
++  *sz = certificate_printable_der_len;
++  ret = grub_malloc (*sz);
++  if (ret)
++    grub_memcpy (ret, certificate_printable_der, *sz);
++  return ret;
++}
++
++static struct grub_procfs_entry certificate_printable_der_entry = {
++  .name = "certificate_printable.der",
++  .get_contents = get_certificate_printable_der
++};
++
++
++static void
++do_verify (const char *f, int is_valid)
++{
++  grub_command_t cmd;
++  char *args[] = { (char *) f, NULL };
++  grub_err_t err;
++
++  cmd = grub_command_find ("verify_appended");
++  if (!cmd)
++    {
++      grub_test_assert (0, "can't find command `%s'", "verify_appended");
++      return;
++    }
++  err = (cmd->func) (cmd, 1, args);
++  if (is_valid)
++    {
++      grub_test_assert (err == GRUB_ERR_NONE,
++			"verification of %s failed: %d: %s", f, grub_errno,
++			grub_errmsg);
++    }
++  else
++    {
++      grub_test_assert (err == GRUB_ERR_BAD_SIGNATURE,
++			"verification of %s unexpectedly succeeded", f);
++    }
++  grub_errno = GRUB_ERR_NONE;
++
++}
++
++static void
++appended_signature_test (void)
++{
++  grub_command_t cmd_trust, cmd_distrust;
++  char *trust_args[] = { (char *) "(proc)/certificate.der", NULL };
++  char *trust_args2[] = { (char *) "(proc)/certificate2.der", NULL };
++  char *trust_args_printable[] = { (char *) "(proc)/certificate_printable.der",
++				   NULL };
++  char *distrust_args[] = { (char *) "1", NULL };
++  char *distrust2_args[] = { (char *) "2", NULL };
++  grub_err_t err;
++
++  grub_procfs_register ("certificate.der", &certificate_der_entry);
++  grub_procfs_register ("certificate2.der", &certificate2_der_entry);
++  grub_procfs_register ("certificate_printable.der",
++			&certificate_printable_der_entry);
++
++  cmd_trust = grub_command_find ("trust_certificate");
++  if (!cmd_trust)
++    {
++      grub_test_assert (0, "can't find command `%s'", "trust_certificate");
++      return;
++    }
++  err = (cmd_trust->func) (cmd_trust, 1, trust_args);
++
++  grub_test_assert (err == GRUB_ERR_NONE,
++		    "loading certificate failed: %d: %s", grub_errno,
++		    grub_errmsg);
++
++  /* If we have no certificate the remainder of the tests are meaningless */
++  if (err != GRUB_ERR_NONE)
++    return;
++
++  /*
++   * Reload the command: this works around some 'interesting' behaviour in the
++   * dynamic command dispatcher. The first time you call cmd->func you get a
++   * dispatcher that loads the module, finds the real cmd, calls it, and then
++   * releases some internal storage. This means it's not safe to call a second
++   * time and we need to reload it.
++   */
++  cmd_trust = grub_command_find ("trust_certificate");
++
++  DO_TEST (hi_signed, 1);
++  DO_TEST (hi_signed_sha256, 1);
++  DO_TEST (hj_signed, 0);
++  DO_TEST (short_msg, 0);
++  DO_TEST (unsigned_msg, 0);
++
++  /*
++   * in enforcing mode, we shouldn't be able to load a certificate that isn't
++   * signed by an existing trusted key.
++   *
++   * However, procfs files automatically skip the verification test, so we can't
++   * easily test this.
++   */
++
++  /*
++   * verify that testing with 2 trusted certs works
++   */
++  DO_TEST (hi_signed_2nd, 0);
++
++  err = (cmd_trust->func) (cmd_trust, 1, trust_args2);
++
++  grub_test_assert (err == GRUB_ERR_NONE,
++		    "loading certificate 2 failed: %d: %s", grub_errno,
++		    grub_errmsg);
++
++  if (err != GRUB_ERR_NONE)
++    return;
++
++  DO_TEST (hi_signed_2nd, 1);
++  DO_TEST (hi_signed, 1);
++
++  /*
++   * Check certificate removal. They're added to the _top_ of the list and
++   * removed by position in the list. Current the list looks like [#2, #1].
++   *
++   * First test removing the second certificate in the list, which is
++   * certificate #1, giving us just [#2].
++   */
++  cmd_distrust = grub_command_find ("distrust_certificate");
++  if (!cmd_distrust)
++    {
++      grub_test_assert (0, "can't find command `%s'", "distrust_certificate");
++      return;
++    }
++
++  err = (cmd_distrust->func) (cmd_distrust, 1, distrust2_args);
++  grub_test_assert (err == GRUB_ERR_NONE,
++		    "distrusting certificate 1 failed: %d: %s", grub_errno,
++		    grub_errmsg);
++  DO_TEST (hi_signed_2nd, 1);
++  DO_TEST (hi_signed, 0);
++
++  /*
++   * Now reload certificate #1. This will make the list look like [#1, #2]
++   */
++  err = (cmd_trust->func) (cmd_trust, 1, trust_args);
++
++  grub_test_assert (err == GRUB_ERR_NONE,
++		    "reloading certificate 1 failed: %d: %s", grub_errno,
++		    grub_errmsg);
++  DO_TEST (hi_signed, 1);
++
++  /* Remove the first certificate in the list, giving us just [#2] */
++  err = (cmd_distrust->func) (cmd_distrust, 1, distrust_args);
++  grub_test_assert (err == GRUB_ERR_NONE,
++		    "distrusting certificate 1 (first time) failed: %d: %s",
++		    grub_errno, grub_errmsg);
++  DO_TEST (hi_signed_2nd, 1);
++  DO_TEST (hi_signed, 0);
++
++  /*
++   * Remove the first certificate again, giving an empty list.
++   *
++   * verify_appended should fail if there are no certificates to verify against.
++   */
++  err = (cmd_distrust->func) (cmd_distrust, 1, distrust_args);
++  grub_test_assert (err == GRUB_ERR_NONE,
++		    "distrusting certificate 1 (second time) failed: %d: %s",
++		    grub_errno, grub_errmsg);
++  DO_TEST (hi_signed_2nd, 0);
++
++  /*
++   * Lastly, check a certificate that uses printableString rather than
++   * utf8String loads properly.
++   */
++  err = (cmd_trust->func) (cmd_trust, 1, trust_args_printable);
++  grub_test_assert (err == GRUB_ERR_NONE,
++		    "distrusting printable certificate failed: %d: %s",
++		    grub_errno, grub_errmsg);
++
++  grub_procfs_unregister (&certificate_der_entry);
++  grub_procfs_unregister (&certificate2_der_entry);
++  grub_procfs_unregister (&certificate_printable_der_entry);
++}
++
++GRUB_FUNCTIONAL_TEST (appended_signature_test, appended_signature_test);
+diff --git a/grub-core/tests/lib/functional_test.c b/grub-core/tests/lib/functional_test.c
+index 96781fb39b5..403fa5c789a 100644
+--- a/grub-core/tests/lib/functional_test.c
++++ b/grub-core/tests/lib/functional_test.c
+@@ -73,6 +73,7 @@ grub_functional_all_tests (grub_extcmd_context_t ctxt __attribute__ ((unused)),
+   grub_dl_load ("xnu_uuid_test");
+   grub_dl_load ("pbkdf2_test");
+   grub_dl_load ("signature_test");
++  grub_dl_load ("appended_signature_test");
+   grub_dl_load ("sleep_test");
+   grub_dl_load ("bswap_test");
+   grub_dl_load ("ctz_test");
+diff --git a/grub-core/tests/appended_signatures.h b/grub-core/tests/appended_signatures.h
+new file mode 100644
+index 00000000000..aa3dc6278e3
+--- /dev/null
++++ b/grub-core/tests/appended_signatures.h
+@@ -0,0 +1,557 @@
++unsigned char certificate_der[] = {
++  0x30, 0x82, 0x03, 0x88, 0x30, 0x82, 0x02, 0x70, 0xa0, 0x03, 0x02, 0x01,
++  0x02, 0x02, 0x14, 0x25, 0x2e, 0xb8, 0xfd, 0x12, 0x62, 0x2e, 0xcd, 0x5d,
++  0xa7, 0x53, 0xd2, 0x0b, 0xc2, 0x61, 0x7c, 0x14, 0xe0, 0x0f, 0x5c, 0x30,
++  0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
++  0x05, 0x00, 0x30, 0x49, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04,
++  0x03, 0x0c, 0x1f, 0x47, 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65,
++  0x6e, 0x64, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75,
++  0x72, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1d,
++  0x30, 0x1b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09,
++  0x01, 0x16, 0x0e, 0x64, 0x6a, 0x61, 0x40, 0x61, 0x78, 0x74, 0x65, 0x6e,
++  0x73, 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x30, 0x30,
++  0x37, 0x30, 0x39, 0x30, 0x36, 0x32, 0x32, 0x30, 0x37, 0x5a, 0x18, 0x0f,
++  0x32, 0x31, 0x32, 0x30, 0x30, 0x36, 0x31, 0x35, 0x30, 0x36, 0x32, 0x32,
++  0x30, 0x37, 0x5a, 0x30, 0x52, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55,
++  0x04, 0x03, 0x0c, 0x28, 0x47, 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70,
++  0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74,
++  0x75, 0x72, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x69, 0x67,
++  0x6e, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x65, 0x79, 0x31, 0x1d, 0x30, 0x1b,
++  0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16,
++  0x0e, 0x64, 0x6a, 0x61, 0x40, 0x61, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x2e,
++  0x6e, 0x65, 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
++  0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
++  0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
++  0xcd, 0xe8, 0x1c, 0x08, 0x68, 0x2e, 0xcb, 0xfe, 0x8c, 0x4b, 0x3b, 0x61,
++  0xe7, 0x8e, 0x80, 0x58, 0x85, 0x85, 0xea, 0xc8, 0x3b, 0x42, 0xba, 0x72,
++  0x84, 0x65, 0x20, 0xbc, 0x48, 0xa2, 0x25, 0x49, 0x6e, 0x1c, 0xb9, 0x7d,
++  0xeb, 0xc1, 0x0c, 0xa8, 0xb7, 0xcc, 0x13, 0x78, 0xba, 0x11, 0xa4, 0x98,
++  0xd7, 0xd0, 0x7c, 0xdd, 0xf5, 0x5a, 0xb7, 0xcd, 0x31, 0x0e, 0xcd, 0x9e,
++  0xa7, 0x19, 0xf0, 0xbd, 0x0f, 0xa6, 0xfe, 0x8a, 0x11, 0x97, 0xed, 0x8b,
++  0xe5, 0x16, 0xa6, 0x21, 0x13, 0x36, 0xad, 0x05, 0x49, 0xec, 0x29, 0x12,
++  0x38, 0xa7, 0x4b, 0x0f, 0xa1, 0xfb, 0x72, 0xc0, 0xc0, 0x09, 0x67, 0x78,
++  0xa8, 0xb6, 0xd6, 0x1a, 0x39, 0xc0, 0xa8, 0xbf, 0x5f, 0x14, 0x89, 0x5c,
++  0xbc, 0x41, 0x0c, 0x0c, 0x5d, 0x42, 0x2e, 0x1c, 0xdf, 0x1f, 0x1d, 0xc9,
++  0x43, 0x94, 0x5b, 0x6e, 0x8f, 0x15, 0x8c, 0x8f, 0x94, 0x73, 0x4f, 0x97,
++  0x54, 0xf1, 0x86, 0x8a, 0xbc, 0xe4, 0xe4, 0x93, 0xc1, 0x5e, 0xc2, 0x3e,
++  0x31, 0x5e, 0xd4, 0x85, 0x57, 0x14, 0xd0, 0x11, 0x07, 0x65, 0xf4, 0x7c,
++  0x8f, 0x07, 0x57, 0xe1, 0x22, 0xd4, 0x78, 0x47, 0x65, 0x4e, 0xa9, 0xb3,
++  0xaa, 0xce, 0xc7, 0x36, 0xfe, 0xda, 0x66, 0x02, 0xb6, 0x8d, 0x18, 0x2f,
++  0x3b, 0x41, 0x8d, 0x02, 0x08, 0x72, 0x4b, 0x69, 0xbd, 0x1e, 0x58, 0xfc,
++  0x1b, 0x64, 0x04, 0x52, 0x35, 0x35, 0xe2, 0x3d, 0x3e, 0xde, 0xd6, 0x64,
++  0xf4, 0xec, 0x57, 0x7e, 0x65, 0x59, 0x00, 0xa6, 0xd3, 0x4b, 0x09, 0x93,
++  0x2a, 0x95, 0x0f, 0x30, 0xb6, 0xa1, 0x8c, 0xe7, 0x8b, 0x49, 0xa4, 0x1d,
++  0x25, 0x2d, 0x65, 0x48, 0x8a, 0x0f, 0xcf, 0x2a, 0xa2, 0xe1, 0xef, 0x72,
++  0x92, 0xc3, 0xf5, 0x21, 0x37, 0x83, 0x9b, 0x6d, 0x0b, 0x1b, 0xb3, 0xa2,
++  0x32, 0x38, 0x11, 0xb1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x5d, 0x30,
++  0x5b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04,
++  0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04,
++  0x03, 0x02, 0x07, 0x80, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
++  0x16, 0x04, 0x14, 0xe5, 0x2a, 0x4f, 0xf2, 0x84, 0x91, 0x57, 0x91, 0xaf,
++  0x12, 0xd2, 0xf1, 0xa1, 0x87, 0x73, 0x0f, 0x90, 0x25, 0xa0, 0x7a, 0x30,
++  0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
++  0x56, 0xd1, 0xfd, 0xe2, 0x1e, 0x7e, 0x1c, 0x63, 0x4f, 0x47, 0xdb, 0xe4,
++  0xc4, 0x51, 0x04, 0x03, 0x9a, 0x48, 0x35, 0x6e, 0x30, 0x0d, 0x06, 0x09,
++  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03,
++  0x82, 0x01, 0x01, 0x00, 0x65, 0x82, 0xd5, 0x88, 0x30, 0xe2, 0x2c, 0x47,
++  0xf3, 0x31, 0x39, 0xa1, 0x75, 0x9a, 0xb0, 0x8a, 0x6c, 0x4b, 0xac, 0xdf,
++  0x09, 0x7b, 0x90, 0xb6, 0x9e, 0x76, 0x62, 0x94, 0xc1, 0x3a, 0x99, 0x49,
++  0x68, 0x29, 0x47, 0x42, 0xc3, 0x06, 0xcb, 0x88, 0x75, 0xe6, 0x79, 0x13,
++  0x8c, 0x4b, 0x49, 0x6a, 0xb5, 0x56, 0x95, 0xc0, 0x42, 0x21, 0x9b, 0xd4,
++  0x61, 0xd0, 0x02, 0x41, 0xdd, 0x20, 0x61, 0xe5, 0x91, 0xdf, 0x75, 0x00,
++  0x25, 0x0e, 0x99, 0x65, 0x5c, 0x54, 0x49, 0x32, 0xa3, 0xe2, 0xcd, 0xa1,
++  0x5f, 0x40, 0xf3, 0xc5, 0x81, 0xd9, 0x3c, 0xa3, 0x63, 0x5a, 0x38, 0x79,
++  0xab, 0x77, 0x98, 0xde, 0x8f, 0x4e, 0x9e, 0x26, 0xbc, 0x4e, 0x80, 0x9e,
++  0x8f, 0xbe, 0xf1, 0x00, 0xb3, 0x78, 0xb9, 0x4b, 0x1d, 0xc7, 0xa4, 0x83,
++  0x59, 0x56, 0x11, 0xd1, 0x11, 0x1e, 0x50, 0x39, 0xd5, 0x78, 0x14, 0xf3,
++  0xb9, 0x1d, 0xda, 0xe4, 0xc4, 0x63, 0x74, 0x26, 0xab, 0xa3, 0xfd, 0x9d,
++  0x58, 0xa2, 0xee, 0x7b, 0x28, 0x34, 0xa3, 0xbe, 0x85, 0x7e, 0xaa, 0x97,
++  0xb7, 0x5b, 0x9d, 0xa9, 0x4d, 0x96, 0xdb, 0x6b, 0x21, 0xe1, 0x96, 0x5d,
++  0xc7, 0xad, 0x23, 0x03, 0x9a, 0x16, 0xdb, 0xa4, 0x1f, 0x63, 0xef, 0xaf,
++  0x1e, 0x4f, 0xf8, 0x27, 0xdc, 0x4b, 0xfc, 0x2b, 0x68, 0x2e, 0xa0, 0xd3,
++  0xae, 0xf2, 0xce, 0xf5, 0xfc, 0x97, 0x92, 0xd2, 0x29, 0x0f, 0x4f, 0x4b,
++  0x29, 0xeb, 0x06, 0xcb, 0xf8, 0x21, 0x6e, 0xbc, 0x8b, 0x5c, 0xc5, 0xc9,
++  0xf7, 0xe2, 0x7c, 0x47, 0xcd, 0x43, 0x98, 0xc4, 0xa3, 0x9a, 0xd7, 0x3e,
++  0xdc, 0x01, 0x13, 0x28, 0x96, 0xc4, 0x60, 0x83, 0xe2, 0x79, 0xa1, 0x46,
++  0xef, 0xf5, 0xa4, 0x7b, 0x00, 0xe3, 0x3d, 0x7d, 0xbc, 0xa8, 0x98, 0x49,
++  0xa8, 0xcf, 0x3b, 0x41, 0xb6, 0x09, 0x97, 0x07
++};
++unsigned int certificate_der_len = 908;
++
++unsigned char hi_signed[] = {
++  0x68, 0x69, 0x0a, 0x30, 0x82, 0x01, 0xc0, 0x06, 0x09, 0x2a, 0x86, 0x48,
++  0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x01, 0xb1, 0x30, 0x82,
++  0x01, 0xad, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
++  0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
++  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x01,
++  0x8a, 0x30, 0x82, 0x01, 0x86, 0x02, 0x01, 0x01, 0x30, 0x61, 0x30, 0x49,
++  0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1f, 0x47,
++  0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64,
++  0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54,
++  0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x09,
++  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0e, 0x64,
++  0x6a, 0x61, 0x40, 0x61, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x2e, 0x6e, 0x65,
++  0x74, 0x02, 0x14, 0x25, 0x2e, 0xb8, 0xfd, 0x12, 0x62, 0x2e, 0xcd, 0x5d,
++  0xa7, 0x53, 0xd2, 0x0b, 0xc2, 0x61, 0x7c, 0x14, 0xe0, 0x0f, 0x5c, 0x30,
++  0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
++  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
++  0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x00, 0xc7, 0x69, 0x35, 0x21, 0x66,
++  0x4d, 0x50, 0xd4, 0x73, 0xde, 0xbd, 0x3a, 0xf6, 0x45, 0xe3, 0xe4, 0xd0,
++  0xb6, 0xa1, 0xe7, 0xc0, 0xa2, 0xc9, 0xf4, 0xf0, 0x05, 0x8c, 0xa4, 0x16,
++  0x9e, 0x81, 0x0d, 0x21, 0x68, 0xf3, 0xfe, 0x03, 0x96, 0x77, 0x31, 0x69,
++  0x01, 0xd8, 0x26, 0xd9, 0x48, 0x95, 0xcf, 0xd1, 0x17, 0xb1, 0x0b, 0x6b,
++  0x2c, 0xf1, 0xb0, 0xab, 0x65, 0x65, 0x56, 0xf8, 0x0c, 0xa7, 0xf7, 0xbb,
++  0xf6, 0x5a, 0x55, 0x98, 0x14, 0x07, 0x8d, 0x2a, 0xbc, 0x16, 0x48, 0x94,
++  0xab, 0x2f, 0x85, 0x97, 0x90, 0x51, 0x78, 0xa0, 0xda, 0x60, 0xb5, 0x41,
++  0x4b, 0xe8, 0x78, 0xc5, 0xa6, 0x04, 0x9d, 0x54, 0x2a, 0x85, 0xfd, 0x86,
++  0x0b, 0x6d, 0xc2, 0xd2, 0xad, 0x07, 0xff, 0x16, 0x42, 0x82, 0xe3, 0x5c,
++  0xaa, 0x22, 0x59, 0x78, 0x92, 0xea, 0x94, 0xc3, 0x41, 0xb7, 0xa1, 0x86,
++  0x44, 0xea, 0xd1, 0xdb, 0xe5, 0xac, 0x30, 0x32, 0xfb, 0x7d, 0x3f, 0xf7,
++  0x8b, 0x11, 0x7f, 0x80, 0x3b, 0xe5, 0xc7, 0x82, 0x0f, 0x92, 0x07, 0x14,
++  0x66, 0x01, 0x6e, 0x85, 0xab, 0x3a, 0x14, 0xcf, 0x76, 0xd1, 0x7e, 0x14,
++  0x85, 0xca, 0x01, 0x73, 0x72, 0x38, 0xdc, 0xde, 0x30, 0x5c, 0xfb, 0xc0,
++  0x3d, 0x93, 0xef, 0x9c, 0xbc, 0xf8, 0xcc, 0xd2, 0xbf, 0x47, 0xec, 0xf8,
++  0x88, 0x9b, 0xe1, 0x43, 0xbe, 0xa7, 0x47, 0x96, 0xb6, 0x5d, 0x46, 0x0e,
++  0x7a, 0x78, 0x38, 0x19, 0xbc, 0xb5, 0xbc, 0x9b, 0x3c, 0x39, 0x92, 0x70,
++  0x0d, 0x9d, 0x8a, 0x35, 0xaf, 0xb4, 0x9e, 0xf4, 0xef, 0xc1, 0xb8, 0x25,
++  0xd0, 0x14, 0x91, 0xd6, 0xc2, 0xb6, 0xc7, 0x3c, 0x72, 0x91, 0x0f, 0xad,
++  0xde, 0xb2, 0x36, 0xf8, 0x4e, 0x59, 0xd4, 0xa4, 0x21, 0x9f, 0x03, 0x95,
++  0x48, 0x01, 0xb4, 0x05, 0xc3, 0x39, 0x60, 0x51, 0x08, 0xd0, 0xbe, 0x00,
++  0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc4, 0x7e,
++  0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61,
++  0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65,
++  0x64, 0x7e, 0x0a
++};
++unsigned int hi_signed_len = 495;
++
++unsigned char hj_signed[] = {
++  0x68, 0x6a, 0x0a, 0x30, 0x82, 0x01, 0xc0, 0x06, 0x09, 0x2a, 0x86, 0x48,
++  0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x01, 0xb1, 0x30, 0x82,
++  0x01, 0xad, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
++  0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
++  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x01,
++  0x8a, 0x30, 0x82, 0x01, 0x86, 0x02, 0x01, 0x01, 0x30, 0x61, 0x30, 0x49,
++  0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1f, 0x47,
++  0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64,
++  0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54,
++  0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x09,
++  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0e, 0x64,
++  0x6a, 0x61, 0x40, 0x61, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x2e, 0x6e, 0x65,
++  0x74, 0x02, 0x14, 0x25, 0x2e, 0xb8, 0xfd, 0x12, 0x62, 0x2e, 0xcd, 0x5d,
++  0xa7, 0x53, 0xd2, 0x0b, 0xc2, 0x61, 0x7c, 0x14, 0xe0, 0x0f, 0x5c, 0x30,
++  0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
++  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
++  0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x00, 0xc7, 0x69, 0x35, 0x21, 0x66,
++  0x4d, 0x50, 0xd4, 0x73, 0xde, 0xbd, 0x3a, 0xf6, 0x45, 0xe3, 0xe4, 0xd0,
++  0xb6, 0xa1, 0xe7, 0xc0, 0xa2, 0xc9, 0xf4, 0xf0, 0x05, 0x8c, 0xa4, 0x16,
++  0x9e, 0x81, 0x0d, 0x21, 0x68, 0xf3, 0xfe, 0x03, 0x96, 0x77, 0x31, 0x69,
++  0x01, 0xd8, 0x26, 0xd9, 0x48, 0x95, 0xcf, 0xd1, 0x17, 0xb1, 0x0b, 0x6b,
++  0x2c, 0xf1, 0xb0, 0xab, 0x65, 0x65, 0x56, 0xf8, 0x0c, 0xa7, 0xf7, 0xbb,
++  0xf6, 0x5a, 0x55, 0x98, 0x14, 0x07, 0x8d, 0x2a, 0xbc, 0x16, 0x48, 0x94,
++  0xab, 0x2f, 0x85, 0x97, 0x90, 0x51, 0x78, 0xa0, 0xda, 0x60, 0xb5, 0x41,
++  0x4b, 0xe8, 0x78, 0xc5, 0xa6, 0x04, 0x9d, 0x54, 0x2a, 0x85, 0xfd, 0x86,
++  0x0b, 0x6d, 0xc2, 0xd2, 0xad, 0x07, 0xff, 0x16, 0x42, 0x82, 0xe3, 0x5c,
++  0xaa, 0x22, 0x59, 0x78, 0x92, 0xea, 0x94, 0xc3, 0x41, 0xb7, 0xa1, 0x86,
++  0x44, 0xea, 0xd1, 0xdb, 0xe5, 0xac, 0x30, 0x32, 0xfb, 0x7d, 0x3f, 0xf7,
++  0x8b, 0x11, 0x7f, 0x80, 0x3b, 0xe5, 0xc7, 0x82, 0x0f, 0x92, 0x07, 0x14,
++  0x66, 0x01, 0x6e, 0x85, 0xab, 0x3a, 0x14, 0xcf, 0x76, 0xd1, 0x7e, 0x14,
++  0x85, 0xca, 0x01, 0x73, 0x72, 0x38, 0xdc, 0xde, 0x30, 0x5c, 0xfb, 0xc0,
++  0x3d, 0x93, 0xef, 0x9c, 0xbc, 0xf8, 0xcc, 0xd2, 0xbf, 0x47, 0xec, 0xf8,
++  0x88, 0x9b, 0xe1, 0x43, 0xbe, 0xa7, 0x47, 0x96, 0xb6, 0x5d, 0x46, 0x0e,
++  0x7a, 0x78, 0x38, 0x19, 0xbc, 0xb5, 0xbc, 0x9b, 0x3c, 0x39, 0x92, 0x70,
++  0x0d, 0x9d, 0x8a, 0x35, 0xaf, 0xb4, 0x9e, 0xf4, 0xef, 0xc1, 0xb8, 0x25,
++  0xd0, 0x14, 0x91, 0xd6, 0xc2, 0xb6, 0xc7, 0x3c, 0x72, 0x91, 0x0f, 0xad,
++  0xde, 0xb2, 0x36, 0xf8, 0x4e, 0x59, 0xd4, 0xa4, 0x21, 0x9f, 0x03, 0x95,
++  0x48, 0x01, 0xb4, 0x05, 0xc3, 0x39, 0x60, 0x51, 0x08, 0xd0, 0xbe, 0x00,
++  0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc4, 0x7e,
++  0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61,
++  0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65,
++  0x64, 0x7e, 0x0a
++};
++unsigned int hj_signed_len = 495;
++
++unsigned char hi_signed_sha256[] = {
++  0x68, 0x69, 0x0a, 0x30, 0x82, 0x01, 0xc0, 0x06, 0x09, 0x2a, 0x86, 0x48,
++  0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x01, 0xb1, 0x30, 0x82,
++  0x01, 0xad, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
++  0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x0b, 0x06, 0x09,
++  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x01,
++  0x8a, 0x30, 0x82, 0x01, 0x86, 0x02, 0x01, 0x01, 0x30, 0x61, 0x30, 0x49,
++  0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1f, 0x47,
++  0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64,
++  0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54,
++  0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x09,
++  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0e, 0x64,
++  0x6a, 0x61, 0x40, 0x61, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x2e, 0x6e, 0x65,
++  0x74, 0x02, 0x14, 0x25, 0x2e, 0xb8, 0xfd, 0x12, 0x62, 0x2e, 0xcd, 0x5d,
++  0xa7, 0x53, 0xd2, 0x0b, 0xc2, 0x61, 0x7c, 0x14, 0xe0, 0x0f, 0x5c, 0x30,
++  0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
++  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
++  0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x00, 0x7b, 0x5e, 0x82, 0x1d, 0x21,
++  0xb6, 0x40, 0xd3, 0x33, 0x79, 0xa7, 0x52, 0x2b, 0xfc, 0x46, 0x51, 0x26,
++  0xfe, 0x0f, 0x81, 0x90, 0x81, 0xab, 0x57, 0x5e, 0xf6, 0x45, 0x41, 0xa3,
++  0x7b, 0x48, 0xdd, 0xd6, 0x59, 0x60, 0x51, 0x31, 0x14, 0x14, 0x7b, 0xb4,
++  0x55, 0x7b, 0x4d, 0xfe, 0x09, 0x7a, 0x5d, 0xae, 0xc4, 0x58, 0x50, 0x80,
++  0x75, 0xf2, 0x23, 0x20, 0x62, 0xe3, 0x7c, 0x26, 0x1d, 0x2a, 0x4d, 0x9f,
++  0x89, 0xf0, 0x4f, 0x95, 0x8a, 0x80, 0x6e, 0x1a, 0xea, 0x87, 0xdb, 0x1f,
++  0xf3, 0xda, 0x04, 0x91, 0x37, 0xea, 0x0a, 0xfb, 0x6c, 0xc9, 0x3d, 0x73,
++  0xf9, 0x58, 0x7c, 0x15, 0x6b, 0xa2, 0x52, 0x5a, 0x97, 0xff, 0xd6, 0xb0,
++  0xf1, 0xbf, 0xa5, 0x04, 0x6d, 0x91, 0xc1, 0x54, 0x05, 0xdc, 0x7f, 0x5d,
++  0x19, 0xaf, 0x55, 0xec, 0x51, 0xfb, 0x66, 0x0a, 0xa4, 0x4e, 0x96, 0x47,
++  0x43, 0x54, 0x7c, 0x64, 0xa8, 0xaa, 0xb4, 0x90, 0x02, 0xf3, 0xa7, 0x0b,
++  0xb7, 0xbf, 0x06, 0xdb, 0x5e, 0x9c, 0x32, 0x6d, 0x45, 0x14, 0x1c, 0xaf,
++  0x46, 0x30, 0x08, 0x55, 0x49, 0x78, 0xfa, 0x57, 0xda, 0x3d, 0xf5, 0xa0,
++  0xef, 0x11, 0x0a, 0x81, 0x0d, 0x82, 0xcd, 0xaf, 0xdb, 0xda, 0x0e, 0x1a,
++  0x44, 0xd1, 0xee, 0xc4, 0xb8, 0xde, 0x97, 0xb4, 0xda, 0xb4, 0x8b, 0x4f,
++  0x58, 0x24, 0x59, 0xc0, 0xe0, 0x08, 0x97, 0x14, 0x68, 0xbe, 0x31, 0x09,
++  0x5e, 0x67, 0x45, 0xf0, 0xcb, 0x81, 0x4f, 0x17, 0x44, 0x61, 0xe0, 0xe2,
++  0xf0, 0xfc, 0x1e, 0xb9, 0x73, 0xaf, 0x42, 0xff, 0x33, 0xde, 0x61, 0x6b,
++  0x7f, 0xc2, 0x69, 0x0d, 0x66, 0x54, 0xae, 0xf6, 0xde, 0x20, 0x47, 0x44,
++  0x9b, 0x73, 0xd1, 0x07, 0x6e, 0x77, 0x37, 0x0a, 0xbb, 0x7f, 0xa0, 0x93,
++  0x2d, 0x8d, 0x44, 0xba, 0xe2, 0xdd, 0x34, 0x32, 0xd7, 0x56, 0x71, 0x00,
++  0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc4, 0x7e,
++  0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61,
++  0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65,
++  0x64, 0x7e, 0x0a
++};
++unsigned int hi_signed_sha256_len = 495;
++
++unsigned char short_msg[] = {
++  0x68, 0x69, 0x0a
++};
++unsigned int short_msg_len = 3;
++
++unsigned char unsigned_msg[] = {
++  0x53, 0x65, 0x64, 0x20, 0x75, 0x74, 0x20, 0x70, 0x65, 0x72, 0x73, 0x70,
++  0x69, 0x63, 0x69, 0x61, 0x74, 0x69, 0x73, 0x20, 0x75, 0x6e, 0x64, 0x65,
++  0x20, 0x6f, 0x6d, 0x6e, 0x69, 0x73, 0x20, 0x69, 0x73, 0x74, 0x65, 0x20,
++  0x6e, 0x61, 0x74, 0x75, 0x73, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20,
++  0x73, 0x69, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74,
++  0x65, 0x6d, 0x20, 0x61, 0x63, 0x63, 0x75, 0x73, 0x61, 0x6e, 0x74, 0x69,
++  0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, 0x71, 0x75,
++  0x65, 0x20, 0x6c, 0x61, 0x75, 0x64, 0x61, 0x6e, 0x74, 0x69, 0x75, 0x6d,
++  0x2c, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6d, 0x20, 0x72, 0x65, 0x6d, 0x20,
++  0x61, 0x70, 0x65, 0x72, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x65, 0x61, 0x71,
++  0x75, 0x65, 0x20, 0x69, 0x70, 0x73, 0x61, 0x20, 0x71, 0x75, 0x61, 0x65,
++  0x20, 0x61, 0x62, 0x20, 0x69, 0x6c, 0x6c, 0x6f, 0x20, 0x69, 0x6e, 0x76,
++  0x65, 0x6e, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x76, 0x65, 0x72, 0x69, 0x74,
++  0x61, 0x74, 0x69, 0x73, 0x20, 0x65, 0x74, 0x20, 0x71, 0x75, 0x61, 0x73,
++  0x69, 0x20, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x6f,
++  0x20, 0x62, 0x65, 0x61, 0x74, 0x61, 0x65, 0x20, 0x76, 0x69, 0x74, 0x61,
++  0x65, 0x20, 0x64, 0x69, 0x63, 0x74, 0x61, 0x20, 0x73, 0x75, 0x6e, 0x74,
++  0x20, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x62, 0x6f, 0x2e, 0x20,
++  0x4e, 0x65, 0x6d, 0x6f, 0x20, 0x65, 0x6e, 0x69, 0x6d, 0x20, 0x69, 0x70,
++  0x73, 0x61, 0x6d, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74,
++  0x65, 0x6d, 0x20, 0x71, 0x75, 0x69, 0x61, 0x20, 0x76, 0x6f, 0x6c, 0x75,
++  0x70, 0x74, 0x61, 0x73, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x73, 0x70,
++  0x65, 0x72, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x20, 0x61, 0x75, 0x74, 0x20,
++  0x6f, 0x64, 0x69, 0x74, 0x20, 0x61, 0x75, 0x74, 0x20, 0x66, 0x75, 0x67,
++  0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64, 0x20, 0x71, 0x75, 0x69, 0x61,
++  0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x75, 0x6e, 0x74, 0x75,
++  0x72, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f,
++  0x72, 0x65, 0x73, 0x20, 0x65, 0x6f, 0x73, 0x20, 0x71, 0x75, 0x69, 0x20,
++  0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x20, 0x76, 0x6f, 0x6c, 0x75,
++  0x70, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x20, 0x73, 0x65, 0x71, 0x75, 0x69,
++  0x20, 0x6e, 0x65, 0x73, 0x63, 0x69, 0x75, 0x6e, 0x74, 0x2e, 0x20, 0x4e,
++  0x65, 0x71, 0x75, 0x65, 0x20, 0x70, 0x6f, 0x72, 0x72, 0x6f, 0x20, 0x71,
++  0x75, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x65, 0x73, 0x74, 0x2c,
++  0x20, 0x71, 0x75, 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d,
++  0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x71, 0x75, 0x69, 0x61, 0x20,
++  0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d,
++  0x65, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65,
++  0x74, 0x75, 0x72, 0x2c, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, 0x73, 0x63,
++  0x69, 0x20, 0x76, 0x65, 0x6c, 0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64,
++  0x20, 0x71, 0x75, 0x69, 0x61, 0x20, 0x6e, 0x6f, 0x6e, 0x20, 0x6e, 0x75,
++  0x6d, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x65, 0x69, 0x75, 0x73, 0x20, 0x6d,
++  0x6f, 0x64, 0x69, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x20,
++  0x69, 0x6e, 0x63, 0x69, 0x64, 0x75, 0x6e, 0x74, 0x20, 0x75, 0x74, 0x20,
++  0x6c, 0x61, 0x62, 0x6f, 0x72, 0x65, 0x20, 0x65, 0x74, 0x20, 0x64, 0x6f,
++  0x6c, 0x6f, 0x72, 0x65, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x61, 0x6d, 0x20,
++  0x61, 0x6c, 0x69, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x71, 0x75, 0x61, 0x65,
++  0x72, 0x61, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74,
++  0x65, 0x6d, 0x2e, 0x20, 0x55, 0x74, 0x20, 0x65, 0x6e, 0x69, 0x6d, 0x20,
++  0x61, 0x64, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x61, 0x20, 0x76, 0x65,
++  0x6e, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x71, 0x75, 0x69, 0x73, 0x20, 0x6e,
++  0x6f, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x20, 0x65, 0x78, 0x65, 0x72, 0x63,
++  0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x6d, 0x20, 0x75, 0x6c,
++  0x6c, 0x61, 0x6d, 0x20, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x69, 0x73,
++  0x20, 0x73, 0x75, 0x73, 0x63, 0x69, 0x70, 0x69, 0x74, 0x20, 0x6c, 0x61,
++  0x62, 0x6f, 0x72, 0x69, 0x6f, 0x73, 0x61, 0x6d, 0x2c, 0x20, 0x6e, 0x69,
++  0x73, 0x69, 0x20, 0x75, 0x74, 0x20, 0x61, 0x6c, 0x69, 0x71, 0x75, 0x69,
++  0x64, 0x20, 0x65, 0x78, 0x20, 0x65, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d,
++  0x6f, 0x64, 0x69, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x61,
++  0x74, 0x75, 0x72, 0x3f, 0x20, 0x51, 0x75, 0x69, 0x73, 0x20, 0x61, 0x75,
++  0x74, 0x65, 0x6d, 0x20, 0x76, 0x65, 0x6c, 0x20, 0x65, 0x75, 0x6d, 0x20,
++  0x69, 0x75, 0x72, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x68, 0x65,
++  0x6e, 0x64, 0x65, 0x72, 0x69, 0x74, 0x20, 0x71, 0x75, 0x69, 0x20, 0x69,
++  0x6e, 0x20, 0x65, 0x61, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61,
++  0x74, 0x65, 0x20, 0x76, 0x65, 0x6c, 0x69, 0x74, 0x20, 0x65, 0x73, 0x73,
++  0x65, 0x20, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x6e, 0x69, 0x68, 0x69, 0x6c,
++  0x20, 0x6d, 0x6f, 0x6c, 0x65, 0x73, 0x74, 0x69, 0x61, 0x65, 0x20, 0x63,
++  0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x61, 0x74, 0x75, 0x72, 0x2c, 0x20,
++  0x76, 0x65, 0x6c, 0x20, 0x69, 0x6c, 0x6c, 0x75, 0x6d, 0x20, 0x71, 0x75,
++  0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x65, 0x75,
++  0x6d, 0x20, 0x66, 0x75, 0x67, 0x69, 0x61, 0x74, 0x20, 0x71, 0x75, 0x6f,
++  0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x73, 0x20, 0x6e, 0x75,
++  0x6c, 0x6c, 0x61, 0x20, 0x70, 0x61, 0x72, 0x69, 0x61, 0x74, 0x75, 0x72,
++  0x3f, 0x0a
++};
++unsigned int unsigned_msg_len = 866;
++
++unsigned char certificate2_der[] = {
++  0x30, 0x82, 0x05, 0x52, 0x30, 0x82, 0x03, 0x3a, 0xa0, 0x03, 0x02, 0x01,
++  0x02, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5,
++  0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30,
++  0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
++  0x05, 0x00, 0x30, 0x3a, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04,
++  0x03, 0x0c, 0x2f, 0x47, 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20,
++  0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20,
++  0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
++  0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
++  0x74, 0x79, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x37, 0x32, 0x38,
++  0x31, 0x33, 0x32, 0x34, 0x32, 0x39, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x32,
++  0x30, 0x30, 0x37, 0x30, 0x34, 0x31, 0x33, 0x32, 0x34, 0x32, 0x39, 0x5a,
++  0x30, 0x2b, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
++  0x20, 0x47, 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65,
++  0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69,
++  0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x65, 0x79, 0x30, 0x82, 0x02,
++  0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
++  0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02,
++  0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xb0, 0x2f, 0x50, 0x01, 0x9c, 0x0e,
++  0xd6, 0x8c, 0x07, 0xca, 0xc1, 0xcf, 0xbc, 0x03, 0xdd, 0xd3, 0xfa, 0xe3,
++  0x4f, 0x71, 0xc1, 0x30, 0xaa, 0x09, 0x96, 0xe4, 0xd0, 0x6c, 0x42, 0x93,
++  0xdb, 0x35, 0xf6, 0x7e, 0x1b, 0x67, 0xc0, 0xc2, 0x2d, 0x5b, 0xec, 0xca,
++  0x35, 0x06, 0x32, 0x6c, 0x7b, 0x2c, 0xd3, 0x71, 0x2b, 0xe9, 0x7a, 0x19,
++  0xd1, 0xf2, 0xa0, 0x7f, 0xd7, 0x4d, 0x6e, 0x28, 0xbb, 0xae, 0x49, 0x4a,
++  0xbc, 0xea, 0x47, 0x67, 0xb8, 0x36, 0xa6, 0xf5, 0x0d, 0x0e, 0x20, 0x14,
++  0x0c, 0x66, 0x67, 0x28, 0xb5, 0x97, 0x8b, 0x1f, 0x5e, 0x32, 0x06, 0x29,
++  0x9c, 0x99, 0x92, 0x0f, 0x73, 0xac, 0xfd, 0xd2, 0x1d, 0xf2, 0xa8, 0x55,
++  0x9d, 0x1b, 0xd8, 0x3d, 0xb0, 0x76, 0x9a, 0xb6, 0x6c, 0x9f, 0x62, 0x37,
++  0x2f, 0xc0, 0xef, 0x44, 0xb3, 0x0d, 0x4a, 0x3e, 0x4f, 0x7d, 0xbd, 0xdb,
++  0xd8, 0x75, 0x5f, 0x68, 0xe3, 0xf0, 0xec, 0x82, 0x66, 0x7c, 0x31, 0x70,
++  0xa9, 0xa1, 0x6f, 0x38, 0x9f, 0xdf, 0xf5, 0xf0, 0x7d, 0x23, 0x9d, 0x34,
++  0xa5, 0x85, 0xd3, 0xdf, 0x68, 0x41, 0xfc, 0x4f, 0x89, 0x45, 0x3c, 0x24,
++  0x81, 0xa6, 0xf2, 0x3c, 0x02, 0x26, 0x09, 0x48, 0xdd, 0xfe, 0x4b, 0xb6,
++  0x66, 0xbf, 0x8f, 0xe5, 0x5f, 0xf0, 0x5d, 0x8a, 0x61, 0x2e, 0x5f, 0x9f,
++  0x80, 0xd9, 0xd5, 0xe6, 0x41, 0xd8, 0x10, 0x5e, 0x7a, 0xc6, 0xdb, 0x89,
++  0xc7, 0xca, 0x6c, 0x5b, 0xb1, 0x4e, 0x7d, 0x0c, 0x03, 0xfd, 0x50, 0xca,
++  0xbf, 0xbb, 0xe2, 0x69, 0x4b, 0x4e, 0xc2, 0x3d, 0x75, 0xfa, 0xd1, 0xcc,
++  0xd6, 0xf9, 0x39, 0xb9, 0xdc, 0x53, 0xad, 0x62, 0xfb, 0x1b, 0x94, 0x26,
++  0x7f, 0x21, 0x54, 0x5c, 0xb7, 0xdc, 0xe7, 0x96, 0x8c, 0xce, 0x75, 0xe0,
++  0x17, 0x01, 0x3a, 0x3c, 0x77, 0x6e, 0xa4, 0x8b, 0x7a, 0x83, 0x28, 0x7a,
++  0xf7, 0xb0, 0x5f, 0xfc, 0x7f, 0x2d, 0x2e, 0xec, 0xf5, 0xeb, 0x9c, 0x63,
++  0x74, 0xd0, 0xe5, 0xdc, 0x19, 0xe4, 0x71, 0xc5, 0x4a, 0x8a, 0x54, 0xa4,
++  0xe0, 0x7d, 0x4e, 0xbf, 0x53, 0x30, 0xaf, 0xd0, 0xeb, 0x96, 0xc3, 0xbb,
++  0x65, 0xf7, 0x67, 0xf5, 0xae, 0xd3, 0x96, 0xf2, 0x63, 0xc8, 0x69, 0xf7,
++  0x47, 0xcb, 0x27, 0x79, 0xe1, 0xff, 0x2f, 0x68, 0xdf, 0x1e, 0xb3, 0xb8,
++  0x0c, 0xc5, 0x58, 0x73, 0xcc, 0xfe, 0x8c, 0xda, 0x4e, 0x3b, 0x01, 0x04,
++  0xcd, 0xcb, 0xb8, 0x3e, 0x06, 0xfd, 0x4c, 0x0a, 0x9f, 0x5e, 0x76, 0x8c,
++  0x0c, 0x83, 0x75, 0x09, 0x08, 0xb2, 0xdb, 0xf4, 0x49, 0x4e, 0xa0, 0xf2,
++  0x0c, 0x7b, 0x87, 0x38, 0x9e, 0x22, 0x67, 0xbd, 0xd1, 0x97, 0x57, 0x24,
++  0xf1, 0x46, 0x07, 0xf9, 0xd2, 0x1b, 0xec, 0x25, 0x5e, 0x67, 0xd9, 0x66,
++  0x23, 0x1b, 0xd3, 0xe4, 0xaa, 0xec, 0x88, 0xf0, 0x7e, 0x15, 0x83, 0x51,
++  0x31, 0x67, 0x51, 0x76, 0x5f, 0x55, 0xd7, 0x36, 0xdf, 0x4a, 0x84, 0x0b,
++  0x6f, 0x5c, 0xbb, 0x5b, 0x8f, 0x37, 0x23, 0x7f, 0xf8, 0x17, 0x84, 0xa2,
++  0x70, 0x20, 0x07, 0x0c, 0x90, 0x3a, 0x04, 0xfd, 0xf0, 0x08, 0x4a, 0xb1,
++  0x16, 0x0f, 0xe6, 0xf6, 0x40, 0x51, 0x83, 0xd2, 0x87, 0x40, 0x9c, 0x1c,
++  0x9f, 0x13, 0x38, 0x17, 0xd3, 0x34, 0x58, 0xad, 0x05, 0x71, 0xa0, 0x73,
++  0xca, 0x40, 0xa6, 0xa4, 0x81, 0x02, 0xee, 0xa8, 0x72, 0x41, 0xa1, 0x41,
++  0x18, 0x64, 0x8a, 0x86, 0x8a, 0x5d, 0xe6, 0x4f, 0x0a, 0xc5, 0x95, 0x98,
++  0xf9, 0x78, 0xfe, 0x19, 0x0d, 0xc9, 0xb3, 0x89, 0xc1, 0x2b, 0x09, 0xbe,
++  0xf1, 0xd2, 0x04, 0x5d, 0xcc, 0x28, 0xf5, 0x4b, 0xd2, 0x20, 0x4f, 0xc5,
++  0x41, 0x9d, 0x8c, 0x85, 0xd8, 0xb0, 0x68, 0x5e, 0xc1, 0x0c, 0xb7, 0x24,
++  0x4d, 0x67, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30,
++  0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30,
++  0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02,
++  0x07, 0x80, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
++  0x14, 0xac, 0xf5, 0x47, 0x17, 0xd9, 0x7d, 0xc1, 0xb1, 0xc4, 0x41, 0xe1,
++  0x41, 0x60, 0xcb, 0x37, 0x11, 0x60, 0x28, 0x78, 0x5f, 0x30, 0x1f, 0x06,
++  0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x21, 0x94,
++  0xfb, 0xf9, 0xb2, 0x43, 0xe9, 0x33, 0xd7, 0x50, 0x7d, 0xc7, 0x37, 0xdb,
++  0xd5, 0x82, 0x5a, 0x4e, 0xbe, 0x1b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
++  0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02,
++  0x01, 0x00, 0x96, 0x70, 0x65, 0x26, 0x42, 0xf8, 0xdc, 0x69, 0xde, 0xcf,
++  0x41, 0x3a, 0x2e, 0x7f, 0x5b, 0xf1, 0xf9, 0x3b, 0x9b, 0xd2, 0x4e, 0x64,
++  0x48, 0x81, 0xe4, 0x5d, 0x1e, 0x22, 0xce, 0x68, 0x63, 0x62, 0xe5, 0x1b,
++  0x9b, 0xf2, 0xc7, 0x12, 0xda, 0x1e, 0x9b, 0x90, 0x84, 0x79, 0x48, 0x12,
++  0xe6, 0x21, 0x6f, 0x2f, 0x7e, 0x18, 0x77, 0xdb, 0x8c, 0xc4, 0xd1, 0x0d,
++  0x91, 0xbf, 0x39, 0x22, 0x0f, 0x64, 0xcf, 0x25, 0x2e, 0x8c, 0x1f, 0x91,
++  0x81, 0xb5, 0xe9, 0x6c, 0x02, 0x3a, 0xf8, 0x07, 0xa2, 0x6f, 0x46, 0x5d,
++  0x7b, 0xfd, 0x43, 0xff, 0x41, 0x0f, 0xe2, 0x57, 0x1c, 0xbd, 0x48, 0x60,
++  0x53, 0x11, 0x48, 0x87, 0x88, 0x9d, 0x13, 0x82, 0x40, 0x68, 0x44, 0x2c,
++  0xc6, 0xc8, 0x95, 0x27, 0x4f, 0xb6, 0xb9, 0x4a, 0x22, 0x0a, 0xfd, 0xe4,
++  0x46, 0x8f, 0x35, 0x12, 0x98, 0x5a, 0x34, 0x6f, 0x2b, 0x57, 0x62, 0xa1,
++  0x4d, 0x8d, 0x79, 0x37, 0xe4, 0x6b, 0x8a, 0x32, 0x5b, 0xcb, 0xef, 0x79,
++  0x11, 0xed, 0xa7, 0xf8, 0x7a, 0x1c, 0xbd, 0x86, 0xdc, 0x0e, 0x2e, 0xfd,
++  0xd3, 0x51, 0xbb, 0x73, 0xad, 0x00, 0xa0, 0x1b, 0xf9, 0x1d, 0xd1, 0x4a,
++  0xe4, 0xd4, 0x02, 0x63, 0x2b, 0x39, 0x5f, 0x18, 0x08, 0x2f, 0x42, 0xb7,
++  0x23, 0x4b, 0x48, 0x46, 0x1f, 0x63, 0x87, 0xae, 0x6d, 0xd5, 0xdb, 0x60,
++  0xf8, 0x5f, 0xd3, 0x13, 0xec, 0xca, 0xdd, 0x60, 0x60, 0x79, 0x52, 0x70,
++  0x47, 0xae, 0x1d, 0x38, 0x78, 0x71, 0xcf, 0xb3, 0x04, 0x03, 0xbe, 0xba,
++  0x81, 0xba, 0x74, 0xb1, 0x30, 0x35, 0xdc, 0xea, 0x21, 0x4a, 0x9b, 0x70,
++  0xfb, 0xd6, 0x60, 0x59, 0x78, 0x0c, 0x4d, 0x39, 0x19, 0x1d, 0xe5, 0x75,
++  0xba, 0x07, 0xf4, 0x22, 0x37, 0x64, 0xb7, 0xf2, 0x9a, 0xc9, 0x11, 0x2d,
++  0x8e, 0x58, 0xa6, 0xcf, 0x83, 0xf1, 0xcb, 0x6c, 0x7f, 0x02, 0xbd, 0xda,
++  0x03, 0x92, 0xa9, 0x45, 0x24, 0x56, 0xc5, 0xbd, 0x41, 0xd1, 0x20, 0x86,
++  0xc0, 0xb6, 0xb7, 0xe8, 0xa7, 0xb2, 0x46, 0xf7, 0x8e, 0xa9, 0x38, 0x0e,
++  0x23, 0x77, 0x3c, 0x0d, 0x66, 0x83, 0x6a, 0x1a, 0x6b, 0x7f, 0x54, 0x11,
++  0x58, 0x0d, 0x4a, 0xb5, 0x74, 0x60, 0xca, 0xed, 0xff, 0x91, 0x47, 0xd9,
++  0x29, 0xe0, 0xaa, 0x8c, 0xa8, 0x8f, 0x10, 0x4c, 0x15, 0x7d, 0xce, 0x95,
++  0xf9, 0x87, 0x1e, 0x18, 0x38, 0x18, 0xfc, 0xcc, 0xaf, 0x91, 0x17, 0x3f,
++  0xfa, 0xf0, 0x8a, 0x09, 0x6f, 0xba, 0x4e, 0x53, 0xf7, 0xfa, 0x4f, 0x20,
++  0xa3, 0xf4, 0x4a, 0x5a, 0xde, 0x17, 0x1c, 0x29, 0x6a, 0x6f, 0x03, 0x48,
++  0xdf, 0xad, 0x4f, 0xe4, 0xbc, 0x71, 0xc4, 0x72, 0x32, 0x11, 0x84, 0xac,
++  0x09, 0xd2, 0x18, 0x44, 0x35, 0xf1, 0xcd, 0xaf, 0xa8, 0x98, 0xe0, 0x8b,
++  0xec, 0xa0, 0x83, 0x37, 0xc3, 0x35, 0x85, 0xd6, 0xd8, 0x1b, 0xe0, 0x75,
++  0xdc, 0xfd, 0xde, 0xc9, 0xeb, 0xd5, 0x18, 0x0f, 0xd3, 0x4c, 0x2f, 0x71,
++  0xdc, 0x48, 0xe3, 0x14, 0xeb, 0xda, 0x00, 0x24, 0x24, 0x9e, 0xa3, 0x8e,
++  0x3e, 0x08, 0x6f, 0x22, 0x24, 0xd6, 0xc4, 0x85, 0x8f, 0x68, 0x00, 0x4a,
++  0x82, 0x4c, 0x33, 0x6e, 0xa5, 0x35, 0x7b, 0xeb, 0x4b, 0xdc, 0xa0, 0xa6,
++  0x65, 0x6f, 0x5a, 0x7a, 0xdf, 0x8a, 0x01, 0x52, 0xa1, 0x6c, 0xff, 0x59,
++  0x22, 0x7f, 0xe1, 0x96, 0x1b, 0x19, 0xb8, 0xf9, 0x5d, 0x44, 0x9f, 0x91,
++  0x03, 0x3c, 0x3d, 0xa1, 0x2a, 0xb6, 0x5a, 0x51, 0xa0, 0xce, 0x4a, 0x88,
++  0x22, 0x72, 0x9c, 0xdc, 0xc0, 0x47, 0x76, 0x35, 0x84, 0x75, 0x9b, 0x87,
++  0x5c, 0xd3, 0xcf, 0xe7, 0xdd, 0xa3, 0x57, 0x14, 0xdf, 0x00, 0xfd, 0x19,
++  0x2a, 0x7d, 0x89, 0x27, 0x1c, 0x78, 0x97, 0x04, 0x58, 0x48
++};
++unsigned int certificate2_der_len = 1366;
++
++unsigned char hi_signed_2nd[] = {
++  0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb1, 0x06, 0x09, 0x2a, 0x86, 0x48,
++  0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa2, 0x30, 0x82,
++  0x02, 0x9e, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
++  0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
++  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02,
++  0x7b, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a,
++  0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47,
++  0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74,
++  0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74,
++  0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
++  0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14,
++  0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07,
++  0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09,
++  0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06,
++  0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
++  0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90,
++  0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99,
++  0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07,
++  0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe,
++  0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f,
++  0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f,
++  0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f,
++  0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53,
++  0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2,
++  0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0,
++  0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf,
++  0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05,
++  0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d,
++  0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d,
++  0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81,
++  0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36,
++  0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5,
++  0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc,
++  0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70,
++  0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94,
++  0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3,
++  0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20,
++  0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2,
++  0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac,
++  0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb,
++  0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49,
++  0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0,
++  0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb,
++  0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76,
++  0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5,
++  0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49,
++  0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b,
++  0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25,
++  0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e,
++  0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0,
++  0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98,
++  0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b,
++  0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83,
++  0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e,
++  0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a,
++  0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7,
++  0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26,
++  0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac,
++  0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xb5,
++  0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e,
++  0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64,
++  0x65, 0x64, 0x7e, 0x0a
++};
++unsigned int hi_signed_2nd_len = 736;
++
++unsigned char certificate_printable_der[] = {
++  0x30, 0x82, 0x03, 0x39, 0x30, 0x82, 0x02, 0x21, 0xa0, 0x03, 0x02, 0x01,
++  0x02, 0x02, 0x09, 0x00, 0xde, 0xf6, 0x22, 0xc4, 0xf2, 0xf1, 0x86, 0x02,
++  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
++  0x0b, 0x05, 0x00, 0x30, 0x2a, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55,
++  0x04, 0x03, 0x13, 0x1f, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20,
++  0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20,
++  0x43, 0x41, 0x20, 0x32, 0x20, 0x28, 0x62, 0x65, 0x74, 0x61, 0x29, 0x30,
++  0x1e, 0x17, 0x0d, 0x31, 0x34, 0x31, 0x30, 0x33, 0x31, 0x31, 0x34, 0x31,
++  0x39, 0x32, 0x33, 0x5a, 0x17, 0x0d, 0x33, 0x37, 0x31, 0x30, 0x32, 0x35,
++  0x31, 0x34, 0x31, 0x39, 0x32, 0x33, 0x5a, 0x30, 0x2f, 0x31, 0x2d, 0x30,
++  0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x52, 0x65, 0x64, 0x20,
++  0x48, 0x61, 0x74, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42,
++  0x6f, 0x6f, 0x74, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20,
++  0x33, 0x20, 0x28, 0x62, 0x65, 0x74, 0x61, 0x29, 0x30, 0x82, 0x01, 0x22,
++  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
++  0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
++  0x02, 0x82, 0x01, 0x01, 0x00, 0xbd, 0xda, 0xa1, 0xed, 0x8d, 0x8e, 0x15,
++  0x5c, 0xf8, 0x01, 0x77, 0x48, 0x4a, 0x60, 0x96, 0xf9, 0x27, 0xfa, 0xe2,
++  0xb1, 0x69, 0x0f, 0x51, 0x19, 0x52, 0x7e, 0xc4, 0x34, 0x8e, 0xe1, 0x9b,
++  0x9c, 0xa4, 0xb1, 0x5c, 0xd6, 0x81, 0x98, 0x78, 0xfe, 0xa9, 0xe5, 0x0b,
++  0x00, 0xba, 0x9c, 0x64, 0x7e, 0xc7, 0xcc, 0x72, 0xb1, 0x73, 0x4b, 0x11,
++  0x07, 0x52, 0xf0, 0x20, 0x96, 0x8b, 0x99, 0x39, 0xde, 0xdb, 0xfa, 0x3d,
++  0x45, 0xe2, 0x98, 0x7b, 0x0c, 0x41, 0xe4, 0x0c, 0xb5, 0x5d, 0x92, 0x74,
++  0x39, 0x96, 0xe1, 0x97, 0x97, 0xa1, 0xad, 0x2e, 0xcc, 0xd0, 0x1b, 0x4d,
++  0x9d, 0xbd, 0x3e, 0xa9, 0x36, 0x8e, 0xcc, 0xc7, 0x5f, 0x6a, 0x7d, 0x39,
++  0x5e, 0x0b, 0x8d, 0xca, 0xe4, 0x83, 0xe9, 0x3b, 0x5c, 0x86, 0x47, 0xd4,
++  0xba, 0x7d, 0x98, 0x26, 0xa1, 0xf4, 0xe8, 0x90, 0x6b, 0x0f, 0xf1, 0x6b,
++  0x8c, 0xe3, 0xa2, 0x80, 0x3c, 0x96, 0xf1, 0x0a, 0xb6, 0x66, 0xc0, 0x4b,
++  0x61, 0xf7, 0x74, 0xcd, 0xd3, 0x7b, 0x8e, 0x5e, 0x39, 0xda, 0x99, 0x20,
++  0x33, 0x93, 0xd3, 0xf0, 0x7f, 0xad, 0x35, 0xe9, 0x88, 0x8d, 0x9c, 0xbf,
++  0x65, 0xf1, 0x47, 0x02, 0xf9, 0x7c, 0xed, 0x27, 0x5f, 0x4a, 0x65, 0x3c,
++  0xcf, 0x5f, 0x0e, 0x88, 0x95, 0x74, 0xde, 0xfb, 0x9e, 0x2e, 0x91, 0x9b,
++  0x45, 0x37, 0xc8, 0x85, 0xff, 0xe3, 0x41, 0x70, 0xfe, 0xd5, 0xef, 0x0e,
++  0x82, 0x22, 0x08, 0xb7, 0x3b, 0x44, 0x3e, 0xdc, 0x5b, 0x7f, 0xba, 0xbf,
++  0xe6, 0x58, 0x9d, 0x02, 0x6e, 0x75, 0xbf, 0x50, 0xec, 0xcf, 0x3f, 0xa5,
++  0x91, 0x0a, 0xe2, 0x59, 0x2c, 0xc3, 0xe7, 0x05, 0x03, 0xe8, 0xf2, 0x6f,
++  0x2a, 0x04, 0x68, 0x9a, 0x31, 0x32, 0x8f, 0x04, 0x35, 0xcd, 0x1f, 0x34,
++  0xcc, 0x4f, 0x79, 0x5a, 0x99, 0x8d, 0x9d, 0x5c, 0xf5, 0x02, 0x03, 0x01,
++  0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d,
++  0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03,
++  0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x1d, 0x06,
++  0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x65, 0xc5, 0xbe, 0xca,
++  0xe6, 0x59, 0x6a, 0xfd, 0x6c, 0x71, 0xc4, 0xa7, 0x98, 0xc6, 0x25, 0x8d,
++  0x7b, 0x67, 0x05, 0xd0, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
++  0x18, 0x30, 0x16, 0x80, 0x14, 0x81, 0xf8, 0xee, 0x47, 0x5c, 0x3e, 0xed,
++  0xfb, 0xce, 0xa5, 0x84, 0xbe, 0xd7, 0xae, 0xdb, 0xd3, 0x7d, 0x64, 0xb3,
++  0x2a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
++  0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x66, 0x1e, 0x3d,
++  0x1d, 0x53, 0x33, 0xde, 0x4e, 0xc7, 0xc4, 0xf4, 0xdf, 0xda, 0x18, 0x19,
++  0x8a, 0xa9, 0xff, 0xe2, 0x63, 0x2b, 0xbe, 0xf2, 0x61, 0x63, 0xe2, 0xf6,
++  0xed, 0x47, 0x1a, 0x71, 0x02, 0xec, 0x2a, 0xef, 0x89, 0x77, 0xe3, 0xfd,
++  0x86, 0x69, 0xf1, 0x3f, 0x0d, 0xf9, 0x6e, 0xf9, 0x3b, 0xad, 0x26, 0x47,
++  0xb7, 0xf2, 0x0d, 0xad, 0x23, 0xa3, 0x67, 0x3b, 0xcb, 0x6d, 0x9e, 0x03,
++  0x0f, 0xbc, 0x69, 0x73, 0x9f, 0xd4, 0xa5, 0x0f, 0x6f, 0xf8, 0xab, 0x4d,
++  0x36, 0xd1, 0xe0, 0xe0, 0x5d, 0x20, 0x43, 0x90, 0xc4, 0x65, 0x61, 0x93,
++  0xe2, 0x0f, 0x51, 0x59, 0x0a, 0xf7, 0x88, 0x70, 0x57, 0xb9, 0x04, 0xa9,
++  0x32, 0x57, 0x9c, 0xb3, 0x57, 0x38, 0x8b, 0x8e, 0x46, 0xc8, 0x32, 0x6c,
++  0xb4, 0xf3, 0x96, 0x7f, 0x4b, 0xf0, 0x88, 0xf9, 0x7f, 0xe2, 0x71, 0xe1,
++  0x8b, 0xe2, 0x14, 0xf1, 0x4b, 0x25, 0x00, 0x48, 0x1c, 0x7e, 0xe5, 0x8d,
++  0x65, 0x2d, 0xeb, 0x72, 0x4f, 0x92, 0x44, 0xf3, 0xe6, 0xe0, 0xd0, 0xdf,
++  0x85, 0xa8, 0x13, 0x4a, 0xfb, 0x99, 0xca, 0x14, 0x2c, 0x97, 0x80, 0x93,
++  0x27, 0xd3, 0x20, 0xf8, 0x6d, 0x29, 0x28, 0x2c, 0xb9, 0x77, 0xea, 0xb1,
++  0x63, 0xbd, 0x7d, 0x53, 0xfd, 0x4a, 0x62, 0x64, 0x0b, 0x98, 0xa8, 0xae,
++  0x11, 0xfc, 0x6e, 0x8d, 0x63, 0xd4, 0x15, 0x55, 0xc6, 0x4c, 0x74, 0xf5,
++  0x5f, 0xa0, 0xb9, 0x2c, 0x2d, 0x9a, 0x7a, 0x87, 0x6e, 0xf0, 0x5e, 0x25,
++  0xed, 0xfc, 0xd8, 0xc4, 0x34, 0x33, 0x32, 0xad, 0x01, 0xd4, 0x4b, 0x49,
++  0x51, 0xc2, 0x07, 0x7f, 0x90, 0x6d, 0xea, 0xf5, 0x4c, 0x41, 0x71, 0x64,
++  0xeb, 0x1f, 0x29, 0xa3, 0x1f, 0x64, 0xa2, 0x1e, 0x0e, 0x6f, 0xa1, 0x67,
++  0x99, 0x8d, 0x98, 0x1c, 0xb8, 0x53, 0x9d, 0x30, 0x1d, 0xae, 0x32, 0x56,
++  0xd2
++};
++unsigned int certificate_printable_der_len = 829;
diff --git a/SOURCES/0196-appended-signatures-documentation.patch b/SOURCES/0196-appended-signatures-documentation.patch
new file mode 100644
index 0000000..864cfa3
--- /dev/null
+++ b/SOURCES/0196-appended-signatures-documentation.patch
@@ -0,0 +1,341 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Thu, 1 Oct 2020 13:02:09 +1000
+Subject: [PATCH] appended signatures: documentation
+
+This explains how appended signatures can be used to form part of
+a secure boot chain, and documents the commands and variables
+introduced.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ docs/grub.texi | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 182 insertions(+), 17 deletions(-)
+
+diff --git a/docs/grub.texi b/docs/grub.texi
+index afbde7c1f7b..4816be85611 100644
+--- a/docs/grub.texi
++++ b/docs/grub.texi
+@@ -3214,6 +3214,7 @@ These variables have special meaning to GRUB.
+ 
+ @menu
+ * biosnum::
++* check_appended_signatures::
+ * check_signatures::
+ * chosen::
+ * cmdpath::
+@@ -3273,11 +3274,18 @@ For an alternative approach which also changes BIOS drive mappings for the
+ chain-loaded system, @pxref{drivemap}.
+ 
+ 
++@node check_appended_signatures
++@subsection check_appended_signatures
++
++This variable controls whether GRUB enforces appended signature validation on
++certain loaded files. @xref{Using appended signatures}.
++
++
+ @node check_signatures
+ @subsection check_signatures
+ 
+-This variable controls whether GRUB enforces digital signature
+-validation on loaded files. @xref{Using digital signatures}.
++This variable controls whether GRUB enforces GPG-style digital signature
++validation on loaded files. @xref{Using GPG-style digital signatures}.
+ 
+ @node chosen
+ @subsection chosen
+@@ -3994,6 +4002,7 @@ you forget a command, you can run the command @command{help}
+ * date::                        Display or set current date and time
+ * devicetree::                  Load a device tree blob
+ * distrust::                    Remove a pubkey from trusted keys
++* distrust_certificate::        Remove a certificate from the list of trusted certificates
+ * drivemap::                    Map a drive to another
+ * echo::                        Display a line of text
+ * eval::                        Evaluate agruments as GRUB commands
+@@ -4010,6 +4019,7 @@ you forget a command, you can run the command @command{help}
+ * keystatus::                   Check key modifier status
+ * linux::                       Load a Linux kernel
+ * linux16::                     Load a Linux kernel (16-bit mode)
++* list_certificates::           List trusted certificates
+ * list_env::                    List variables in environment block
+ * list_trusted::                List trusted public keys
+ * load_env::                    Load variables from environment block
+@@ -4047,8 +4057,10 @@ you forget a command, you can run the command @command{help}
+ * test::                        Check file types and compare values
+ * true::                        Do nothing, successfully
+ * trust::                       Add public key to list of trusted keys
++* trust_certificate::           Add an x509 certificate to the list of trusted certificates
+ * unset::                       Unset an environment variable
+ @comment * vbeinfo::                     List available video modes
++* verify_appended::             Verify appended digital signature
+ * verify_detached::             Verify detached digital signature
+ * videoinfo::                   List available video modes
+ @comment * xen_*::              Xen boot commands for AArch64
+@@ -4376,9 +4388,28 @@ These keys are used to validate signatures when environment variable
+ @code{check_signatures} is set to @code{enforce}
+ (@pxref{check_signatures}), and by some invocations of
+ @command{verify_detached} (@pxref{verify_detached}).  @xref{Using
+-digital signatures}, for more information.
++GPG-style digital signatures}, for more information.
+ @end deffn
+ 
++
++@node distrust_certificate
++@subsection distrust_certificate
++
++@deffn Command distrust_certificate cert_number
++Remove the x509 certificate numbered @var{cert_number} from GRUB's keyring of
++trusted x509 certificates for verifying appended signatures.
++
++@var{cert_number} is the certificate number as listed by
++@command{list_certificates} (@pxref{list_certificates}).
++
++These certificates are used to validate appended signatures when environment
++variable @code{check_appended_signatures} is set to @code{enforce} or
++@code{forced} (@pxref{check_appended_signatures}), and by
++@command{verify_appended} (@pxref{verify_appended}). See
++@xref{Using appended signatures} for more information.
++@end deffn
++
++
+ @node drivemap
+ @subsection drivemap
+ 
+@@ -4636,6 +4667,21 @@ This command is only available on x86 systems.
+ @end deffn
+ 
+ 
++@node list_certificates
++@subsection list_certificates
++
++@deffn Command list_certificates
++List all x509 certificates trusted by GRUB for validating appended signatures.
++The output is a numbered list of certificates, showing the certificate's serial
++number and Common Name.
++
++The certificate number can be used as an argument to
++@command{distrust_certificate} (@pxref{distrust_certificate}).
++
++See @xref{Using appended signatures} for more information.
++@end deffn
++
++
+ @node list_env
+ @subsection list_env
+ 
+@@ -4655,7 +4701,7 @@ The output is in GPG's v4 key fingerprint format (i.e., the output of
+ @code{gpg --fingerprint}).  The least significant four bytes (last
+ eight hexadecimal digits) can be used as an argument to
+ @command{distrust} (@pxref{distrust}).
+-@xref{Using digital signatures}, for more information about uses for
++@xref{Using GPG-style digital signatures}, for more information about uses for
+ these keys.
+ @end deffn
+ 
+@@ -4690,8 +4736,13 @@ When used with care, @option{--skip-sig} and the whitelist enable an
+ administrator to configure a system to boot only signed
+ configurations, but to allow the user to select from among multiple
+ configurations, and to enable ``one-shot'' boot attempts and
+-``savedefault'' behavior.  @xref{Using digital signatures}, for more
++``savedefault'' behavior.  @xref{Using GPG-style digital signatures}, for more
+ information.
++
++Extra care should be taken when combining this command with appended signatures
++(@pxref{Using appended signatures}), as this file is not validated by an
++appended signature and could set @code{check_appended_signatures=no} if GRUB is
++not in @pxref{Lockdown} mode.
+ @end deffn
+ 
+ 
+@@ -4987,7 +5038,7 @@ read.  It is possible to modify a digitally signed environment block
+ file from within GRUB using this command, such that its signature will
+ no longer be valid on subsequent boots.  Care should be taken in such
+ advanced configurations to avoid rendering the system
+-unbootable. @xref{Using digital signatures}, for more information.
++unbootable. @xref{Using GPG-style digital signatures}, for more information.
+ @end deffn
+ 
+ 
+@@ -5387,11 +5438,32 @@ signatures when environment variable @code{check_signatures} is set to
+ must itself be properly signed.  The @option{--skip-sig} option can be
+ used to disable signature-checking when reading @var{pubkey_file}
+ itself. It is expected that @option{--skip-sig} is useful for testing
+-and manual booting. @xref{Using digital signatures}, for more
++and manual booting. @xref{Using GPG-style digital signatures}, for more
+ information.
+ @end deffn
+ 
+ 
++@node trust_certificate
++@subsection trust_certificate
++
++@deffn Command trust_certificate x509_certificate
++Read an DER-formatted x509 certificate from the file @var{x509_certificate}
++and add it to GRUB's internal list of trusted x509 certificates. These
++certificates are used to validate appended signatures when the environment
++variable @code{check_appended_signatures} is set to @code{enforce} or
++@code{forced}.
++
++Note that if @code{check_appended_signatures} is set to @code{enforce} or
++@code{forced} when @command{trust_certificate} is executed, then
++@var{x509_certificate} must itself bear an appended signature. (It is not
++sufficient that @var{x509_certificate} be signed by a trusted certificate
++according to the x509 rules: grub does not include support for validating
++signatures within x509 certificates themselves.)
++
++See @xref{Using appended signatures} for more information.
++@end deffn
++
++
+ @node unset
+ @subsection unset
+ 
+@@ -5410,6 +5482,18 @@ only on PC BIOS platforms.
+ @end deffn
+ @end ignore
+ 
++@node verify_appended
++@subsection verify_appended
++
++@deffn Command verify_appended file
++Verifies an appended signature on @var{file} against the trusted certificates
++known to GRUB (See @pxref{list_certificates}, @pxref{trust_certificate}, and
++@pxref{distrust_certificate}).
++
++Exit code @code{$?} is set to 0 if the signature validates
++successfully.  If validation fails, it is set to a non-zero value.
++See @xref{Using appended signatures}, for more information.
++@end deffn
+ 
+ @node verify_detached
+ @subsection verify_detached
+@@ -5428,7 +5512,7 @@ tried.
+ 
+ Exit code @code{$?} is set to 0 if the signature validates
+ successfully.  If validation fails, it is set to a non-zero value.
+-@xref{Using digital signatures}, for more information.
++@xref{Using GPG-style digital signatures}, for more information.
+ @end deffn
+ 
+ @node videoinfo
+@@ -5811,13 +5895,14 @@ environment variables and commands are listed in the same order.
+ @chapter Security
+ 
+ @menu
+-* Authentication and authorisation:: Users and access control
+-* Using digital signatures::         Booting digitally signed code
+-* UEFI secure boot and shim::        Booting digitally signed PE files
+-* Secure Boot Advanced Targeting::   Embedded information for generation number based revocation
+-* Measured Boot::                    Measuring boot components
+-* Lockdown::                         Lockdown when booting on a secure setup
+-* Signing GRUB itself::              Ensuring the integrity of the GRUB core image
++* Authentication and authorisation::   Users and access control
++* Using GPG-style digital signatures:: Booting digitally signed code
++* Using appended signatures::          An alternative approach to booting digitally signed code
++* UEFI secure boot and shim::          Booting digitally signed PE files
++* Secure Boot Advanced Targeting::     Embedded information for generation number based revocation
++* Measured Boot::                      Measuring boot components
++* Lockdown::                           Lockdown when booting on a secure setup
++* Signing GRUB itself::                Ensuring the integrity of the GRUB core image
+ @end menu
+ 
+ @node Authentication and authorisation
+@@ -5891,8 +5976,8 @@ generating configuration files with authentication.  You can use
+ adding @kbd{set superusers=} and @kbd{password} or @kbd{password_pbkdf2}
+ commands.
+ 
+-@node Using digital signatures
+-@section Using digital signatures in GRUB
++@node Using GPG-style digital signatures
++@section Using GPG-style digital signatures in GRUB
+ 
+ GRUB's @file{core.img} can optionally provide enforcement that all files
+ subsequently read from disk are covered by a valid digital signature.
+@@ -5985,6 +6070,86 @@ or BIOS) configuration to cause the machine to boot from a different
+ (attacker-controlled) device.  GRUB is at best only one link in a
+ secure boot chain.
+ 
++@node Using appended signatures
++@section Using appended signatures in GRUB
++
++GRUB supports verifying Linux-style 'appended signatures' for secure boot.
++Appended signatures are PKCS#7 messages containing a signature over the
++contents of a file, plus some metadata, appended to the end of a file. A file
++with an appended signature ends with the magic string:
++
++@example
++~Module signature appended~\n
++@end example
++
++where @code{\n} represents the line-feed character, @code{0x0a}.
++
++Certificates can be managed at boot time using the @pxref{trust_certificate},
++@pxref{distrust_certificate} and @pxref{list_certificates} commands.
++Certificates can also be built in to the core image using the @code{--x509}
++parameter to @command{grub-install} or @command{grub-mkimage}.
++
++A file can be explictly verified using the @pxref{verify_appended} command.
++
++Only signatures made with the SHA-256 or SHA-512 hash algorithm are supported,
++and only RSA signatures are supported.
++
++A file can be signed with the @command{sign-file} utility supplied with the
++Linux kernel source. For example, if you have @code{signing.key} as the private
++key and @code{certificate.der} as the x509 certificate containing the public key:
++
++@example
++sign-file SHA256 signing.key certificate.der vmlinux vmlinux.signed
++@end example
++
++Enforcement of signature verification is controlled by the
++@code{check_appended_signatures} variable.
++
++@itemize
++@item @samp{no}: no verification is performed. This is the default when GRUB
++      is not in @pxref{Lockdown} mode.
++@item @samp{enforce}: verification is performed. Verification can be disabled
++      by setting the variable back to @samp{no}.
++@item @samp{forced}: verification is performed and cannot be disabled. This is
++      set when GRUB is in Lockdown when the appendedsig module is loaded.
++@end itemize
++
++Unlike GPG-style signatures, not all files loaded by GRUB are required to be
++signed. Once verification is turned on, the following file types will have
++appended signatures verified:
++
++@itemize
++@item Linux kernels
++@item GRUB modules, except those built into the core image
++@item Any new certificate files to be trusted
++@end itemize
++
++ACPI tables and Device Tree images will not be checked for appended signatures
++but must be verified by another mechanism such as GPG-style signatures before
++they will be loaded.
++
++Unless lockdown mode is enabled, signature checking does @strong{not}
++stop an attacker with console access from dropping manually to the GRUB
++console and executing:
++
++@example
++set check_appended_signatures=no
++@end example
++
++Refer to the section on password-protecting GRUB (@pxref{Authentication
++and authorisation}) for more information on preventing this.
++
++Additionally, unless lockdown mode is enabled:
++
++@itemize
++@item Special care must be taken around the @command{loadenv} command, which
++      can be used to turn off @code{check_appended_signature}.
++
++@item If the grub configuration file is loaded from the disk, anyone who can
++      modify the file on disk can turn off @code{check_appended_signature}.
++      Consider embedding the configuration into the core grub image.
++@end itemize
++
+ @node UEFI secure boot and shim
+ @section UEFI secure boot and shim support
+ 
diff --git a/SOURCES/0197-ieee1275-enter-lockdown-based-on-ibm-secure-boot.patch b/SOURCES/0197-ieee1275-enter-lockdown-based-on-ibm-secure-boot.patch
new file mode 100644
index 0000000..40c3b3e
--- /dev/null
+++ b/SOURCES/0197-ieee1275-enter-lockdown-based-on-ibm-secure-boot.patch
@@ -0,0 +1,109 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Mon, 28 Sep 2020 11:11:17 +1000
+Subject: [PATCH] ieee1275: enter lockdown based on /ibm,secure-boot
+
+If the 'ibm,secure-boot' property of the root node is 2 or greater,
+enter lockdown.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/Makefile.core.def    |  1 +
+ grub-core/kern/ieee1275/init.c | 27 +++++++++++++++++++++++++++
+ include/grub/lockdown.h        |  3 ++-
+ docs/grub.texi                 |  4 ++--
+ 4 files changed, 32 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 6bddc841b85..3f3459b2c70 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -323,6 +323,7 @@ kernel = {
+   powerpc_ieee1275 = kern/powerpc/cache.S;
+   powerpc_ieee1275 = kern/powerpc/dl.c;
+   powerpc_ieee1275 = kern/powerpc/compiler-rt.S;
++  powerpc_ieee1275 = kern/lockdown.c;
+ 
+   sparc64_ieee1275 = kern/sparc64/cache.S;
+   sparc64_ieee1275 = kern/sparc64/dl.c;
+diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
+index 937c1bc44cb..fc7d9712729 100644
+--- a/grub-core/kern/ieee1275/init.c
++++ b/grub-core/kern/ieee1275/init.c
+@@ -44,6 +44,7 @@
+ #ifdef __sparc__
+ #include <grub/machine/kernel.h>
+ #endif
++#include <grub/lockdown.h>
+ 
+ /* The minimal heap size we can live with. */
+ #define HEAP_MIN_SIZE		(unsigned long) (2 * 1024 * 1024)
+@@ -271,6 +272,30 @@ grub_parse_cmdline (void)
+     }
+ }
+ 
++static void
++grub_get_ieee1275_secure_boot (void)
++{
++  grub_ieee1275_phandle_t root;
++  int rc;
++  grub_uint32_t is_sb;
++
++  grub_ieee1275_finddevice ("/", &root);
++
++  rc = grub_ieee1275_get_integer_property (root, "ibm,secure-boot", &is_sb,
++                                           sizeof (is_sb), 0);
++
++  /* ibm,secure-boot:
++   * 0 - disabled
++   * 1 - audit
++   * 2 - enforce
++   * 3 - enforce + OS-specific behaviour
++   *
++   * We only support enforce.
++   */
++  if (rc >= 0 && is_sb >= 2)
++    grub_lockdown ();
++}
++
+ grub_addr_t grub_modbase;
+ 
+ void
+@@ -296,6 +321,8 @@ grub_machine_init (void)
+ #else
+   grub_install_get_time_ms (grub_rtc_get_time_ms);
+ #endif
++
++  grub_get_ieee1275_secure_boot ();
+ }
+ 
+ void
+diff --git a/include/grub/lockdown.h b/include/grub/lockdown.h
+index 40531fa823b..ebfee4bf06e 100644
+--- a/include/grub/lockdown.h
++++ b/include/grub/lockdown.h
+@@ -24,7 +24,8 @@
+ #define GRUB_LOCKDOWN_DISABLED       0
+ #define GRUB_LOCKDOWN_ENABLED        1
+ 
+-#ifdef GRUB_MACHINE_EFI
++#if defined(GRUB_MACHINE_EFI) || \
++    (defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275))
+ extern void
+ EXPORT_FUNC (grub_lockdown) (void);
+ extern int
+diff --git a/docs/grub.texi b/docs/grub.texi
+index 4816be85611..a4da9c2a1b9 100644
+--- a/docs/grub.texi
++++ b/docs/grub.texi
+@@ -6227,8 +6227,8 @@ Measured boot is currently only supported on EFI platforms.
+ @section Lockdown when booting on a secure setup
+ 
+ The GRUB can be locked down when booted on a secure boot environment, for example
+-if the UEFI secure boot is enabled. On a locked down configuration, the GRUB will
+-be restricted and some operations/commands cannot be executed.
++if UEFI or Power secure boot is enabled. On a locked down configuration, the
++GRUB will be restricted and some operations/commands cannot be executed.
+ 
+ The @samp{lockdown} variable is set to @samp{y} when the GRUB is locked down.
+ Otherwise it does not exit.
diff --git a/SOURCES/0198-ieee1275-drop-HEAP_MAX_ADDR-HEAP_MIN_SIZE.patch b/SOURCES/0198-ieee1275-drop-HEAP_MAX_ADDR-HEAP_MIN_SIZE.patch
new file mode 100644
index 0000000..52fa9d2
--- /dev/null
+++ b/SOURCES/0198-ieee1275-drop-HEAP_MAX_ADDR-HEAP_MIN_SIZE.patch
@@ -0,0 +1,75 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Wed, 14 Apr 2021 20:10:23 +1000
+Subject: [PATCH] ieee1275: drop HEAP_MAX_ADDR, HEAP_MIN_SIZE
+
+HEAP_MAX_ADDR is confusing. Currently it is set to 32MB, except
+on ieee1275 on x86, where it is 64MB.
+
+There is a comment which purports to explain it:
+
+/* If possible, we will avoid claiming heap above this address, because it
+   seems to cause relocation problems with OSes that link at 4 MiB */
+
+This doesn't make a lot of sense when the constants are well above 4MB
+already. It was not always this way. Prior to
+commit 7b5d0fe4440c ("Increase heap limit") in 2010, HEAP_MAX_SIZE and
+HEAP_MAX_ADDR were indeed 4MB. However, when the constants were increased
+the comment was left unchanged.
+
+It's been over a decade. It doesn't seem like we have problems with
+claims over 4MB on powerpc or x86 ieee1275. (sparc does things completely
+differently and never used the constant.)
+
+Drop the constant and the check.
+
+The only use of HEAP_MIN_SIZE was to potentially override the
+HEAP_MAX_ADDR check. It is now unused. Remove it.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/kern/ieee1275/init.c | 17 -----------------
+ 1 file changed, 17 deletions(-)
+
+diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
+index fc7d9712729..0dcd114ce54 100644
+--- a/grub-core/kern/ieee1275/init.c
++++ b/grub-core/kern/ieee1275/init.c
+@@ -46,9 +46,6 @@
+ #endif
+ #include <grub/lockdown.h>
+ 
+-/* The minimal heap size we can live with. */
+-#define HEAP_MIN_SIZE		(unsigned long) (2 * 1024 * 1024)
+-
+ /* The maximum heap size we're going to claim */
+ #ifdef __i386__
+ #define HEAP_MAX_SIZE		(unsigned long) (64 * 1024 * 1024)
+@@ -56,14 +53,6 @@
+ #define HEAP_MAX_SIZE		(unsigned long) (32 * 1024 * 1024)
+ #endif
+ 
+-/* If possible, we will avoid claiming heap above this address, because it
+-   seems to cause relocation problems with OSes that link at 4 MiB */
+-#ifdef __i386__
+-#define HEAP_MAX_ADDR		(unsigned long) (64 * 1024 * 1024)
+-#else
+-#define HEAP_MAX_ADDR		(unsigned long) (32 * 1024 * 1024)
+-#endif
+-
+ extern char _end[];
+ 
+ #ifdef __sparc__
+@@ -185,12 +174,6 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
+   if (*total + len > HEAP_MAX_SIZE)
+     len = HEAP_MAX_SIZE - *total;
+ 
+-  /* Avoid claiming anything above HEAP_MAX_ADDR, if possible. */
+-  if ((addr < HEAP_MAX_ADDR) &&				/* if it's too late, don't bother */
+-      (addr + len > HEAP_MAX_ADDR) &&				/* if it wasn't available anyway, don't bother */
+-      (*total + (HEAP_MAX_ADDR - addr) > HEAP_MIN_SIZE))	/* only limit ourselves when we can afford to */
+-     len = HEAP_MAX_ADDR - addr;
+-
+   /* In theory, firmware should already prevent this from happening by not
+      listing our own image in /memory/available.  The check below is intended
+      as a safeguard in case that doesn't happen.  However, it doesn't protect
diff --git a/SOURCES/0199-ieee1275-claim-more-memory.patch b/SOURCES/0199-ieee1275-claim-more-memory.patch
new file mode 100644
index 0000000..001a9df
--- /dev/null
+++ b/SOURCES/0199-ieee1275-claim-more-memory.patch
@@ -0,0 +1,252 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Wed, 15 Apr 2020 23:28:29 +1000
+Subject: [PATCH] ieee1275: claim more memory
+
+On powerpc-ieee1275, we are running out of memory trying to verify
+anything. This is because:
+
+ - we have to load an entire file into memory to verify it. This is
+   extremely difficult to change with appended signatures.
+ - We only have 32MB of heap.
+ - Distro kernels are now often around 30MB.
+
+So we want to claim more memory from OpenFirmware for our heap.
+
+There are some complications:
+
+ - The grub mm code isn't the only thing that will make claims on
+   memory from OpenFirmware:
+
+    * PFW/SLOF will have claimed some for their own use.
+
+    * The ieee1275 loader will try to find other bits of memory that we
+      haven't claimed to place the kernel and initrd when we go to boot.
+
+    * Once we load Linux, it will also try to claim memory. It claims
+      memory without any reference to /memory/available, it just starts
+      at min(top of RMO, 768MB) and works down. So we need to avoid this
+      area. See arch/powerpc/kernel/prom_init.c as of v5.11.
+
+ - The smallest amount of memory a ppc64 KVM guest can have is 256MB.
+   It doesn't work with distro kernels but can work with custom kernels.
+   We should maintain support for that. (ppc32 can boot with even less,
+   and we shouldn't break that either.)
+
+ - Even if a VM has more memory, the memory OpenFirmware makes available
+   as Real Memory Area can be restricted. A freshly created LPAR on a
+   PowerVM machine is likely to have only 256MB available to OpenFirmware
+   even if it has many gigabytes of memory allocated.
+
+EFI systems will attempt to allocate 1/4th of the available memory,
+clamped to between 1M and 1600M. That seems like a good sort of
+approach, we just need to figure out if 1/4 is the right fraction
+for us.
+
+We don't know in advance how big the kernel and initrd are going to be,
+which makes figuring out how much memory we can take a bit tricky.
+
+To figure out how much memory we should leave unused, I looked at:
+
+ - an Ubuntu 20.04.1 ppc64le pseries KVM guest:
+    vmlinux: ~30MB
+    initrd:  ~50MB
+
+ - a RHEL8.2 ppc64le pseries KVM guest:
+    vmlinux: ~30MB
+    initrd:  ~30MB
+
+Ubuntu VMs struggle to boot with just 256MB under SLOF.
+RHEL likewise has a higher minimum supported memory figure.
+So lets first consider a distro kernel and 512MB of addressible memory.
+(This is the default case for anything booting under PFW.) Say we lose
+131MB to PFW (based on some tests). This leaves us 381MB. 1/4 of 381MB
+is ~95MB. That should be enough to verify a 30MB vmlinux and should
+leave plenty of space to load Linux and the initrd.
+
+If we consider 256MB of RMA under PFW, we have just 125MB remaining. 1/4
+of that is a smidge under 32MB, which gives us very poor odds of verifying
+a distro-sized kernel. However, if we need 80MB just to put the kernel
+and initrd in memory, we can't claim any more than 45MB anyway. So 1/4
+will do. We'll come back to this later.
+
+grub is always built as a 32-bit binary, even if it's loading a ppc64
+kernel. So we can't address memory beyond 4GB. This gives a natural cap
+of 1GB for powerpc-ieee1275.
+
+Also apply this 1/4 approach to i386-ieee1275, but keep the 32MB cap.
+
+make check still works for both i386 and powerpc and I've booted
+powerpc grub with this change under SLOF and PFW.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/kern/ieee1275/init.c | 81 +++++++++++++++++++++++++++++++++---------
+ docs/grub-dev.texi             |  6 ++--
+ 2 files changed, 69 insertions(+), 18 deletions(-)
+
+diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
+index 0dcd114ce54..c61d91a0285 100644
+--- a/grub-core/kern/ieee1275/init.c
++++ b/grub-core/kern/ieee1275/init.c
+@@ -46,11 +46,12 @@
+ #endif
+ #include <grub/lockdown.h>
+ 
+-/* The maximum heap size we're going to claim */
++/* The maximum heap size we're going to claim. Not used by sparc.
++   We allocate 1/4 of the available memory under 4G, up to this limit. */
+ #ifdef __i386__
+ #define HEAP_MAX_SIZE		(unsigned long) (64 * 1024 * 1024)
+-#else
+-#define HEAP_MAX_SIZE		(unsigned long) (32 * 1024 * 1024)
++#else // __powerpc__
++#define HEAP_MAX_SIZE		(unsigned long) (1 * 1024 * 1024 * 1024)
+ #endif
+ 
+ extern char _end[];
+@@ -147,16 +148,45 @@ grub_claim_heap (void)
+ 				 + GRUB_KERNEL_MACHINE_STACK_SIZE), 0x200000);
+ }
+ #else
+-/* Helper for grub_claim_heap.  */
++/* Helper for grub_claim_heap on powerpc. */
++static int
++heap_size (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
++	   void *data)
++{
++  grub_uint32_t total = *(grub_uint32_t *)data;
++
++  if (type != GRUB_MEMORY_AVAILABLE)
++    return 0;
++
++  /* Do not consider memory beyond 4GB */
++  if (addr > 0xffffffffUL)
++    return 0;
++
++  if (addr + len > 0xffffffffUL)
++    len = 0xffffffffUL - addr;
++
++  total += len;
++  *(grub_uint32_t *)data = total;
++
++  return 0;
++}
++
+ static int
+ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
+ 	   void *data)
+ {
+-  unsigned long *total = data;
++  grub_uint32_t total = *(grub_uint32_t *)data;
+ 
+   if (type != GRUB_MEMORY_AVAILABLE)
+     return 0;
+ 
++  /* Do not consider memory beyond 4GB */
++  if (addr > 0xffffffffUL)
++    return 0;
++
++  if (addr + len > 0xffffffffUL)
++    len = 0xffffffffUL - addr;
++
+   if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_PRE1_5M_CLAIM))
+     {
+       if (addr + len <= 0x180000)
+@@ -170,10 +200,6 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
+     }
+   len -= 1; /* Required for some firmware.  */
+ 
+-  /* Never exceed HEAP_MAX_SIZE  */
+-  if (*total + len > HEAP_MAX_SIZE)
+-    len = HEAP_MAX_SIZE - *total;
+-
+   /* In theory, firmware should already prevent this from happening by not
+      listing our own image in /memory/available.  The check below is intended
+      as a safeguard in case that doesn't happen.  However, it doesn't protect
+@@ -185,6 +211,18 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
+       len = 0;
+     }
+ 
++  /* If this block contains 0x30000000 (768MB), do not claim below that.
++     Linux likes to claim memory at min(RMO top, 768MB) and works down
++     without reference to /memory/available. */
++  if ((addr < 0x30000000) && ((addr + len) > 0x30000000))
++    {
++      len = len - (0x30000000 - addr);
++      addr = 0x30000000;
++    }
++
++  if (len > total)
++    len = total;
++
+   if (len)
+     {
+       grub_err_t err;
+@@ -193,10 +231,12 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
+       if (err)
+ 	return err;
+       grub_mm_init_region ((void *) (grub_addr_t) addr, len);
++      total -= len;
+     }
+ 
+-  *total += len;
+-  if (*total >= HEAP_MAX_SIZE)
++  *(grub_uint32_t *)data = total;
++
++  if (total == 0)
+     return 1;
+ 
+   return 0;
+@@ -205,13 +245,22 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
+ static void 
+ grub_claim_heap (void)
+ {
+-  unsigned long total = 0;
++  grub_uint32_t total = 0;
+ 
+   if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM))
+-    heap_init (GRUB_IEEE1275_STATIC_HEAP_START, GRUB_IEEE1275_STATIC_HEAP_LEN,
+-	       1, &total);
+-  else
+-    grub_machine_mmap_iterate (heap_init, &total);
++    {
++      heap_init (GRUB_IEEE1275_STATIC_HEAP_START, GRUB_IEEE1275_STATIC_HEAP_LEN,
++		 1, &total);
++      return;
++    }
++
++  grub_machine_mmap_iterate (heap_size, &total);
++
++  total = total / 4;
++  if (total > HEAP_MAX_SIZE)
++    total = HEAP_MAX_SIZE;
++
++  grub_machine_mmap_iterate (heap_init, &total);
+ }
+ #endif
+ 
+diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
+index 19f708ee662..90083772c8a 100644
+--- a/docs/grub-dev.texi
++++ b/docs/grub-dev.texi
+@@ -1047,7 +1047,9 @@ space is limited to 4GiB. GRUB allocates pages from EFI for its heap, at most
+ 1.6 GiB.
+ 
+ On i386-ieee1275 and powerpc-ieee1275 GRUB uses same stack as IEEE1275.
+-It allocates at most 32MiB for its heap.
++
++On i386-ieee1275, GRUB allocates at most 32MiB for its heap. On
++powerpc-ieee1275, GRUB allocates up to 1GiB.
+ 
+ On sparc64-ieee1275 stack is 256KiB and heap is 2MiB.
+ 
+@@ -1075,7 +1077,7 @@ In short:
+ @item i386-qemu               @tab 60 KiB  @tab < 4 GiB
+ @item *-efi                   @tab ?       @tab < 1.6 GiB
+ @item i386-ieee1275           @tab ?       @tab < 32 MiB
+-@item powerpc-ieee1275        @tab ?       @tab < 32 MiB
++@item powerpc-ieee1275        @tab ?       @tab < 1 GiB
+ @item sparc64-ieee1275        @tab 256KiB  @tab 2 MiB
+ @item arm-uboot               @tab 256KiB  @tab 2 MiB
+ @item mips(el)-qemu_mips      @tab 2MiB    @tab 253 MiB
diff --git a/SOURCES/0200-ieee1275-request-memory-with-ibm-client-architecture.patch b/SOURCES/0200-ieee1275-request-memory-with-ibm-client-architecture.patch
new file mode 100644
index 0000000..6e6c1f0
--- /dev/null
+++ b/SOURCES/0200-ieee1275-request-memory-with-ibm-client-architecture.patch
@@ -0,0 +1,268 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Fri, 16 Apr 2021 11:48:46 +1000
+Subject: [PATCH] ieee1275: request memory with ibm,client-architecture-support
+
+On PowerVM, the first time we boot a Linux partition, we may only get
+256MB of real memory area, even if the partition has more memory.
+
+This isn't really enough. Fortunately, the Power Architecture Platform
+Reference (PAPR) defines a method we can call to ask for more memory.
+This is part of the broad and powerful ibm,client-architecture-support
+(CAS) method.
+
+CAS can do an enormous amount of things on a PAPR platform: as well as
+asking for memory, you can set the supported processor level, the interrupt
+controller, hash vs radix mmu, and so on. We want to touch as little of
+this as possible because we don't want to step on the toes of the future OS.
+
+If:
+
+ - we are running under what we think is PowerVM (compatible property of /
+   begins with "IBM"), and
+
+ - the full amount of RMA is less than 512MB (as determined by the reg
+   property of /memory)
+
+then call CAS as follows: (refer to the Linux on Power Architecture
+Reference, LoPAR, which is public, at B.5.2.3):
+
+ - Use the "any" PVR value and supply 2 option vectors.
+
+ - Set option vector 1 (PowerPC Server Processor Architecture Level)
+   to "ignore".
+
+ - Set option vector 2 with default or Linux-like options, including a
+   min-rma-size of 512MB.
+
+This will cause a CAS reboot and the partition will restart with 512MB
+of RMA. Grub will notice the 512MB and not call CAS again.
+
+(A partition can be configured with only 256MB of memory, which would
+mean this request couldn't be satisfied, but PFW refuses to load with
+only 256MB of memory, so it's a bit moot. SLOF will run fine with 256MB,
+but we will never call CAS under qemu/SLOF because /compatible won't
+begin with "IBM".)
+
+One of the first things Linux does while still running under OpenFirmware
+is to call CAS with a much fuller set of options (including asking for
+512MB of memory). This includes a much more restrictive set of PVR values
+and processor support levels, and this will induce another reboot. On this
+reboot grub will again notice the higher RMA, and not call CAS. We will get
+to Linux, Linux will call CAS but because the values are now set for Linux
+this will not induce another CAS reboot and we will finally boot.
+
+On all subsequent boots, everything will be configured with 512MB of RMA
+and all the settings Linux likes, so there will be no further CAS reboots.
+
+(phyp is super sticky with the RMA size - it persists even on cold boots.
+So if you've ever booted Linux in a partition, you'll probably never have
+grub call CAS. It'll only ever fire the first time a partition loads grub,
+or if you deliberately lower the amount of memory your partition has below
+512MB.)
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/kern/ieee1275/cmain.c  |   3 +
+ grub-core/kern/ieee1275/init.c   | 144 ++++++++++++++++++++++++++++++++++++++-
+ include/grub/ieee1275/ieee1275.h |   8 ++-
+ 3 files changed, 152 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c
+index 04df9d2c667..6435628ec57 100644
+--- a/grub-core/kern/ieee1275/cmain.c
++++ b/grub-core/kern/ieee1275/cmain.c
+@@ -127,6 +127,9 @@ grub_ieee1275_find_options (void)
+ 	      break;
+ 	    }
+ 	}
++
++      if (grub_strncmp (tmp, "IBM,", 4) == 0)
++	grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY);
+     }
+ 
+   if (is_smartfirmware)
+diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
+index c61d91a0285..9704715c837 100644
+--- a/grub-core/kern/ieee1275/init.c
++++ b/grub-core/kern/ieee1275/init.c
+@@ -242,6 +242,135 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
+   return 0;
+ }
+ 
++/* How much memory does OF believe it has? (regardless of whether
++   it's accessible or not) */
++static grub_err_t
++grub_ieee1275_total_mem (grub_uint64_t *total)
++{
++  grub_ieee1275_phandle_t root;
++  grub_ieee1275_phandle_t memory;
++  grub_uint32_t reg[4];
++  grub_ssize_t reg_size;
++  grub_uint32_t address_cells = 1;
++  grub_uint32_t size_cells = 1;
++  grub_uint64_t size;
++
++  /* If we fail to get to the end, report 0. */
++  *total = 0;
++
++  /* Determine the format of each entry in `reg'.  */
++  grub_ieee1275_finddevice ("/", &root);
++  grub_ieee1275_get_integer_property (root, "#address-cells", &address_cells,
++				      sizeof address_cells, 0);
++  grub_ieee1275_get_integer_property (root, "#size-cells", &size_cells,
++				      sizeof size_cells, 0);
++
++  if (size_cells > address_cells)
++    address_cells = size_cells;
++
++  /* Load `/memory/reg'.  */
++  if (grub_ieee1275_finddevice ("/memory", &memory))
++    return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
++		       "couldn't find /memory node");
++  if (grub_ieee1275_get_integer_property (memory, "reg", reg,
++					  sizeof reg, &reg_size))
++    return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
++		       "couldn't examine /memory/reg property");
++  if (reg_size < 0 || (grub_size_t) reg_size > sizeof (reg))
++    return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
++                       "/memory response buffer exceeded");
++
++  if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_ADDRESS_CELLS))
++    {
++      address_cells = 1;
++      size_cells = 1;
++    }
++
++  /* Decode only the size */
++  size = reg[address_cells];
++  if (size_cells == 2)
++    size = (size << 32) | reg[address_cells + 1];
++
++  *total = size;
++
++  return grub_errno;
++}
++
++/* Based on linux - arch/powerpc/kernel/prom_init.c */
++struct option_vector2 {
++	grub_uint8_t byte1;
++	grub_uint16_t reserved;
++	grub_uint32_t real_base;
++	grub_uint32_t real_size;
++	grub_uint32_t virt_base;
++	grub_uint32_t virt_size;
++	grub_uint32_t load_base;
++	grub_uint32_t min_rma;
++	grub_uint32_t min_load;
++	grub_uint8_t min_rma_percent;
++	grub_uint8_t max_pft_size;
++} __attribute__((packed));
++
++struct pvr_entry {
++	  grub_uint32_t mask;
++	  grub_uint32_t entry;
++};
++
++struct cas_vector {
++    struct {
++      struct pvr_entry terminal;
++    } pvr_list;
++    grub_uint8_t num_vecs;
++    grub_uint8_t vec1_size;
++    grub_uint8_t vec1;
++    grub_uint8_t vec2_size;
++    struct option_vector2 vec2;
++} __attribute__((packed));
++
++/* Call ibm,client-architecture-support to try to get more RMA.
++   We ask for 512MB which should be enough to verify a distro kernel.
++   We ignore most errors: if we don't succeed we'll proceed with whatever
++   memory we have. */
++static void
++grub_ieee1275_ibm_cas (void)
++{
++  int rc;
++  grub_ieee1275_ihandle_t root;
++  struct cas_args {
++    struct grub_ieee1275_common_hdr common;
++    grub_ieee1275_cell_t method;
++    grub_ieee1275_ihandle_t ihandle;
++    grub_ieee1275_cell_t cas_addr;
++    grub_ieee1275_cell_t result;
++  } args;
++  struct cas_vector vector = {
++    .pvr_list = { { 0x00000000, 0xffffffff } }, /* any processor */
++    .num_vecs = 2 - 1,
++    .vec1_size = 0,
++    .vec1 = 0x80, /* ignore */
++    .vec2_size = 1 + sizeof(struct option_vector2) - 2,
++    .vec2 = {
++      0, 0, -1, -1, -1, -1, -1, 512, -1, 0, 48
++    },
++  };
++
++  INIT_IEEE1275_COMMON (&args.common, "call-method", 3, 2);
++  args.method = (grub_ieee1275_cell_t)"ibm,client-architecture-support";
++  rc = grub_ieee1275_open("/", &root);
++  if (rc) {
++	  grub_error (GRUB_ERR_IO, "could not open root when trying to call CAS");
++	  return;
++  }
++  args.ihandle = root;
++  args.cas_addr = (grub_ieee1275_cell_t)&vector;
++
++  grub_printf("Calling ibm,client-architecture-support...");
++  IEEE1275_CALL_ENTRY_FN (&args);
++  grub_printf("done\n");
++
++  grub_ieee1275_close(root);
++}
++
+ static void 
+ grub_claim_heap (void)
+ {
+@@ -249,11 +378,22 @@ grub_claim_heap (void)
+ 
+   if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM))
+     {
+-      heap_init (GRUB_IEEE1275_STATIC_HEAP_START, GRUB_IEEE1275_STATIC_HEAP_LEN,
+-		 1, &total);
++      heap_init (GRUB_IEEE1275_STATIC_HEAP_START,
++		 GRUB_IEEE1275_STATIC_HEAP_LEN, 1, &total);
+       return;
+     }
+ 
++  if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY))
++    {
++      grub_uint64_t rma_size;
++      grub_err_t err;
++
++      err = grub_ieee1275_total_mem (&rma_size);
++      /* if we have an error, don't call CAS, just hope for the best */
++      if (!err && rma_size < (512 * 1024 * 1024))
++	grub_ieee1275_ibm_cas();
++    }
++
+   grub_machine_mmap_iterate (heap_size, &total);
+ 
+   total = total / 4;
+diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h
+index b5a1d49bbc3..e0a6c2ce1e6 100644
+--- a/include/grub/ieee1275/ieee1275.h
++++ b/include/grub/ieee1275/ieee1275.h
+@@ -149,7 +149,13 @@ enum grub_ieee1275_flag
+ 
+   GRUB_IEEE1275_FLAG_RAW_DEVNAMES,
+   
+-  GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT
++  GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT,
++
++  /* On PFW, the first time we boot a Linux partition, we may only get 256MB
++     of real memory area, even if the partition has more memory. Set this flag
++     if we think we're running under PFW. Then, if this flag is set, and the
++     RMA is only 256MB in size, try asking for more with CAS. */
++  GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY,
+ };
+ 
+ extern int EXPORT_FUNC(grub_ieee1275_test_flag) (enum grub_ieee1275_flag flag);
diff --git a/SOURCES/0201-appendedsig-x509-Also-handle-the-Extended-Key-Usage-.patch b/SOURCES/0201-appendedsig-x509-Also-handle-the-Extended-Key-Usage-.patch
new file mode 100644
index 0000000..a237a21
--- /dev/null
+++ b/SOURCES/0201-appendedsig-x509-Also-handle-the-Extended-Key-Usage-.patch
@@ -0,0 +1,315 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Sat, 8 May 2021 02:27:58 +0200
+Subject: [PATCH] appendedsig/x509: Also handle the Extended Key Usage
+ extension
+
+Red Hat certificates have both Key Usage and Extended Key Usage extensions
+present, but the appended signatures x509 parser doesn't handle the latter
+and so buils due finding an unrecognised critical extension:
+
+Error loading initial key:
+../../grub-core/commands/appendedsig/x509.c:780:Unhandled critical x509 extension with OID 2.5.29.37
+
+Fix this by also parsing the Extended Key Usage extension and handle it by
+verifying that the certificate has a single purpose, that is code signing.
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/commands/appendedsig/x509.c     | 94 ++++++++++++++++++++++++++++++-
+ grub-core/tests/appended_signature_test.c | 29 +++++++++-
+ grub-core/tests/appended_signatures.h     | 81 ++++++++++++++++++++++++++
+ 3 files changed, 201 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/commands/appendedsig/x509.c b/grub-core/commands/appendedsig/x509.c
+index 2b38b3670a2..42ec65c54aa 100644
+--- a/grub-core/commands/appendedsig/x509.c
++++ b/grub-core/commands/appendedsig/x509.c
+@@ -47,6 +47,12 @@ const char *keyUsage_oid = "2.5.29.15";
+  */
+ const char *basicConstraints_oid = "2.5.29.19";
+ 
++/*
++ * RFC 5280 4.2.1.12 Extended Key Usage
++ */
++const char *extendedKeyUsage_oid = "2.5.29.37";
++const char *codeSigningUsage_oid = "1.3.6.1.5.5.7.3.3";
++
+ /*
+  * RFC 3279 2.3.1
+  *
+@@ -637,6 +643,77 @@ cleanup:
+   return err;
+ }
+ 
++/*
++ * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
++ *
++ * KeyPurposeId ::= OBJECT IDENTIFIER
++ */
++static grub_err_t
++verify_extended_key_usage (grub_uint8_t * value, int value_size)
++{
++  asn1_node extendedasn;
++  int result, count;
++  grub_err_t err = GRUB_ERR_NONE;
++  char usage[MAX_OID_LEN];
++  int usage_size = sizeof (usage);
++
++  result =
++    asn1_create_element (_gnutls_pkix_asn, "PKIX1.ExtKeyUsageSyntax",
++			 &extendedasn);
++  if (result != ASN1_SUCCESS)
++    {
++      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++			 "Could not create ASN.1 structure for Extended Key Usage");
++    }
++
++  result = asn1_der_decoding2 (&extendedasn, value, &value_size,
++			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
++  if (result != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		    "Error parsing DER for Extended Key Usage: %s",
++		    asn1_error);
++      goto cleanup;
++    }
++
++  /*
++   * If EKUs are present, there must be exactly 1 and it must be a
++   * codeSigning usage.
++   */
++  result = asn1_number_of_elements(extendedasn, "", &count);
++  if (result != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		    "Error counting number of Extended Key Usages: %s",
++		    asn1_strerror (result));
++      goto cleanup;
++    }
++
++  result = asn1_read_value (extendedasn, "?1", usage, &usage_size);
++  if (result != ASN1_SUCCESS)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		    "Error reading Extended Key Usage: %s",
++		    asn1_strerror (result));
++      goto cleanup;
++    }
++
++  if (grub_strncmp (codeSigningUsage_oid, usage, usage_size) != 0)
++    {
++      err =
++	grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		    "Unexpected Extended Key Usage OID, got: %s",
++		    usage);
++      goto cleanup;
++    }
++
++cleanup:
++  asn1_delete_structure (&extendedasn);
++  return err;
++}
+ 
+ /*
+  * Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
+@@ -660,7 +737,7 @@ verify_extensions (asn1_node cert)
+ {
+   int result;
+   int ext, num_extensions = 0;
+-  int usage_present = 0, constraints_present = 0;
++  int usage_present = 0, constraints_present = 0, extended_usage_present = 0;
+   char *oid_path, *critical_path, *value_path;
+   char extnID[MAX_OID_LEN];
+   int extnID_size;
+@@ -754,6 +831,15 @@ verify_extensions (asn1_node cert)
+ 	    }
+ 	  constraints_present++;
+ 	}
++      else if (grub_strncmp (extendedKeyUsage_oid, extnID, extnID_size) == 0)
++	{
++	  err = verify_extended_key_usage (value, value_size);
++	  if (err != GRUB_ERR_NONE)
++	    {
++	      goto cleanup_value;
++	    }
++	  extended_usage_present++;
++	}
+       else if (grub_strncmp ("TRUE", critical, critical_size) == 0)
+ 	{
+ 	  /*
+@@ -785,6 +871,12 @@ verify_extensions (asn1_node cert)
+ 			 "Unexpected number of basic constraints extensions - expected 1, got %d",
+ 			 constraints_present);
+     }
++  if (extended_usage_present > 1)
++    {
++      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			 "Unexpected number of Extended Key Usage extensions - expected 0 or 1, got %d",
++			 extended_usage_present);
++    }
+   return GRUB_ERR_NONE;
+ 
+ cleanup_value:
+diff --git a/grub-core/tests/appended_signature_test.c b/grub-core/tests/appended_signature_test.c
+index 88a485200d8..dbba0616621 100644
+--- a/grub-core/tests/appended_signature_test.c
++++ b/grub-core/tests/appended_signature_test.c
+@@ -111,6 +111,22 @@ static struct grub_procfs_entry certificate_printable_der_entry = {
+   .get_contents = get_certificate_printable_der
+ };
+ 
++static char *
++get_certificate_eku_der (grub_size_t * sz)
++{
++  char *ret;
++  *sz = certificate_eku_der_len;
++  ret = grub_malloc (*sz);
++  if (ret)
++    grub_memcpy (ret, certificate_eku_der, *sz);
++  return ret;
++}
++
++static struct grub_procfs_entry certificate_eku_der_entry = {
++  .name = "certificate_eku.der",
++  .get_contents = get_certificate_eku_der
++};
++
+ 
+ static void
+ do_verify (const char *f, int is_valid)
+@@ -149,6 +165,7 @@ appended_signature_test (void)
+   char *trust_args2[] = { (char *) "(proc)/certificate2.der", NULL };
+   char *trust_args_printable[] = { (char *) "(proc)/certificate_printable.der",
+ 				   NULL };
++  char *trust_args_eku[] = { (char *) "(proc)/certificate_eku.der", NULL };
+   char *distrust_args[] = { (char *) "1", NULL };
+   char *distrust2_args[] = { (char *) "2", NULL };
+   grub_err_t err;
+@@ -157,6 +174,7 @@ appended_signature_test (void)
+   grub_procfs_register ("certificate2.der", &certificate2_der_entry);
+   grub_procfs_register ("certificate_printable.der",
+ 			&certificate_printable_der_entry);
++  grub_procfs_register ("certificate_eku.der", &certificate_eku_der_entry);
+ 
+   cmd_trust = grub_command_find ("trust_certificate");
+   if (!cmd_trust)
+@@ -266,16 +284,23 @@ appended_signature_test (void)
+ 
+   /*
+    * Lastly, check a certificate that uses printableString rather than
+-   * utf8String loads properly.
++   * utf8String loads properly, and that a certificate with an appropriate
++   * extended key usage loads.
+    */
+   err = (cmd_trust->func) (cmd_trust, 1, trust_args_printable);
+   grub_test_assert (err == GRUB_ERR_NONE,
+-		    "distrusting printable certificate failed: %d: %s",
++		    "trusting printable certificate failed: %d: %s",
++		    grub_errno, grub_errmsg);
++
++  err = (cmd_trust->func) (cmd_trust, 1, trust_args_eku);
++  grub_test_assert (err == GRUB_ERR_NONE,
++		    "trusting certificate with extended key usage failed: %d: %s",
+ 		    grub_errno, grub_errmsg);
+ 
+   grub_procfs_unregister (&certificate_der_entry);
+   grub_procfs_unregister (&certificate2_der_entry);
+   grub_procfs_unregister (&certificate_printable_der_entry);
++  grub_procfs_unregister (&certificate_eku_der_entry);
+ }
+ 
+ GRUB_FUNCTIONAL_TEST (appended_signature_test, appended_signature_test);
+diff --git a/grub-core/tests/appended_signatures.h b/grub-core/tests/appended_signatures.h
+index aa3dc6278e3..2e5ebd7d8bd 100644
+--- a/grub-core/tests/appended_signatures.h
++++ b/grub-core/tests/appended_signatures.h
+@@ -555,3 +555,84 @@ unsigned char certificate_printable_der[] = {
+   0xd2
+ };
+ unsigned int certificate_printable_der_len = 829;
++
++unsigned char certificate_eku_der[] = {
++  0x30, 0x82, 0x03, 0x90, 0x30, 0x82, 0x02, 0x78, 0xa0, 0x03, 0x02, 0x01,
++  0x02, 0x02, 0x09, 0x00, 0xd3, 0x9c, 0x41, 0x33, 0xdd, 0x6b, 0x5f, 0x45,
++  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
++  0x0b, 0x05, 0x00, 0x30, 0x47, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
++  0x04, 0x03, 0x0c, 0x18, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20,
++  0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20,
++  0x43, 0x41, 0x20, 0x36, 0x31, 0x22, 0x30, 0x20, 0x06, 0x09, 0x2a, 0x86,
++  0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x13, 0x73, 0x65, 0x63,
++  0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65, 0x64, 0x68, 0x61, 0x74,
++  0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x32,
++  0x31, 0x35, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a, 0x17, 0x0d, 0x33,
++  0x38, 0x30, 0x31, 0x31, 0x37, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a,
++  0x30, 0x4e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
++  0x1f, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, 0x53, 0x65, 0x63,
++  0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, 0x53, 0x69, 0x67,
++  0x6e, 0x69, 0x6e, 0x67, 0x20, 0x36, 0x30, 0x32, 0x31, 0x22, 0x30, 0x20,
++  0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16,
++  0x13, 0x73, 0x65, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65,
++  0x64, 0x68, 0x61, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22,
++  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
++  0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
++  0x02, 0x82, 0x01, 0x01, 0x00, 0xaa, 0x6f, 0xbb, 0x92, 0x77, 0xd7, 0x15,
++  0xef, 0x88, 0x80, 0x88, 0xc0, 0xe7, 0x89, 0xeb, 0x35, 0x76, 0xf4, 0x85,
++  0x05, 0x0f, 0x19, 0xe4, 0x5f, 0x25, 0xdd, 0xc1, 0xa2, 0xe5, 0x5c, 0x06,
++  0xfb, 0xf1, 0x06, 0xb5, 0x65, 0x45, 0xcb, 0xbd, 0x19, 0x33, 0x54, 0xb5,
++  0x1a, 0xcd, 0xe4, 0xa8, 0x35, 0x2a, 0xfe, 0x9c, 0x53, 0xf4, 0xc6, 0x76,
++  0xdb, 0x1f, 0x8a, 0xd4, 0x7b, 0x18, 0x11, 0xaf, 0xa3, 0x90, 0xd4, 0xdd,
++  0x4d, 0xd5, 0x42, 0xcc, 0x14, 0x9a, 0x64, 0x6b, 0xc0, 0x7f, 0xaa, 0x1c,
++  0x94, 0x47, 0x4d, 0x79, 0xbd, 0x57, 0x9a, 0xbf, 0x99, 0x4e, 0x96, 0xa9,
++  0x31, 0x2c, 0xa9, 0xe7, 0x14, 0x65, 0x86, 0xc8, 0xac, 0x79, 0x5e, 0x78,
++  0xa4, 0x3c, 0x00, 0x24, 0xd3, 0xf7, 0xe1, 0xf5, 0x12, 0xad, 0xa0, 0x29,
++  0xe5, 0xfe, 0x80, 0xae, 0xf8, 0xaa, 0x60, 0x36, 0xe7, 0xe8, 0x94, 0xcb,
++  0xe9, 0xd1, 0xcc, 0x0b, 0x4d, 0xf7, 0xde, 0xeb, 0x52, 0xd2, 0x73, 0x09,
++  0x28, 0xdf, 0x48, 0x99, 0x53, 0x9f, 0xc5, 0x9a, 0xd4, 0x36, 0xa3, 0xc6,
++  0x5e, 0x8d, 0xbe, 0xd5, 0xdc, 0x76, 0xb4, 0x74, 0xb8, 0x26, 0x18, 0x27,
++  0xfb, 0xf2, 0xfb, 0xd0, 0x9b, 0x3d, 0x7f, 0x10, 0xe2, 0xab, 0x44, 0xc7,
++  0x88, 0x7f, 0xb4, 0x3d, 0x3e, 0xa3, 0xff, 0x6d, 0x06, 0x4b, 0x3e, 0x55,
++  0xb2, 0x84, 0xf4, 0xad, 0x54, 0x88, 0x81, 0xc3, 0x9c, 0xf8, 0xb6, 0x68,
++  0x96, 0x38, 0x8b, 0xcd, 0x90, 0x6d, 0x25, 0x4b, 0xbf, 0x0c, 0x44, 0x90,
++  0xa5, 0x5b, 0x98, 0xd0, 0x40, 0x2f, 0xbb, 0x0d, 0xa8, 0x4b, 0x8a, 0x62,
++  0x82, 0x46, 0x46, 0x18, 0x38, 0xae, 0x82, 0x07, 0xd0, 0xb4, 0x2f, 0x16,
++  0x79, 0x55, 0x9f, 0x1b, 0xc5, 0x08, 0x6d, 0x85, 0xdf, 0x3f, 0xa9, 0x9b,
++  0x4b, 0xc6, 0x28, 0xd3, 0x58, 0x72, 0x3d, 0x37, 0x11, 0x02, 0x03, 0x01,
++  0x00, 0x01, 0xa3, 0x78, 0x30, 0x76, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d,
++  0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0e, 0x06, 0x03,
++  0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80,
++  0x30, 0x16, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x01, 0x01, 0xff, 0x04, 0x0c,
++  0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03,
++  0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x6c,
++  0xe4, 0x6c, 0x27, 0xaa, 0xcd, 0x0d, 0x4b, 0x74, 0x21, 0xa4, 0xf6, 0x5f,
++  0x87, 0xb5, 0x31, 0xfe, 0x10, 0xbb, 0xa7, 0x30, 0x1f, 0x06, 0x03, 0x55,
++  0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe8, 0x6a, 0x1c, 0xab,
++  0x2c, 0x48, 0xf9, 0x60, 0x36, 0xa2, 0xf0, 0x7b, 0x8e, 0xd2, 0x9d, 0xb4,
++  0x2a, 0x28, 0x98, 0xc8, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
++  0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
++  0x55, 0x34, 0xe2, 0xfa, 0xf6, 0x89, 0x86, 0xad, 0x92, 0x21, 0xec, 0xb9,
++  0x54, 0x0e, 0x18, 0x47, 0x0d, 0x1b, 0xa7, 0x58, 0xad, 0x69, 0xe4, 0xef,
++  0x3b, 0xe6, 0x8d, 0xdd, 0xda, 0x0c, 0x45, 0xf6, 0xe8, 0x96, 0xa4, 0x29,
++  0x0f, 0xbb, 0xcf, 0x16, 0xae, 0x93, 0xd0, 0xcb, 0x2a, 0x26, 0x1a, 0x7b,
++  0xfc, 0x51, 0x22, 0x76, 0x98, 0x31, 0xa7, 0x0f, 0x29, 0x35, 0x79, 0xbf,
++  0xe2, 0x4f, 0x0f, 0x14, 0xf5, 0x1f, 0xcb, 0xbf, 0x87, 0x65, 0x13, 0x32,
++  0xa3, 0x19, 0x4a, 0xd1, 0x3f, 0x45, 0xd4, 0x4b, 0xe2, 0x00, 0x26, 0xa9,
++  0x3e, 0xd7, 0xa5, 0x37, 0x9f, 0xf5, 0xad, 0x61, 0xe2, 0x40, 0xa9, 0x74,
++  0x24, 0x53, 0xf2, 0x78, 0xeb, 0x10, 0x9b, 0x2c, 0x27, 0x88, 0x46, 0xcb,
++  0xe4, 0x60, 0xca, 0xf5, 0x06, 0x24, 0x40, 0x2a, 0x97, 0x3a, 0xcc, 0xd0,
++  0x81, 0xb1, 0x15, 0xa3, 0x4f, 0xd0, 0x2b, 0x4f, 0xca, 0x6e, 0xaa, 0x24,
++  0x31, 0xb3, 0xac, 0xa6, 0x75, 0x05, 0xfe, 0x8a, 0xf4, 0x41, 0xc4, 0x06,
++  0x8a, 0xc7, 0x0a, 0x83, 0x4e, 0x49, 0xd4, 0x3f, 0x83, 0x50, 0xec, 0x57,
++  0x04, 0x97, 0x14, 0x49, 0xf5, 0xe1, 0xb1, 0x7a, 0x9c, 0x09, 0x4f, 0x61,
++  0x87, 0xc3, 0x97, 0x22, 0x17, 0xc2, 0xeb, 0xcc, 0x32, 0x81, 0x31, 0x21,
++  0x3f, 0x10, 0x57, 0x5b, 0x43, 0xbe, 0xcd, 0x68, 0x82, 0xbe, 0xe5, 0xc1,
++  0x65, 0x94, 0x7e, 0xc2, 0x34, 0x76, 0x2b, 0xcf, 0x89, 0x3c, 0x2b, 0x81,
++  0x23, 0x72, 0x95, 0xcf, 0xc9, 0x67, 0x19, 0x2a, 0xd5, 0x5c, 0xca, 0xa3,
++  0x46, 0xbd, 0x48, 0x06, 0x0b, 0xa6, 0xa3, 0x96, 0x50, 0x28, 0xc7, 0x7e,
++  0xcf, 0x62, 0xf2, 0xfa, 0xc4, 0xf2, 0x53, 0xe3, 0xc9, 0xe8, 0x2e, 0xdd,
++  0x29, 0x37, 0x07, 0x47, 0xff, 0xff, 0x8a, 0x32, 0xbd, 0xa2, 0xb7, 0x21,
++  0x89, 0xa0, 0x55, 0xf7
++};
++unsigned int certificate_eku_der_len = 916;
diff --git a/SOURCES/0202-ieee1275-ofdisk-retry-on-open-failure.patch b/SOURCES/0202-ieee1275-ofdisk-retry-on-open-failure.patch
new file mode 100644
index 0000000..275d14f
--- /dev/null
+++ b/SOURCES/0202-ieee1275-ofdisk-retry-on-open-failure.patch
@@ -0,0 +1,103 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Diego Domingos <diegodo@br.ibm.com>
+Date: Wed, 10 Mar 2021 14:17:52 -0500
+Subject: [PATCH] ieee1275/ofdisk: retry on open failure
+
+This patch aims to make grub more robust when booting from SAN/Multipath disks.
+
+If a path is failing intermittently so grub will retry the OPEN and READ the
+disk (grub_ieee1275_open and grub_ieee1275_read) until the total amount of times
+specified in MAX_RETRIES.
+
+Signed-off-by: Diego Domingos <diegodo@br.ibm.com>
+---
+ grub-core/disk/ieee1275/ofdisk.c | 25 ++++++++++++++++++++-----
+ include/grub/ieee1275/ofdisk.h   |  8 ++++++++
+ 2 files changed, 28 insertions(+), 5 deletions(-)
+
+diff --git a/grub-core/disk/ieee1275/ofdisk.c b/grub-core/disk/ieee1275/ofdisk.c
+index ea7f78ac7d8..55346849d35 100644
+--- a/grub-core/disk/ieee1275/ofdisk.c
++++ b/grub-core/disk/ieee1275/ofdisk.c
+@@ -225,7 +225,9 @@ dev_iterate (const struct grub_ieee1275_devalias *alias)
+       char *buf, *bufptr;
+       unsigned i;
+ 
+-      if (grub_ieee1275_open (alias->path, &ihandle))
++
++      RETRY_IEEE1275_OFDISK_OPEN(alias->path, &ihandle)
++      if (! ihandle)
+ 	return;
+ 
+       /* This method doesn't need memory allocation for the table. Open
+@@ -305,7 +307,9 @@ dev_iterate (const struct grub_ieee1275_devalias *alias)
+           return;
+         }
+ 
+-      if (grub_ieee1275_open (alias->path, &ihandle))
++      RETRY_IEEE1275_OFDISK_OPEN(alias->path, &ihandle);
++
++      if (! ihandle)
+         {
+           grub_free (buf);
+           grub_free (table);
+@@ -495,7 +499,7 @@ grub_ofdisk_open (const char *name, grub_disk_t disk)
+     last_ihandle = 0;
+     last_devpath = NULL;
+ 
+-    grub_ieee1275_open (op->open_path, &last_ihandle);
++    RETRY_IEEE1275_OFDISK_OPEN(op->open_path, &last_ihandle);
+     if (! last_ihandle)
+       return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device");
+     last_devpath = op->open_path;
+@@ -571,7 +575,7 @@ grub_ofdisk_prepare (grub_disk_t disk, grub_disk_addr_t sector)
+       last_ihandle = 0;
+       last_devpath = NULL;
+ 
+-      grub_ieee1275_open (disk->data, &last_ihandle);
++      RETRY_IEEE1275_OFDISK_OPEN(disk->data, &last_ihandle);
+       if (! last_ihandle)
+ 	return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device");
+       last_devpath = disk->data;      
+@@ -598,12 +602,23 @@ grub_ofdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
+     return err;
+   grub_ieee1275_read (last_ihandle, buf, size  << disk->log_sector_size,
+ 		      &actual);
+-  if (actual != (grub_ssize_t) (size  << disk->log_sector_size))
++  int i = 0;
++  while(actual != (grub_ssize_t) (size  << disk->log_sector_size)){
++    if (i>MAX_RETRIES){
+     return grub_error (GRUB_ERR_READ_ERROR, N_("failure reading sector 0x%llx "
+ 					       "from `%s'"),
+ 		       (unsigned long long) sector,
+ 		       disk->name);
++    }
++    last_devpath = NULL;
++    err = grub_ofdisk_prepare (disk, sector);
++    if (err)
++      return err;
+ 
++    grub_ieee1275_read (last_ihandle, buf, size  << disk->log_sector_size,
++                      &actual);
++    i++;
++  }
+   return 0;
+ }
+ 
+diff --git a/include/grub/ieee1275/ofdisk.h b/include/grub/ieee1275/ofdisk.h
+index 2f69e3f191d..7d2d5409305 100644
+--- a/include/grub/ieee1275/ofdisk.h
++++ b/include/grub/ieee1275/ofdisk.h
+@@ -22,4 +22,12 @@
+ extern void grub_ofdisk_init (void);
+ extern void grub_ofdisk_fini (void);
+ 
++#define MAX_RETRIES 20
++
++
++#define RETRY_IEEE1275_OFDISK_OPEN(device, last_ihandle) unsigned retry_i=0;for(retry_i=0; retry_i < MAX_RETRIES; retry_i++){ \
++						if(!grub_ieee1275_open(device, last_ihandle)) \
++						break; \
++						grub_dprintf("ofdisk","Opening disk %s failed. Retrying...\n",device); }
++
+ #endif /* ! GRUB_INIT_HEADER */
diff --git a/SOURCES/0203-01_menu_auto_hide.in-fix-a-then-than-typo.patch b/SOURCES/0203-01_menu_auto_hide.in-fix-a-then-than-typo.patch
new file mode 100644
index 0000000..4847ef9
--- /dev/null
+++ b/SOURCES/0203-01_menu_auto_hide.in-fix-a-then-than-typo.patch
@@ -0,0 +1,26 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= <jpokorny@fedoraproject.org>
+Date: Fri, 11 Jun 2021 12:57:56 +0200
+Subject: [PATCH] 01_menu_auto_hide.in: fix a then/than typo
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Jan Pokorný <jpokorny@fedoraproject.org>
+---
+ util/grub.d/10_reset_boot_success.in | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/util/grub.d/10_reset_boot_success.in b/util/grub.d/10_reset_boot_success.in
+index 737e1ae5b68..e73f4137b36 100644
+--- a/util/grub.d/10_reset_boot_success.in
++++ b/util/grub.d/10_reset_boot_success.in
+@@ -15,7 +15,7 @@ fi
+ # Reset boot_indeterminate after a successful boot
+ if [ "\${boot_success}" = "1" ] ; then
+   set boot_indeterminate=0
+-# Avoid boot_indeterminate causing the menu to be hidden more then once
++# Avoid boot_indeterminate causing the menu to be hidden more than once
+ elif [ "\${boot_indeterminate}" = "1" ]; then
+   set boot_indeterminate=2
+ fi
diff --git a/SOURCES/0204-Fix-disabling-grub-rpm-sort.patch b/SOURCES/0204-Fix-disabling-grub-rpm-sort.patch
new file mode 100644
index 0000000..dccde3c
--- /dev/null
+++ b/SOURCES/0204-Fix-disabling-grub-rpm-sort.patch
@@ -0,0 +1,59 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tim Landscheidt <tim@tim-landscheidt.de>
+Date: Fri, 11 Jun 2021 13:02:37 +0200
+Subject: [PATCH] Fix disabling grub-rpm-sort
+
+Currently, grub-rpm-sort is unconditionally compiled whether
+./configure has been called with --disable-rpm-sort or not.  This adds
+the necessary logic to configure.ac and Makefile.util.def and some
+debug output to ./configure and fixes #44.
+---
+ configure.ac      | 8 ++++++++
+ Makefile.util.def | 1 +
+ 2 files changed, 9 insertions(+)
+
+diff --git a/configure.ac b/configure.ac
+index d5d2a28b4ef..c7842ec29d8 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1936,6 +1936,8 @@ AC_ARG_ENABLE([rpm-sort],
+                               [enable native rpm sorting of kernels in grub (default=guessed)])])
+ if test x"$enable_rpm_sort" = xno ; then
+   rpm_sort_excuse="explicitly disabled"
++else
++  enable_rpm_sort=yes
+ fi
+ 
+ if test x"$rpm_sort_excuse" = x ; then
+@@ -2200,6 +2202,7 @@ AM_CONDITIONAL([COND_GRUB_EMU_SDL], [test x$enable_grub_emu_sdl = xyes])
+ AM_CONDITIONAL([COND_GRUB_EMU_PCI], [test x$enable_grub_emu_pci = xyes])
+ AM_CONDITIONAL([COND_GRUB_MKFONT], [test x$enable_grub_mkfont = xyes])
+ AM_CONDITIONAL([COND_GRUB_MOUNT], [test x$enable_grub_mount = xyes])
++AM_CONDITIONAL([COND_GRUB_RPM_SORT], [test x$enable_rpm_sort = xyes])
+ AM_CONDITIONAL([COND_HAVE_FONT_SOURCE], [test x$FONT_SOURCE != x])
+ if test x$FONT_SOURCE != x ; then
+    HAVE_FONT_SOURCE=1
+@@ -2328,6 +2331,11 @@ echo grub-mount: Yes
+ else
+ echo grub-mount: No "($grub_mount_excuse)"
+ fi
++if [ x"$rpm_sort_excuse" = x ]; then
++echo grub-rpm-sort: Yes
++else
++echo grub-rpm-sort: No "($rpm_sort_excuse)"
++fi
+ if [ x"$starfield_excuse" = x ]; then
+ echo starfield theme: Yes
+ echo With DejaVuSans font from $DJVU_FONT_SOURCE
+diff --git a/Makefile.util.def b/Makefile.util.def
+index 8cfbe69a76e..3f191aa8095 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -774,6 +774,7 @@ program = {
+   ldadd = libgrubkern.a;
+   ldadd = grub-core/lib/gnulib/libgnu.a;
+   ldadd = '$(LIBDEVMAPPER) $(LIBRPM)';
++  condition = COND_GRUB_RPM_SORT;
+ };
+ 
+ script = {
diff --git a/SOURCES/0205-Don-t-check-for-rpmvercmp-in-librpm.patch b/SOURCES/0205-Don-t-check-for-rpmvercmp-in-librpm.patch
new file mode 100644
index 0000000..c21dca4
--- /dev/null
+++ b/SOURCES/0205-Don-t-check-for-rpmvercmp-in-librpm.patch
@@ -0,0 +1,57 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Fri, 11 Jun 2021 13:13:27 +0200
+Subject: [PATCH] Don't check for rpmvercmp in librpm
+
+The rpmvercmp() function was moved from librpm to librpmio. The configure
+option had some logic to first check if the symbol is in librpm and then
+librpmio if this check didn't succeed.
+
+But the logic wasn't working and rpm sorting was always disabled. Instead
+of trying to fix this logic, let's just remove since the function already
+moved and there's no need to check librpm anymore. Now it's enabled again:
+
+  GRUB2 will be compiled with following components:
+  ...
+  grub-rpm-sort: Yes
+  ...
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ configure.ac | 19 +++++--------------
+ 1 file changed, 5 insertions(+), 14 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index c7842ec29d8..3c808a72230 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1947,24 +1947,15 @@ if test x"$rpm_sort_excuse" = x ; then
+ fi
+ 
+ if test x"$rpm_sort_excuse" = x ; then
+-  # Check for rpm library.
+-  AC_CHECK_LIB([rpm], [rpmvercmp], [],
+-               [rpm_sort_excuse="rpmlib missing rpmvercmp"])
+-fi
+-
+-if test x"$rpm_sort_excuse" = x ; then
+-   LIBRPM="-lrpm";
+-   AC_DEFINE([HAVE_RPM], [1],
+-             [Define to 1 if you have the rpm library.])
+-fi
+-
+-if test x"$LIBRPM" = x ; then
+-  # Check for rpm library.
++  # Check for rpmio library.
+   AC_CHECK_LIB([rpmio], [rpmvercmp], [],
+                [rpm_sort_excuse="rpmio missing rpmvercmp"])
++fi
++
++if test x"$rpm_sort_excuse" = x ; then
+    LIBRPM="-lrpmio";
+    AC_DEFINE([HAVE_RPMIO], [1],
+-             [Define to 1 if you have the rpm library.])
++             [Define to 1 if you have the rpmio library.])
+ fi
+ 
+ AC_SUBST([LIBRPM])
diff --git a/SOURCES/0206-Allow-chainloading-EFI-apps-from-loop-mounts.patch b/SOURCES/0206-Allow-chainloading-EFI-apps-from-loop-mounts.patch
new file mode 100644
index 0000000..e48f102
--- /dev/null
+++ b/SOURCES/0206-Allow-chainloading-EFI-apps-from-loop-mounts.patch
@@ -0,0 +1,136 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Dimitri John Ledkov <xnox@ubuntu.com>
+Date: Fri, 11 Jun 2021 13:51:20 +0200
+Subject: [PATCH] Allow chainloading EFI apps from loop mounts.
+
+---
+ grub-core/disk/loopback.c          |  9 +--------
+ grub-core/loader/efi/chainloader.c | 23 +++++++++++++++++++++++
+ include/grub/loopback.h            | 30 ++++++++++++++++++++++++++++++
+ 3 files changed, 54 insertions(+), 8 deletions(-)
+ create mode 100644 include/grub/loopback.h
+
+diff --git a/grub-core/disk/loopback.c b/grub-core/disk/loopback.c
+index 41bebd14fe3..99f47924ec2 100644
+--- a/grub-core/disk/loopback.c
++++ b/grub-core/disk/loopback.c
+@@ -21,20 +21,13 @@
+ #include <grub/misc.h>
+ #include <grub/file.h>
+ #include <grub/disk.h>
++#include <grub/loopback.h>
+ #include <grub/mm.h>
+ #include <grub/extcmd.h>
+ #include <grub/i18n.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+-struct grub_loopback
+-{
+-  char *devname;
+-  grub_file_t file;
+-  struct grub_loopback *next;
+-  unsigned long id;
+-};
+-
+ static struct grub_loopback *loopback_list;
+ static unsigned long last_id = 0;
+ 
+diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
+index d41e8ea14a8..3af6b122926 100644
+--- a/grub-core/loader/efi/chainloader.c
++++ b/grub-core/loader/efi/chainloader.c
+@@ -24,6 +24,7 @@
+ #include <grub/err.h>
+ #include <grub/device.h>
+ #include <grub/disk.h>
++#include <grub/loopback.h>
+ #include <grub/misc.h>
+ #include <grub/charset.h>
+ #include <grub/mm.h>
+@@ -901,6 +902,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+   grub_efi_status_t status;
+   grub_efi_boot_services_t *b;
+   grub_device_t dev = 0;
++  grub_device_t orig_dev = 0;
+   grub_efi_device_path_t *dp = 0;
+   char *filename;
+   void *boot_image = 0;
+@@ -958,6 +960,15 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+   if (! dev)
+     goto fail;
+ 
++  /* if device is loopback, use underlying dev */
++  if (dev->disk->dev->id == GRUB_DISK_DEVICE_LOOPBACK_ID)
++    {
++      struct grub_loopback *d;
++      orig_dev = dev;
++      d = dev->disk->data;
++      dev = d->file->device;
++    }
++
+   if (dev->disk)
+     dev_handle = grub_efidisk_get_device_handle (dev->disk);
+   else if (dev->net && dev->net->server)
+@@ -1065,6 +1076,12 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+     }
+ #endif
+ 
++  if (orig_dev)
++    {
++      dev = orig_dev;
++      orig_dev = 0;
++    }
++
+   rc = grub_linuxefi_secure_validate((void *)(unsigned long)address, fsize);
+   grub_dprintf ("chain", "linuxefi_secure_validate: %d\n", rc);
+   if (rc > 0)
+@@ -1087,6 +1104,12 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+   // -1 fall-through to fail
+ 
+ fail:
++  if (orig_dev)
++    {
++      dev = orig_dev;
++      orig_dev = 0;
++    }
++
+   if (dev)
+     grub_device_close (dev);
+ 
+diff --git a/include/grub/loopback.h b/include/grub/loopback.h
+new file mode 100644
+index 00000000000..3b9a9e32e80
+--- /dev/null
++++ b/include/grub/loopback.h
+@@ -0,0 +1,30 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2019  Free Software Foundation, Inc.
++ *
++ *  GRUB is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef GRUB_LOOPBACK_HEADER
++#define GRUB_LOOPBACK_HEADER	1
++
++struct grub_loopback
++{
++  char *devname;
++  grub_file_t file;
++  struct grub_loopback *next;
++  unsigned long id;
++};
++
++#endif /* ! GRUB_LOOPBACK_HEADER */
diff --git a/SOURCES/0207-efinet-Add-DHCP-proxy-support.patch b/SOURCES/0207-efinet-Add-DHCP-proxy-support.patch
new file mode 100644
index 0000000..a8755a3
--- /dev/null
+++ b/SOURCES/0207-efinet-Add-DHCP-proxy-support.patch
@@ -0,0 +1,50 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Ian Page Hands <iphands@gmail.com>
+Date: Tue, 8 Jun 2021 13:48:56 -0400
+Subject: [PATCH] efinet: Add DHCP proxy support
+
+If a proxyDHCP configuration is used, the server name, server IP and boot
+file values should be taken from the DHCP proxy offer instead of the DHCP
+server ack packet. Currently that case is not handled, add support for it.
+---
+ grub-core/net/drivers/efi/efinet.c | 25 +++++++++++++++++++++++--
+ 1 file changed, 23 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
+index e11d759f19a..1a24f38a21a 100644
+--- a/grub-core/net/drivers/efi/efinet.c
++++ b/grub-core/net/drivers/efi/efinet.c
+@@ -850,10 +850,31 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+     else
+       {
+ 	grub_dprintf ("efinet", "using ipv4 and dhcp\n");
++
++        struct grub_net_bootp_packet *dhcp_ack = &pxe_mode->dhcp_ack;
++
++        if (pxe_mode->proxy_offer_received)
++          {
++            grub_dprintf ("efinet", "proxy offer receive");
++            struct grub_net_bootp_packet *proxy_offer = &pxe_mode->proxy_offer;
++
++            if (proxy_offer && dhcp_ack->boot_file[0] == '\0')
++              {
++                grub_dprintf ("efinet", "setting values from proxy offer");
++                /* Here we got a proxy offer and the dhcp_ack has a nil boot_file
++                 * Copy the proxy DHCP offer details into the bootp_packet we are
++                 * sending forward as they are the deatils we need.
++                 */
++                *dhcp_ack->server_name = *proxy_offer->server_name;
++                *dhcp_ack->boot_file   = *proxy_offer->boot_file;
++                dhcp_ack->server_ip    = proxy_offer->server_ip;
++              }
++          }
++
+ 	grub_net_configure_by_dhcp_ack (card->name, card, 0,
+ 					(struct grub_net_bootp_packet *)
+-					packet_buf,
+-					packet_bufsz,
++					&pxe_mode->dhcp_ack,
++					sizeof (pxe_mode->dhcp_ack),
+ 					1, device, path);
+ 	grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path);
+       }
diff --git a/SOURCES/0208-fs-ext2-Ignore-checksum-seed-incompat-feature.patch b/SOURCES/0208-fs-ext2-Ignore-checksum-seed-incompat-feature.patch
new file mode 100644
index 0000000..b4a5fd4
--- /dev/null
+++ b/SOURCES/0208-fs-ext2-Ignore-checksum-seed-incompat-feature.patch
@@ -0,0 +1,54 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Fri, 11 Jun 2021 00:01:29 +0200
+Subject: [PATCH] fs/ext2: Ignore checksum seed incompat feature
+
+This incompat feature is used to denote that the filesystem stored its
+metadata checksum seed in the superblock. This is used to allow tune2fs
+to change the UUID on a mounted metadata_csum filesystem without having
+to rewrite all the disk metadata.
+
+But GRUB doesn't use the metadata checksum in anyway, so can just ignore
+this feature if is enabled. This is consistent with GRUB filesystem code
+in general which just does a best effort to access the filesystem's data.
+
+It may be removed from the ignored list in the future if supports to do
+metadata checksumming verification is added to the read-only FS driver.
+
+Suggested-by: Eric Sandeen <esandeen@redhat.com>
+Suggested-by: Lukas Czerner <lczerner@redhat.com>
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/fs/ext2.c | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/fs/ext2.c b/grub-core/fs/ext2.c
+index e7dd78e6635..731d346f886 100644
+--- a/grub-core/fs/ext2.c
++++ b/grub-core/fs/ext2.c
+@@ -103,6 +103,7 @@ GRUB_MOD_LICENSE ("GPLv3+");
+ #define EXT4_FEATURE_INCOMPAT_64BIT		0x0080
+ #define EXT4_FEATURE_INCOMPAT_MMP		0x0100
+ #define EXT4_FEATURE_INCOMPAT_FLEX_BG		0x0200
++#define EXT4_FEATURE_INCOMPAT_CSUM_SEED		0x2000
+ #define EXT4_FEATURE_INCOMPAT_ENCRYPT          0x10000
+ 
+ /* The set of back-incompatible features this driver DOES support. Add (OR)
+@@ -123,9 +124,16 @@ GRUB_MOD_LICENSE ("GPLv3+");
+  * mmp:            Not really back-incompatible - was added as such to
+  *                 avoid multiple read-write mounts. Safe to ignore for this
+  *                 RO driver.
++ * checksum seed:  Not really back-incompatible - was added to allow tools
++ *                 such as tune2fs to change the UUID on a mounted metadata
++ *                 checksummed filesystem. Safe to ignore for now since the
++ *                 driver doesn't support checksum verification. But it must
++ *                 be removed from this list if that support is added later.
++ *
+  */
+ #define EXT2_DRIVER_IGNORED_INCOMPAT ( EXT3_FEATURE_INCOMPAT_RECOVER \
+-				     | EXT4_FEATURE_INCOMPAT_MMP)
++				     | EXT4_FEATURE_INCOMPAT_MMP \
++				     | EXT4_FEATURE_INCOMPAT_CSUM_SEED)
+ 
+ 
+ #define EXT3_JOURNAL_MAGIC_NUMBER	0xc03b3998U
diff --git a/SOURCES/0209-Don-t-update-the-cmdline-when-generating-legacy-menu.patch b/SOURCES/0209-Don-t-update-the-cmdline-when-generating-legacy-menu.patch
new file mode 100644
index 0000000..c55b7e3
--- /dev/null
+++ b/SOURCES/0209-Don-t-update-the-cmdline-when-generating-legacy-menu.patch
@@ -0,0 +1,36 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Thu, 17 Jun 2021 14:31:42 +0200
+Subject: [PATCH] Don't update the cmdline when generating legacy menuentry
+ commands
+
+On OPAL ppc64le machines with an old petitboot version that doesn't have
+support to parse BLS snippets, the grub2-mkconfig script is executed to
+generate menuentry commands from the BLS snippets.
+
+In this case, the script is executed with the --no-grubenv-update option
+that indicates that no side effects should happen when running the script.
+
+But the options field in the BLS snippets are updated regardless, only do
+the update if --no-grubenv-update was not used.
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ util/grub.d/10_linux.in | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
+index 68adb55d893..c9296154f51 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -261,7 +261,9 @@ if [ -z "\${kernelopts}" ]; then
+ fi
+ EOF
+ 
+-  update_bls_cmdline
++  if [ "x${GRUB_GRUBENV_UPDATE}" = "xyes" ]; then
++      update_bls_cmdline
++  fi
+ 
+   if [ "x${BLS_POPULATE_MENU}" = "xtrue" ]; then
+       populate_menu
diff --git a/SOURCES/0210-Suppress-gettext-error-message.patch b/SOURCES/0210-Suppress-gettext-error-message.patch
new file mode 100644
index 0000000..c57e7ff
--- /dev/null
+++ b/SOURCES/0210-Suppress-gettext-error-message.patch
@@ -0,0 +1,33 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Paulo Flabiano Smorigo <pfsmorigo@br.ibm.com>
+Date: Tue, 29 Jun 2021 13:17:42 +0200
+Subject: [PATCH] Suppress gettext error message
+
+Colin Watson's patch from comment #11 on the upstream bug:
+https://savannah.gnu.org/bugs/?35880#comment11
+
+Resolves: rhbz#1592124
+
+Signed-off-by: Paulo Flabiano Smorigo <pfsmorigo@br.ibm.com>
+---
+ grub-core/gettext/gettext.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/grub-core/gettext/gettext.c b/grub-core/gettext/gettext.c
+index 84d520cd494..87a912ac6e5 100644
+--- a/grub-core/gettext/gettext.c
++++ b/grub-core/gettext/gettext.c
+@@ -424,6 +424,13 @@ grub_gettext_init_ext (struct grub_gettext_context *ctx,
+       grub_free (lang);
+     }
+ 
++  /* If no translations are available, fall back to untranslated text. */
++  if (err == GRUB_ERR_FILE_NOT_FOUND)
++    {
++      grub_errno = GRUB_ERR_NONE;
++      return 0;
++    }
++
+   if (locale[0] == 'e' && locale[1] == 'n'
+       && (locale[2] == '\0' || locale[2] == '_'))
+     grub_errno = err = GRUB_ERR_NONE;
diff --git a/SOURCES/0211-grub-boot-success.timer-Only-run-if-not-in-a-contain.patch b/SOURCES/0211-grub-boot-success.timer-Only-run-if-not-in-a-contain.patch
new file mode 100644
index 0000000..61d0f6d
--- /dev/null
+++ b/SOURCES/0211-grub-boot-success.timer-Only-run-if-not-in-a-contain.patch
@@ -0,0 +1,32 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Gena Makhomed <gmm@csdoc.com>
+Date: Thu, 1 Jul 2021 01:07:46 +0200
+Subject: [PATCH] grub-boot-success.timer: Only run if not in a container
+
+The grub-boot-success.timer should be disabled inside a container since it
+leads to the following error:
+
+Jan 09 22:56:38 test sshd[8786]: pam_unix(sshd:session): session opened for user www(uid=1000) by (uid=0)
+Jan 09 22:58:39 test systemd[8857]: Starting Mark boot as successful...
+Jan 09 22:58:39 test systemd[8857]: grub-boot-success.service: Main process exited, code=exited, status=1/FAILURE
+Jan 09 22:58:39 test systemd[8857]: grub-boot-success.service: Failed with result 'exit-code'.
+Jan 09 22:58:39 test systemd[8857]: Failed to start Mark boot as successful.
+Jan 09 22:58:39 test grub2-set-bootflag[10034]: Error canonicalizing /boot/grub2/grubenv filename: No such file or directory
+
+Resolves: rhbz#1914571
+---
+ docs/grub-boot-success.timer | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/docs/grub-boot-success.timer b/docs/grub-boot-success.timer
+index 5d8fcba21aa..406f1720056 100644
+--- a/docs/grub-boot-success.timer
++++ b/docs/grub-boot-success.timer
+@@ -1,6 +1,7 @@
+ [Unit]
+ Description=Mark boot as successful after the user session has run 2 minutes
+ ConditionUser=!@system
++ConditionVirtualization=!container
+ 
+ [Timer]
+ OnActiveSec=2min
diff --git a/SOURCES/0212-grub-set-password-Always-use-boot-grub2-user.cfg-as-.patch b/SOURCES/0212-grub-set-password-Always-use-boot-grub2-user.cfg-as-.patch
new file mode 100644
index 0000000..7ecea22
--- /dev/null
+++ b/SOURCES/0212-grub-set-password-Always-use-boot-grub2-user.cfg-as-.patch
@@ -0,0 +1,42 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Mon, 5 Jul 2021 18:24:22 +0200
+Subject: [PATCH] grub-set-password: Always use /boot/grub2/user.cfg as
+ password default
+
+The GRUB configuration file is always placed in /boot/grub2/ now, even for
+EFI. But the tool is still creating the user.cfg in the ESP and not there.
+
+Resolves: rhbz#1955294
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ util/grub-set-password.in | 9 +--------
+ 1 file changed, 1 insertion(+), 8 deletions(-)
+
+diff --git a/util/grub-set-password.in b/util/grub-set-password.in
+index c0b5ebbfdc5..d8005e5a142 100644
+--- a/util/grub-set-password.in
++++ b/util/grub-set-password.in
+@@ -1,11 +1,6 @@
+ #!/bin/sh -e
+ 
+-EFIDIR=$(grep ^ID= /etc/os-release | sed -e 's/^ID=//' -e 's/rhel/redhat/' -e 's/\"//g')
+-if [ -d /sys/firmware/efi/efivars/ ]; then
+-    grubdir=`echo "/@bootdirname@/efi/EFI/${EFIDIR}/" | sed 's,//*,/,g'`
+-else
+-    grubdir=`echo "/@bootdirname@/@grubdirname@" | sed 's,//*,/,g'`
+-fi
++grubdir=`echo "/@bootdirname@/@grubdirname@" | sed 's,//*,/,g'`
+ 
+ PACKAGE_VERSION="@PACKAGE_VERSION@"
+ PACKAGE_NAME="@PACKAGE_NAME@"
+@@ -116,8 +111,6 @@ if [ -z "${MYPASS}" ]; then
+       exit 1
+ fi
+ 
+-# on the ESP, these will fail to set the permissions, but it's okay because
+-# the directory is protected.
+ install -m 0600 /dev/null "${OUTPUT_PATH}/user.cfg" 2>/dev/null || :
+ chmod 0600 "${OUTPUT_PATH}/user.cfg" 2>/dev/null || :
+ echo "GRUB2_PASSWORD=${MYPASS}" > "${OUTPUT_PATH}/user.cfg"
diff --git a/SOURCES/0213-Remove-outdated-URL-for-BLS-document.patch b/SOURCES/0213-Remove-outdated-URL-for-BLS-document.patch
new file mode 100644
index 0000000..d380fe8
--- /dev/null
+++ b/SOURCES/0213-Remove-outdated-URL-for-BLS-document.patch
@@ -0,0 +1,28 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Mon, 5 Jul 2021 19:00:25 +0200
+Subject: [PATCH] Remove outdated URL for BLS document
+
+The document was moved to https://systemd.io/BOOT_LOADER_SPECIFICATION/,
+update the URL accordingly to point to the current location.
+
+Resolves: rhbz#1926453
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ util/grub.d/10_linux.in | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
+index c9296154f51..6ee0a2cf3d0 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -96,7 +96,7 @@ cat <<EOF
+ #
+ # The $bls_parser parses the BootLoaderSpec files stored in /boot/loader/entries and
+ # populates the boot menu. Please refer to the Boot Loader Specification documentation
+-# for the files format: https://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/.
++# for the files format: https://systemd.io/BOOT_LOADER_SPECIFICATION/.
+ 
+ EOF
+ }
diff --git a/SOURCES/0214-templates-Check-for-EFI-at-runtime-instead-of-config.patch b/SOURCES/0214-templates-Check-for-EFI-at-runtime-instead-of-config.patch
new file mode 100644
index 0000000..e36afa1
--- /dev/null
+++ b/SOURCES/0214-templates-Check-for-EFI-at-runtime-instead-of-config.patch
@@ -0,0 +1,63 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Tue, 6 Jul 2021 00:38:40 +0200
+Subject: [PATCH] templates: Check for EFI at runtime instead of config
+ generation time
+
+The 30_uefi-firmware template checks if an OsIndicationsSupported UEFI var
+exists and EFI_OS_INDICATIONS_BOOT_TO_FW_UI bit is set, to decide whether
+a "fwsetup" menu entry would be added or not to the GRUB menu.
+
+But this has the problem that it will only work if the configuration file
+was created on an UEFI machine that supports booting to a firmware UI.
+
+This for example doesn't support creating GRUB config files when executing
+on systems that support both UEFI and legacy BIOS booting. Since creating
+the config file from legacy BIOS wouldn't allow to access the firmware UI.
+
+To prevent this, make the template to unconditionally create the grub.cfg
+snippet but check at runtime if was booted through UEFI to decide if this
+entry should be added. That way it won't be added when booting with BIOS.
+
+There's no need to check if EFI_OS_INDICATIONS_BOOT_TO_FW_UI bit is set,
+since that's already done by the "fwsetup" command when is executed.
+
+Resolves: rhbz#1823864
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ util/grub.d/30_uefi-firmware.in | 21 ++++++++-------------
+ 1 file changed, 8 insertions(+), 13 deletions(-)
+
+diff --git a/util/grub.d/30_uefi-firmware.in b/util/grub.d/30_uefi-firmware.in
+index d344d3883d7..b6041b55e2a 100644
+--- a/util/grub.d/30_uefi-firmware.in
++++ b/util/grub.d/30_uefi-firmware.in
+@@ -26,19 +26,14 @@ export TEXTDOMAINDIR="@localedir@"
+ 
+ . "$pkgdatadir/grub-mkconfig_lib"
+ 
+-EFI_VARS_DIR=/sys/firmware/efi/efivars
+-EFI_GLOBAL_VARIABLE=8be4df61-93ca-11d2-aa0d-00e098032b8c
+-OS_INDICATIONS="$EFI_VARS_DIR/OsIndicationsSupported-$EFI_GLOBAL_VARIABLE"
++LABEL="UEFI Firmware Settings"
+ 
+-if [ -e "$OS_INDICATIONS" ] && \
+-   [ "$(( $(printf 0x%x \'"$(cat $OS_INDICATIONS | cut -b5)"\') & 1 ))" = 1 ]; then
+-  LABEL="UEFI Firmware Settings"
++gettext_printf "Adding boot menu entry for UEFI Firmware Settings ...\n" >&2
+ 
+-  gettext_printf "Adding boot menu entry for UEFI Firmware Settings ...\n" >&2
+-
+-  cat << EOF
+-menuentry '$LABEL' \$menuentry_id_option 'uefi-firmware' {
+-	fwsetup
+-}
+-EOF
++cat << EOF
++if [ "\$grub_platform" = "efi" ]; then
++	menuentry '$LABEL' \$menuentry_id_option 'uefi-firmware' {
++		fwsetup
++	}
+ fi
++EOF
diff --git a/SOURCES/0215-efi-Print-an-error-if-boot-to-firmware-setup-is-not-.patch b/SOURCES/0215-efi-Print-an-error-if-boot-to-firmware-setup-is-not-.patch
new file mode 100644
index 0000000..65b4aec
--- /dev/null
+++ b/SOURCES/0215-efi-Print-an-error-if-boot-to-firmware-setup-is-not-.patch
@@ -0,0 +1,92 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Tue, 6 Jul 2021 01:10:18 +0200
+Subject: [PATCH] efi: Print an error if boot to firmware setup is not
+ supported
+
+The "fwsetup" command is only registered if the firmware supports booting
+to the firmware setup UI. But it could be possible that the GRUB config
+already contains a "fwsetup" entry, because it was generated in a machine
+that has support for this feature.
+
+To prevent users getting a "can't find command `fwsetup`" error if it is
+not supported by the firmware, let's just always register the command but
+print a more accurate message if the firmware doesn't support this option.
+
+Resolves: rhbz#1823864
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/commands/efi/efifwsetup.c | 43 ++++++++++++++++++++-----------------
+ 1 file changed, 23 insertions(+), 20 deletions(-)
+
+diff --git a/grub-core/commands/efi/efifwsetup.c b/grub-core/commands/efi/efifwsetup.c
+index eaca0328388..328c45e82e0 100644
+--- a/grub-core/commands/efi/efifwsetup.c
++++ b/grub-core/commands/efi/efifwsetup.c
+@@ -27,6 +27,25 @@
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
++static grub_efi_boolean_t
++efifwsetup_is_supported (void)
++{
++  grub_efi_uint64_t *os_indications_supported = NULL;
++  grub_size_t oi_size = 0;
++  grub_efi_guid_t global = GRUB_EFI_GLOBAL_VARIABLE_GUID;
++
++  grub_efi_get_variable ("OsIndicationsSupported", &global, &oi_size,
++			 (void **) &os_indications_supported);
++
++  if (!os_indications_supported)
++    return 0;
++
++  if (*os_indications_supported & GRUB_EFI_OS_INDICATIONS_BOOT_TO_FW_UI)
++    return 1;
++
++  return 0;
++}
++
+ static grub_err_t
+ grub_cmd_fwsetup (grub_command_t cmd __attribute__ ((unused)),
+ 		  int argc __attribute__ ((unused)),
+@@ -38,6 +57,10 @@ grub_cmd_fwsetup (grub_command_t cmd __attribute__ ((unused)),
+   grub_size_t oi_size;
+   grub_efi_guid_t global = GRUB_EFI_GLOBAL_VARIABLE_GUID;
+ 
++  if (!efifwsetup_is_supported ())
++	  return grub_error (GRUB_ERR_INVALID_COMMAND,
++			     N_("Reboot to firmware setup is not supported"));
++
+   grub_efi_get_variable ("OsIndications", &global, &oi_size,
+ 			 (void **) &old_os_indications);
+ 
+@@ -56,28 +79,8 @@ grub_cmd_fwsetup (grub_command_t cmd __attribute__ ((unused)),
+ 
+ static grub_command_t cmd = NULL;
+ 
+-static grub_efi_boolean_t
+-efifwsetup_is_supported (void)
+-{
+-  grub_efi_uint64_t *os_indications_supported = NULL;
+-  grub_size_t oi_size = 0;
+-  grub_efi_guid_t global = GRUB_EFI_GLOBAL_VARIABLE_GUID;
+-
+-  grub_efi_get_variable ("OsIndicationsSupported", &global, &oi_size,
+-			 (void **) &os_indications_supported);
+-
+-  if (!os_indications_supported)
+-    return 0;
+-
+-  if (*os_indications_supported & GRUB_EFI_OS_INDICATIONS_BOOT_TO_FW_UI)
+-    return 1;
+-
+-  return 0;
+-}
+-
+ GRUB_MOD_INIT (efifwsetup)
+ {
+-  if (efifwsetup_is_supported ())
+     cmd = grub_register_command ("fwsetup", grub_cmd_fwsetup, NULL,
+ 				 N_("Reboot into firmware setup menu."));
+ 
diff --git a/SOURCES/0216-arm64-Fix-EFI-loader-kernel-image-allocation.patch b/SOURCES/0216-arm64-Fix-EFI-loader-kernel-image-allocation.patch
new file mode 100644
index 0000000..600b461
--- /dev/null
+++ b/SOURCES/0216-arm64-Fix-EFI-loader-kernel-image-allocation.patch
@@ -0,0 +1,189 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
+Date: Mon, 2 Aug 2021 23:10:01 +1000
+Subject: [PATCH] arm64: Fix EFI loader kernel image allocation
+
+We are currently allocating just enough memory for the file size,
+which means that the kernel BSS is in limbo (and not even zeroed).
+
+We are also not honoring the alignment specified in the image
+PE header.
+
+This makes us use the PE optional header in which the kernel puts the
+actual size it needs, including BSS, and make sure we clear it, and
+honors the specified alignment for the image.
+
+Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
+---
+ grub-core/loader/arm64/linux.c | 100 +++++++++++++++++++++++++++--------------
+ 1 file changed, 66 insertions(+), 34 deletions(-)
+
+diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
+index 47f8cf0d84b..4a252d5e7e9 100644
+--- a/grub-core/loader/arm64/linux.c
++++ b/grub-core/loader/arm64/linux.c
+@@ -41,6 +41,8 @@ GRUB_MOD_LICENSE ("GPLv3+");
+ static grub_dl_t my_mod;
+ static int loaded;
+ 
++static void *kernel_alloc_addr;
++static grub_uint32_t kernel_alloc_pages;
+ static void *kernel_addr;
+ static grub_uint64_t kernel_size;
+ static grub_uint32_t handover_offset;
+@@ -204,9 +206,8 @@ grub_linux_unload (void)
+ 			 GRUB_EFI_BYTES_TO_PAGES (initrd_end - initrd_start));
+   initrd_start = initrd_end = 0;
+   grub_free (linux_args);
+-  if (kernel_addr)
+-    grub_efi_free_pages ((grub_addr_t) kernel_addr,
+-			 GRUB_EFI_BYTES_TO_PAGES (kernel_size));
++  if (kernel_alloc_addr)
++    grub_efi_free_pages ((grub_addr_t) kernel_alloc_addr, kernel_alloc_pages);
+   grub_fdt_unload ();
+   return GRUB_ERR_NONE;
+ }
+@@ -311,14 +312,35 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+   return grub_errno;
+ }
+ 
++static grub_err_t
++parse_pe_header (void *kernel, grub_uint64_t *total_size,
++		 grub_uint32_t *entry_offset,
++		 grub_uint32_t *alignment)
++{
++  struct linux_arch_kernel_header *lh = kernel;
++  struct grub_armxx_linux_pe_header *pe;
++
++  pe = (void *)((unsigned long)kernel + lh->hdr_offset);
++
++  if (pe->opt.magic != GRUB_PE32_PE64_MAGIC)
++    return grub_error(GRUB_ERR_BAD_OS, "Invalid PE optional header magic");
++
++  *total_size   = pe->opt.image_size;
++  *entry_offset = pe->opt.entry_addr;
++  *alignment    = pe->opt.section_alignment;
++
++  return GRUB_ERR_NONE;
++}
++
+ static grub_err_t
+ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 		int argc, char *argv[])
+ {
+   grub_file_t file = 0;
+-  struct linux_arch_kernel_header lh;
+-  struct grub_armxx_linux_pe_header *pe;
+   grub_err_t err;
++  grub_off_t filelen;
++  grub_uint32_t align;
++  void *kernel = NULL;
+   int rc;
+ 
+   grub_dl_ref (my_mod);
+@@ -333,40 +355,24 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   if (!file)
+     goto fail;
+ 
+-  kernel_size = grub_file_size (file);
+-
+-  if (grub_file_read (file, &lh, sizeof (lh)) < (long) sizeof (lh))
+-    return grub_errno;
+-
+-  if (grub_arch_efi_linux_check_image (&lh) != GRUB_ERR_NONE)
+-    goto fail;
+-
+-  grub_loader_unset();
+-
+-  grub_dprintf ("linux", "kernel file size: %lld\n", (long long) kernel_size);
+-  kernel_addr = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (kernel_size));
+-  grub_dprintf ("linux", "kernel numpages: %lld\n",
+-		(long long) GRUB_EFI_BYTES_TO_PAGES (kernel_size));
+-  if (!kernel_addr)
++  filelen = grub_file_size (file);
++  kernel = grub_malloc(filelen);
++  if (!kernel)
+     {
+-      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
++      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel load buffer"));
+       goto fail;
+     }
+ 
+-  grub_file_seek (file, 0);
+-  if (grub_file_read (file, kernel_addr, kernel_size)
+-      < (grub_int64_t) kernel_size)
++  if (grub_file_read (file, kernel, filelen) < (grub_ssize_t)filelen)
+     {
+-      if (!grub_errno)
+-	grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]);
++      grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"),
++		  argv[0]);
+       goto fail;
+     }
+ 
+-  grub_dprintf ("linux", "kernel @ %p\n", kernel_addr);
+-
+   if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
+     {
+-      rc = grub_linuxefi_secure_validate (kernel_addr, kernel_size);
++      rc = grub_linuxefi_secure_validate (kernel, filelen);
+       if (rc <= 0)
+ 	{
+ 	  grub_error (GRUB_ERR_INVALID_COMMAND,
+@@ -375,8 +381,32 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 	}
+     }
+ 
+-  pe = (void *)((unsigned long)kernel_addr + lh.hdr_offset);
+-  handover_offset = pe->opt.entry_addr;
++  if (grub_arch_efi_linux_check_image (kernel) != GRUB_ERR_NONE)
++    goto fail;
++  if (parse_pe_header (kernel, &kernel_size, &handover_offset, &align) != GRUB_ERR_NONE)
++    goto fail;
++  grub_dprintf ("linux", "kernel mem size     : %lld\n", (long long) kernel_size);
++  grub_dprintf ("linux", "kernel entry offset : %d\n", handover_offset);
++  grub_dprintf ("linux", "kernel alignment    : 0x%x\n", align);
++
++  grub_loader_unset();
++
++  kernel_alloc_pages = GRUB_EFI_BYTES_TO_PAGES (kernel_size + align - 1);
++  kernel_alloc_addr = grub_efi_allocate_any_pages (kernel_alloc_pages);
++  grub_dprintf ("linux", "kernel numpages: %d\n", kernel_alloc_pages);
++  if (!kernel_alloc_addr)
++    {
++      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
++      goto fail;
++    }
++  kernel_addr = (void *)ALIGN_UP((grub_uint64_t)kernel_alloc_addr, align);
++
++  grub_dprintf ("linux", "kernel @ %p\n", kernel_addr);
++  grub_memcpy (kernel_addr, kernel, grub_min(filelen, kernel_size));
++  if (kernel_size > filelen)
++    grub_memset ((char *)kernel_addr + filelen, 0, kernel_size - filelen);
++  grub_free(kernel);
++  kernel = NULL;
+ 
+   cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE);
+   linux_args = grub_malloc (cmdline_size);
+@@ -400,6 +430,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+     }
+ 
+ fail:
++  if (kernel)
++    grub_free (kernel);
++
+   if (file)
+     grub_file_close (file);
+ 
+@@ -412,9 +445,8 @@ fail:
+   if (linux_args && !loaded)
+     grub_free (linux_args);
+ 
+-  if (kernel_addr && !loaded)
+-    grub_efi_free_pages ((grub_addr_t) kernel_addr,
+-			 GRUB_EFI_BYTES_TO_PAGES (kernel_size));
++  if (kernel_alloc_addr && !loaded)
++    grub_efi_free_pages ((grub_addr_t) kernel_alloc_addr, kernel_alloc_pages);
+ 
+   return grub_errno;
+ }
diff --git a/SOURCES/0217-normal-main-Discover-the-device-to-read-the-config-f.patch b/SOURCES/0217-normal-main-Discover-the-device-to-read-the-config-f.patch
new file mode 100644
index 0000000..cf12fd6
--- /dev/null
+++ b/SOURCES/0217-normal-main-Discover-the-device-to-read-the-config-f.patch
@@ -0,0 +1,123 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Javier Martinez Canillas <javierm@redhat.com>
+Date: Mon, 30 Aug 2021 12:31:18 +0200
+Subject: [PATCH] normal/main: Discover the device to read the config from as a
+ fallback
+
+The GRUB core.img is generated locally, when this is done the grub2-probe
+tool figures out the device and partition that needs to be read to parse
+the GRUB configuration file.
+
+But in some cases the core.img can't be generated on the host and instead
+has to be done at package build time. For example, if needs to get signed
+with a key that's only available on the package building infrastructure.
+
+If that's the case, the prefix variable won't have a device and partition
+but only a directory path. So there's no way for GRUB to know from which
+device has to read the configuration file.
+
+To allow GRUB to continue working on that scenario, fallback to iterating
+over all the available devices, if reading the config failed when using
+the prefix and fw_path variables.
+
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+---
+ grub-core/normal/main.c | 58 +++++++++++++++++++++++++++++++++++++++++++------
+ 1 file changed, 51 insertions(+), 7 deletions(-)
+
+diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
+index 155bf366da2..f9ccca502ee 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -339,18 +339,13 @@ grub_enter_normal_mode (const char *config)
+ }
+ 
+ static grub_err_t
+-grub_try_normal (const char *variable)
++grub_try_normal_prefix (const char *prefix)
+ {
+     char *config;
+-    const char *prefix;
+     grub_err_t err = GRUB_ERR_FILE_NOT_FOUND;
+     const char *net_search_cfg;
+     int disable_net_search = 0;
+ 
+-    prefix = grub_env_get (variable);
+-    if (!prefix)
+-      return GRUB_ERR_FILE_NOT_FOUND;
+-
+     net_search_cfg = grub_env_get ("feature_net_search_cfg");
+     if (net_search_cfg && net_search_cfg[0] == 'n')
+       disable_net_search = 1;
+@@ -364,7 +359,7 @@ grub_try_normal (const char *variable)
+        config = grub_malloc (config_len);
+ 
+        if (! config)
+-         return GRUB_ERR_FILE_NOT_FOUND;
++         return err;
+ 
+        grub_snprintf (config, config_len, "%s/grub.cfg", prefix);
+        err = grub_net_search_config_file (config);
+@@ -393,6 +388,53 @@ grub_try_normal (const char *variable)
+     return err;
+ }
+ 
++static int
++grub_try_normal_dev (const char *name, void *data)
++{
++  grub_err_t err;
++  const char *prefix = grub_xasprintf ("(%s)%s", name, (char *)data);
++
++  if (!prefix)
++    return 0;
++
++  err = grub_try_normal_prefix (prefix);
++  if (err == GRUB_ERR_NONE)
++    return 1;
++
++  return 0;
++}
++
++static grub_err_t
++grub_try_normal_discover (void)
++{
++  char *prefix = grub_env_get ("prefix");
++  grub_err_t err = GRUB_ERR_FILE_NOT_FOUND;
++
++  if (!prefix)
++    return err;
++
++  if (grub_device_iterate (grub_try_normal_dev, (void *)prefix))
++    return GRUB_ERR_NONE;
++
++  return err;
++}
++
++static grub_err_t
++grub_try_normal (const char *variable)
++{
++  grub_err_t err = GRUB_ERR_FILE_NOT_FOUND;
++  const char *prefix;
++
++  if (!variable)
++    return err;
++
++  prefix = grub_env_get (variable);
++  if (!prefix)
++    return err;
++
++  return grub_try_normal_prefix (prefix);
++}
++
+ /* Enter normal mode from rescue mode.  */
+ static grub_err_t
+ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)),
+@@ -407,6 +449,8 @@ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)),
+       err = grub_try_normal ("fw_path");
+       if (err == GRUB_ERR_FILE_NOT_FOUND)
+         err = grub_try_normal ("prefix");
++      if (err == GRUB_ERR_FILE_NOT_FOUND)
++        err = grub_try_normal_discover ();
+       if (err == GRUB_ERR_FILE_NOT_FOUND)
+         grub_enter_normal_mode (0);
+     }
diff --git a/SOURCES/0218-powerpc-adjust-setting-of-prefix-for-signed-binary-c.patch b/SOURCES/0218-powerpc-adjust-setting-of-prefix-for-signed-binary-c.patch
new file mode 100644
index 0000000..70b021d
--- /dev/null
+++ b/SOURCES/0218-powerpc-adjust-setting-of-prefix-for-signed-binary-c.patch
@@ -0,0 +1,75 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Mon, 19 Jul 2021 14:35:55 +1000
+Subject: [PATCH] powerpc: adjust setting of prefix for signed binary case
+
+On RHEL-signed powerpc grub, we sign a grub with -p /grub2 and expect
+that there's a boot partition.
+
+Unfortunately grub_set_prefix_and_root tries to convert this to
+($fwdevice)/grub2. This ends up being (ieee1275/disk)/grub2 and that
+falls apart pretty quickly - there's no file-system on ieee1275/disk,
+and it makes the search routine try things like
+(ieee1275/disk,msdos2)(ieee1275/disk)/grub2 which also doesn't work.
+
+Detect if we would be about to create (ieee1275/disk)/path and don't:
+preserve a prefix of /path instead and hope the search later finds us.
+
+Related: rhbz#1899864
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/kern/main.c | 38 +++++++++++++++++++++++++++++++++-----
+ 1 file changed, 33 insertions(+), 5 deletions(-)
+
+diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c
+index 0285e95a2bb..e809a5edec1 100644
+--- a/grub-core/kern/main.c
++++ b/grub-core/kern/main.c
+@@ -216,13 +216,41 @@ grub_set_prefix_and_root (void)
+   if (device)
+     {
+       char *prefix_set;
+-    
+-      prefix_set = grub_xasprintf ("(%s)%s", device, path ? : "");
+-      if (prefix_set)
++
++#ifdef __powerpc__
++      /* We have to be careful here on powerpc-ieee1275 + signed grub. We
++	 will have signed something with a prefix that doesn't have a device
++	 because we cannot know in advance what partition we're on.
++
++	 We will have had !device earlier, so we will have set device=fwdevice
++	 However, we want to make sure we do not end up setting prefix to be
++	 ($fwdevice)/path, because we will then end up trying to boot or search
++	 based on a prefix of (ieee1275/disk)/path, which will not work because
++	 it's missing a partition.
++
++	 Also:
++	  - You can end up with a device with an FS directly on it, without
++	    a partition, e.g. ieee1275/cdrom.
++
++	  - powerpc-ieee1275 + grub-install sets e.g. prefix=(,gpt2)/path,
++	    which will have now been extended to device=$fwdisk,partition
++	    and path=/path
++
++	 So we only need to act if device = ieee1275/disk exactly.
++       */
++      if (grub_strncmp (device, "ieee1275/disk", 14) == 0)
++        grub_env_set ("prefix", path);
++      else
++#endif
+ 	{
+-	  grub_env_set ("prefix", prefix_set);
+-	  grub_free (prefix_set);
++	  prefix_set = grub_xasprintf ("(%s)%s", device, path ? : "");
++	  if (prefix_set)
++	  {
++	    grub_env_set ("prefix", prefix_set);
++	    grub_free (prefix_set);
++	  }
+ 	}
++
+       grub_env_set ("root", device);
+     }
+ 
diff --git a/SOURCES/0219-powerpc-fix-prefix-signed-grub-special-case-for-Powe.patch b/SOURCES/0219-powerpc-fix-prefix-signed-grub-special-case-for-Powe.patch
new file mode 100644
index 0000000..1cb21ef
--- /dev/null
+++ b/SOURCES/0219-powerpc-fix-prefix-signed-grub-special-case-for-Powe.patch
@@ -0,0 +1,43 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Mon, 16 Aug 2021 16:01:47 +1000
+Subject: [PATCH] powerpc: fix prefix + signed grub special case for PowerVM
+
+Mea culpa: when testing the PowerPC special case for signed grub, I
+assumed qemu and PowerVM would behave identically. This was wrong, and
+with hindsight a pretty dumb error.
+
+This fixes it. This time, I am actually testing on PowerVM.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/kern/main.c | 15 +++++++++++++--
+ 1 file changed, 13 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c
+index e809a5edec1..2d0d2bbd4cf 100644
+--- a/grub-core/kern/main.c
++++ b/grub-core/kern/main.c
+@@ -236,9 +236,20 @@ grub_set_prefix_and_root (void)
+ 	    which will have now been extended to device=$fwdisk,partition
+ 	    and path=/path
+ 
+-	 So we only need to act if device = ieee1275/disk exactly.
++	  - PowerVM will give us device names like
++	    ieee1275//vdevice/v-scsi@3000006c/disk@8100000000000000
++	    and we don't want to try to encode some sort of truth table about
++	    what sorts of paths represent disks with partition tables and those
++	    without partition tables.
++
++	 So we act unless there is a comma in the device, which would indicate
++	 a partition has already been specified.
++
++	 (If we only have a path, the code in normal to discover config files
++	 will try both without partitions and then with any partitions so we
++	 will cover both CDs and HDs.)
+        */
+-      if (grub_strncmp (device, "ieee1275/disk", 14) == 0)
++      if (grub_strchr (device, ',') == NULL)
+         grub_env_set ("prefix", path);
+       else
+ #endif
diff --git a/SOURCES/0220-Arm-check-for-the-PE-magic-for-the-compiled-arch.patch b/SOURCES/0220-Arm-check-for-the-PE-magic-for-the-compiled-arch.patch
new file mode 100644
index 0000000..8b5170b
--- /dev/null
+++ b/SOURCES/0220-Arm-check-for-the-PE-magic-for-the-compiled-arch.patch
@@ -0,0 +1,63 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 9 Sep 2021 10:59:28 -0400
+Subject: [PATCH] Arm: check for the PE magic for the compiled arch
+
+In "arm64: Fix EFI loader kernel image allocation", Ben fixed the kernel
+alignment to match the alignment given in the PE header.  In doing so, a
+check for valid PE magic was added, which was hard-coded to the value
+seen on Aarch64 (GRUB_PE32_PE64_MAGIC).
+
+Unfortunately, this code is shared between 64-bit and 32-bit, and so
+that value broke 32-bit Arm systems.
+
+This patch adds a constant definition for GRUB_PE32_PEXX_MAGIC, which is
+either GRUB_PE32_PE64_MAGIC or GRUB_PE32_PE32_MAGIC, depending on which
+platform is being built, and uses it in the header magic check.
+
+Resolves: rhbz#2000756
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/loader/arm64/linux.c | 2 +-
+ include/grub/arm/linux.h       | 1 +
+ include/grub/arm64/linux.h     | 1 +
+ 3 files changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
+index 4a252d5e7e9..f18d90bd749 100644
+--- a/grub-core/loader/arm64/linux.c
++++ b/grub-core/loader/arm64/linux.c
+@@ -322,7 +322,7 @@ parse_pe_header (void *kernel, grub_uint64_t *total_size,
+ 
+   pe = (void *)((unsigned long)kernel + lh->hdr_offset);
+ 
+-  if (pe->opt.magic != GRUB_PE32_PE64_MAGIC)
++  if (pe->opt.magic != GRUB_PE32_PEXX_MAGIC)
+     return grub_error(GRUB_ERR_BAD_OS, "Invalid PE optional header magic");
+ 
+   *total_size   = pe->opt.image_size;
+diff --git a/include/grub/arm/linux.h b/include/grub/arm/linux.h
+index b582f67f661..966a5074f53 100644
+--- a/include/grub/arm/linux.h
++++ b/include/grub/arm/linux.h
+@@ -44,6 +44,7 @@ struct grub_arm_linux_pe_header
+ 
+ #if defined(__arm__)
+ # define GRUB_LINUX_ARMXX_MAGIC_SIGNATURE GRUB_LINUX_ARM_MAGIC_SIGNATURE
++# define GRUB_PE32_PEXX_MAGIC GRUB_PE32_PE32_MAGIC
+ # define linux_arch_kernel_header linux_arm_kernel_header
+ # define grub_armxx_linux_pe_header grub_arm_linux_pe_header
+ #endif
+diff --git a/include/grub/arm64/linux.h b/include/grub/arm64/linux.h
+index ea030312df3..422bf2bf24b 100644
+--- a/include/grub/arm64/linux.h
++++ b/include/grub/arm64/linux.h
+@@ -48,6 +48,7 @@ struct grub_arm64_linux_pe_header
+ 
+ #if defined(__aarch64__)
+ # define GRUB_LINUX_ARMXX_MAGIC_SIGNATURE GRUB_LINUX_ARM64_MAGIC_SIGNATURE
++# define GRUB_PE32_PEXX_MAGIC GRUB_PE32_PE64_MAGIC
+ # define linux_arch_kernel_header linux_arm64_kernel_header
+ # define grub_armxx_linux_pe_header grub_arm64_linux_pe_header
+ #endif
diff --git a/SOURCES/0221-fs-xfs-Fix-unreadable-filesystem-with-v4-superblock.patch b/SOURCES/0221-fs-xfs-Fix-unreadable-filesystem-with-v4-superblock.patch
new file mode 100644
index 0000000..893d8b4
--- /dev/null
+++ b/SOURCES/0221-fs-xfs-Fix-unreadable-filesystem-with-v4-superblock.patch
@@ -0,0 +1,118 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Erwan Velu <erwanaliasr1@gmail.com>
+Date: Wed, 25 Aug 2021 15:31:52 +0200
+Subject: [PATCH] fs/xfs: Fix unreadable filesystem with v4 superblock
+
+The commit 8b1e5d193 (fs/xfs: Add bigtime incompat feature support)
+introduced the bigtime support by adding some features in v3 inodes.
+This change extended grub_xfs_inode struct by 76 bytes but also changed
+the computation of XFS_V2_INODE_SIZE and XFS_V3_INODE_SIZE. Prior this
+commit, XFS_V2_INODE_SIZE was 100 bytes. After the commit it's 84 bytes
+XFS_V2_INODE_SIZE becomes 16 bytes too small.
+
+As a result, the data structures aren't properly aligned and the GRUB
+generates "attempt to read or write outside of partition" errors when
+trying to read the XFS filesystem:
+
+                             GNU GRUB  version 2.11
+	....
+	grub> set debug=efi,gpt,xfs
+	grub> insmod part_gpt
+	grub> ls (hd0,gpt1)/
+	partmap/gpt.c:93: Read a valid GPT header
+	partmap/gpt.c:115: GPT entry 0: start=4096, length=1953125
+	fs/xfs.c:931: Reading sb
+	fs/xfs.c:270: Validating superblock
+	fs/xfs.c:295: XFS v4 superblock detected
+	fs/xfs.c:962: Reading root ino 128
+	fs/xfs.c:515: Reading inode (128) - 64, 0
+	fs/xfs.c:515: Reading inode (739521961424144223) - 344365866970255880, 3840
+	error: attempt to read or write outside of partition.
+
+This commit change the XFS_V2_INODE_SIZE computation by subtracting 76
+bytes instead of 92 bytes from the actual size of grub_xfs_inode struct.
+This 76 bytes value comes from added members:
+	20 grub_uint8_t   unused5
+	 1 grub_uint64_t  flags2
+        48 grub_uint8_t   unused6
+
+This patch explicitly splits the v2 and v3 parts of the structure.
+The unused4 is still ending of the v2 structures and the v3 starts
+at unused5. Thanks to this we will avoid future corruptions of v2
+or v3 inodes.
+
+The XFS_V2_INODE_SIZE is returning to its expected size and the
+filesystem is back to a readable state:
+
+                      GNU GRUB  version 2.11
+	....
+	grub> set debug=efi,gpt,xfs
+	grub> insmod part_gpt
+	grub> ls (hd0,gpt1)/
+	partmap/gpt.c:93: Read a valid GPT header
+	partmap/gpt.c:115: GPT entry 0: start=4096, length=1953125
+	fs/xfs.c:931: Reading sb
+	fs/xfs.c:270: Validating superblock
+	fs/xfs.c:295: XFS v4 superblock detected
+	fs/xfs.c:962: Reading root ino 128
+	fs/xfs.c:515: Reading inode (128) - 64, 0
+	fs/xfs.c:515: Reading inode (128) - 64, 0
+	fs/xfs.c:931: Reading sb
+	fs/xfs.c:270: Validating superblock
+	fs/xfs.c:295: XFS v4 superblock detected
+	fs/xfs.c:962: Reading root ino 128
+	fs/xfs.c:515: Reading inode (128) - 64, 0
+	fs/xfs.c:515: Reading inode (128) - 64, 0
+	fs/xfs.c:515: Reading inode (128) - 64, 0
+	fs/xfs.c:515: Reading inode (131) - 64, 768
+	efi/ fs/xfs.c:515: Reading inode (3145856) - 1464904, 0
+	grub2/ fs/xfs.c:515: Reading inode (132) - 64, 1024
+	grub/ fs/xfs.c:515: Reading inode (139) - 64, 2816
+	grub>
+
+Fixes: 8b1e5d193 (fs/xfs: Add bigtime incompat feature support)
+
+Signed-off-by: Erwan Velu <e.velu@criteo.com>
+Tested-by: Carlos Maiolino <cmaiolino@redhat.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit a4b495520e4dc41a896a8b916a64eda9970c50ea)
+---
+ grub-core/fs/xfs.c | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c
+index 0f524c3a8a6..e3816d1ec4a 100644
+--- a/grub-core/fs/xfs.c
++++ b/grub-core/fs/xfs.c
+@@ -192,6 +192,11 @@ struct grub_xfs_time_legacy
+   grub_uint32_t nanosec;
+ } GRUB_PACKED;
+ 
++/*
++ * The struct grub_xfs_inode layout was taken from the
++ * struct xfs_dinode_core which is described here:
++ * https://mirrors.edge.kernel.org/pub/linux/utils/fs/xfs/docs/xfs_filesystem_structure.pdf
++ */
+ struct grub_xfs_inode
+ {
+   grub_uint8_t magic[2];
+@@ -208,14 +213,15 @@ struct grub_xfs_inode
+   grub_uint32_t nextents;
+   grub_uint16_t unused3;
+   grub_uint8_t fork_offset;
+-  grub_uint8_t unused4[37];
++  grub_uint8_t unused4[17]; /* Last member of inode v2. */
++  grub_uint8_t unused5[20]; /* First member of inode v3. */
+   grub_uint64_t flags2;
+-  grub_uint8_t unused5[48];
++  grub_uint8_t unused6[48]; /* Last member of inode v3. */
+ } GRUB_PACKED;
+ 
+ #define XFS_V3_INODE_SIZE	sizeof(struct grub_xfs_inode)
+-/* Size of struct grub_xfs_inode until fork_offset (included). */
+-#define XFS_V2_INODE_SIZE	(XFS_V3_INODE_SIZE - 92)
++/* Size of struct grub_xfs_inode v2, up to unused4 member included. */
++#define XFS_V2_INODE_SIZE	(XFS_V3_INODE_SIZE - 76)
+ 
+ struct grub_xfs_dirblock_tail
+ {
diff --git a/SOURCES/20-grub.install b/SOURCES/20-grub.install
new file mode 100755
index 0000000..2a0ac32
--- /dev/null
+++ b/SOURCES/20-grub.install
@@ -0,0 +1,191 @@
+#!/bin/bash
+
+if ! [[ $KERNEL_INSTALL_MACHINE_ID ]]; then
+    exit 0
+fi
+
+[[ -f /etc/default/grub ]] && . /etc/default/grub
+[[ -f /etc/os-release ]] && . /etc/os-release
+
+COMMAND="$1"
+KERNEL_VERSION="$2"
+BOOT_DIR_ABS="$3"
+KERNEL_IMAGE="$4"
+
+KERNEL_DIR="${KERNEL_IMAGE%/*}"
+
+MACHINE_ID=$KERNEL_INSTALL_MACHINE_ID
+
+# If ${BOOT_DIR_ABS} exists, some other boot loader is active.
+[[ -d "${BOOT_DIR_ABS}" ]] && exit 0
+
+BLS_DIR="/boot/loader/entries"
+
+mkbls() {
+    local kernelver=$1 && shift
+    local datetime=$1 && shift
+    local kernelopts=$1 && shift
+
+    local debugname=""
+    local debugid=""
+    local flavor=""
+
+    if [[ "$kernelver" == *\+* ]] ; then
+        local flavor=-"${kernelver##*+}"
+        if [[ "${flavor}" == "-debug" ]]; then
+            local debugname=" with debugging"
+            local debugid="-debug"
+        fi
+    fi
+
+    cat <<EOF
+title ${NAME} (${kernelver}) ${VERSION}${debugname}
+version ${kernelver}${debugid}
+linux /vmlinuz-${kernelver}
+initrd /initramfs-${kernelver}.img
+options ${kernelopts}
+grub_users \$grub_users
+grub_arg --unrestricted
+grub_class ${ID}
+EOF
+}
+
+[[ "$KERNEL_VERSION" == *\+* ]] && flavor=-"${KERNEL_VERSION##*+}"
+case "$COMMAND" in
+    add)
+        if [[ "${KERNEL_DIR}" != "/boot" ]]; then
+            for i in \
+                "$KERNEL_IMAGE" \
+                    "$KERNEL_DIR"/System.map \
+                    "$KERNEL_DIR"/config \
+                    "$KERNEL_DIR"/zImage.stub \
+                    "$KERNEL_DIR"/dtb
+            do
+                [[ -e "$i" ]] || continue
+                rm -f "/boot/${i##*/}-${KERNEL_VERSION}"
+                cp -aT "$i" "/boot/${i##*/}-${KERNEL_VERSION}"
+                command -v restorecon &>/dev/null && \
+                    restorecon -R "/boot/${i##*/}-${KERNEL_VERSION}"
+            done
+            # hmac is .vmlinuz-<version>.hmac so needs a special treatment
+            i="$KERNEL_DIR/.${KERNEL_IMAGE##*/}.hmac"
+            if [[ -e "$i" ]]; then
+                rm -f "/boot/.${KERNEL_IMAGE##*/}-${KERNEL_VERSION}.hmac"
+                cp -a "$i" "/boot/.${KERNEL_IMAGE##*/}-${KERNEL_VERSION}.hmac"
+                command -v restorecon &>/dev/null && \
+                    restorecon "/boot/.${KERNEL_IMAGE##*/}-${KERNEL_VERSION}.hmac"
+            fi
+            # symvers is symvers-<version>.gz symlink, needs a special treatment
+            i="$KERNEL_DIR/symvers.gz"
+            if [[ -e "$i" ]]; then
+                rm -f "/boot/symvers-${KERNEL_VERSION}.gz"
+                ln -s "$i" "/boot/symvers-${KERNEL_VERSION}.gz"
+                command -v restorecon &>/dev/null && \
+                    restorecon "/boot/symvers-${KERNEL_VERSION}.gz"
+            fi
+        fi
+
+        if [[ "x${GRUB_ENABLE_BLSCFG}" = "xtrue" ]] || [[ ! -f /sbin/new-kernel-pkg ]]; then
+            if [[ -f /etc/kernel/cmdline ]]; then
+                read -r -d '' -a BOOT_OPTIONS < /etc/kernel/cmdline
+            elif [[ -f /usr/lib/kernel/cmdline ]]; then
+                read -r -d '' -a BOOT_OPTIONS < /usr/lib/kernel/cmdline
+            else
+                declare -a BOOT_OPTIONS
+
+                read -r -d '' -a line < /proc/cmdline
+                for i in "${line[@]}"; do
+                    [[ "${i#initrd=*}" != "$i" ]] && continue
+                    [[ "${i#BOOT_IMAGE=*}" != "$i" ]] && continue
+                    BOOT_OPTIONS+=("$i")
+                done
+            fi
+
+            eval "$(grub2-get-kernel-settings)" || true
+            [[ -d "$BLS_DIR" ]] || mkdir -m 0700 -p "$BLS_DIR"
+            BLS_ID="${MACHINE_ID}-${KERNEL_VERSION}"
+            BLS_TARGET="${BLS_DIR}/${BLS_ID}.conf"
+            mkbls "${KERNEL_VERSION}" \
+		  "$(date -u +%Y%m%d%H%M%S -d "$(stat -c '%y' "${KERNEL_DIR}")")" \
+                  "${BOOT_OPTIONS[*]}" >"${BLS_TARGET}"
+            command -v restorecon &>/dev/null && restorecon -R "${BLS_TARGET}"
+
+            LINUX="$(grep '^linux[ \t]' "${BLS_TARGET}" | sed -e 's,^linux[ \t]*,,')"
+            INITRD="$(grep '^initrd[ \t]' "${BLS_TARGET}" | sed -e 's,^initrd[ \t]*,,')"
+            if [[ "$(grub2-probe --device $(grub2-probe --target=device /) --target=fs)" == "btrfs" &&
+		      "${SUSE_BTRFS_SNAPSHOT_BOOTING}" == "true" ]]; then
+                LINUX_RELPATH="$(grub2-mkrelpath -r /boot${LINUX})"
+            else
+                LINUX_RELPATH="$(grub2-mkrelpath /boot${LINUX})"
+            fi
+            BOOTPREFIX="$(dirname ${LINUX_RELPATH})"
+            ROOTPREFIX="$(dirname "/boot${LINUX}")"
+
+            if [[ $LINUX != $LINUX_RELPATH ]]; then
+                sed -i -e "s,^linux.*,linux ${BOOTPREFIX}${LINUX},g" "${BLS_TARGET}"
+                sed -i -e "s,^initrd.*,initrd ${BOOTPREFIX}${INITRD},g" "${BLS_TARGET}"
+            fi
+
+            if [[ "$KERNEL_VERSION" == *\+* ]] && [ "x$GRUB_DEFAULT_TO_DEBUG" != "xtrue" ]; then
+                GRUB_UPDATE_DEFAULT_KERNEL=false
+            fi
+
+            if [ "x$GRUB_UPDATE_DEFAULT_KERNEL" = "xtrue" ]; then
+                NEWDEFAULT="${BLS_ID}"
+            fi
+
+            if [ "x$GRUB_LINUX_MAKE_DEBUG" = "xtrue" ]; then
+                BLS_DEBUG="$(echo ${BLS_TARGET} | sed -e "s/${KERNEL_VERSION}/${KERNEL_VERSION}~debug/")"
+                cp -aT  "${BLS_TARGET}" "${BLS_DEBUG}"
+                TITLE="$(grep '^title[ \t]' "${BLS_DEBUG}" | sed -e 's/^title[ \t]*//')"
+                OPTIONS="$(echo "${BOOT_OPTIONS[*]} ${GRUB_CMDLINE_LINUX_DEBUG}" | sed -e 's/\//\\\//g')"
+                sed -i -e "s/^title.*/title ${TITLE}${GRUB_LINUX_DEBUG_TITLE_POSTFIX}/" "${BLS_DEBUG}"
+                sed -i -e "s/^options.*/options ${OPTIONS}/" "${BLS_DEBUG}"
+                if [ -n "$NEWDEFAULT" -a "x$GRUB_DEFAULT_TO_DEBUG" = "xtrue" ]; then
+                    NEWDEFAULT="${BLS_DEBUG_ID}"
+                fi
+            fi
+            if [ -n "$NEWDEFAULT" ]; then
+                grub2-editenv - set "saved_entry=${NEWDEFAULT}"
+            fi
+
+            # this probably isn't the best place to do this, but it will do for now.
+            if [ -e "${ROOTPREFIX}${INITRD}" -a -e "${ROOTPREFIX}${LINUX}" -a \
+                 "${ROOTPREFIX}${INITRD}" -ot "${ROOTPREFIX}${LINUX}" -a \
+                 -x /usr/lib/kernel/install.d/50-dracut.install ]; then
+                rm -f "${ROOTPREFIX}${INITRD}"
+            fi
+            exit 0
+        fi
+
+        /sbin/new-kernel-pkg --package "kernel${flavor}" --install "$KERNEL_VERSION" || exit $?
+        /sbin/new-kernel-pkg --package "kernel${flavor}" --mkinitrd --dracut --depmod --update "$KERNEL_VERSION" || exit $?
+        /sbin/new-kernel-pkg --package "kernel${flavor}" --rpmposttrans "$KERNEL_VERSION" || exit $?
+        # If grubby is used there's no need to run other installation plugins
+        exit 77
+        ;;
+    remove)
+
+        if [[ "x${GRUB_ENABLE_BLSCFG}" = "xtrue" ]] || [[ ! -f /sbin/new-kernel-pkg ]]; then
+            BLS_TARGET="${BLS_DIR}/${MACHINE_ID}-${KERNEL_VERSION}.conf"
+            BLS_DEBUG="$(echo ${BLS_TARGET} | sed -e "s/${KERNEL_VERSION}/${KERNEL_VERSION}~debug/")"
+            rm -f "${BLS_TARGET}" "${BLS_DEBUG}"
+
+            for i in vmlinuz System.map config zImage.stub dtb; do
+                rm -rf "/boot/${i}-${KERNEL_VERSION}"
+            done
+            # hmac is .vmlinuz-<version>.hmac so needs a special treatment
+            rm -f "/boot/.vmlinuz-${KERNEL_VERSION}.hmac"
+            # symvers is symvers-<version>.gz symlink, needs a special treatment
+            rm -f "/boot/symvers-${KERNEL_VERSION}.gz"
+
+            exit 0
+        fi
+
+        /sbin/new-kernel-pkg --package "kernel${flavor+-$flavor}" --rminitrd --rmmoddep --remove "$KERNEL_VERSION" || exit $?
+        # If grubby is used there's no need to run other installation plugins
+        exit 77
+        ;;
+    *)
+        ;;
+esac
diff --git a/SOURCES/99-grub-mkconfig.install b/SOURCES/99-grub-mkconfig.install
new file mode 100755
index 0000000..d9686b5
--- /dev/null
+++ b/SOURCES/99-grub-mkconfig.install
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+if ! [[ $KERNEL_INSTALL_MACHINE_ID ]]; then
+    exit 0
+fi
+
+# PV and PVH Xen DomU guests boot with pygrub that doesn't have BLS support,
+# also Xen Dom0 use the menuentries from 20_linux_xen and not the ones from
+# 10_linux. So BLS support needs to be disabled for both Xen Dom0 and DomU.
+if [[ -e /sys/hypervisor/type ]] && grep -q "^xen$" /sys/hypervisor/type; then
+    RUN_MKCONFIG=true
+    DISABLE_BLS=true
+fi
+
+ARCH=$(uname -m)
+# Older ppc64le OPAL firmware (petitboot version < 1.8.0) don't have BLS support
+# so grub2-mkconfig has to be run to generate a config with menuentry commands.
+if [[ $ARCH = "ppc64le" ]] && [ -d /sys/firmware/opal ]; then
+
+    petitboot_path="/sys/firmware/devicetree/base/ibm,firmware-versions/petitboot"
+
+    if test -e ${petitboot_path}; then
+        read -r -d '' petitboot_version < ${petitboot_path}
+        petitboot_version="$(echo ${petitboot_version//v})"
+        major_version="$(echo ${petitboot_version} | cut -d . -f1)"
+        minor_version="$(echo ${petitboot_version} | cut -d . -f2)"
+
+        if test -z ${petitboot_version} || test ${major_version} -lt 1 || \
+           test ${major_version} -eq 1 -a ${minor_version} -lt 8; then
+            RUN_MKCONFIG=true
+        fi
+    else
+        RUN_MKCONFIG=true
+    fi
+fi
+
+if [[ $DISABLE_BLS = "true" ]]; then
+    if grep -q '^GRUB_ENABLE_BLSCFG="*true"*\s*$' /etc/default/grub; then
+	sed -i 's/^GRUB_ENABLE_BLSCFG=.*/GRUB_ENABLE_BLSCFG=false/' /etc/default/grub
+    fi
+fi
+
+# A traditional grub configuration file needs to be generated only in the case when
+# the bootloaders are not capable of populating a menu entry from the BLS fragments.
+if [[ $RUN_MKCONFIG != "true" ]]; then
+    exit 0
+fi
+
+[[ -f /etc/default/grub ]] && . /etc/default/grub
+
+COMMAND="$1"
+
+case "$COMMAND" in
+    add|remove)
+        grub2-mkconfig --no-grubenv-update -o /boot/grub2/grub.cfg >& /dev/null
+        ;;
+    *)
+        ;;
+esac
diff --git a/SOURCES/bootstrap b/SOURCES/bootstrap
new file mode 100755
index 0000000..5b08e7e
--- /dev/null
+++ b/SOURCES/bootstrap
@@ -0,0 +1,1073 @@
+#! /bin/sh
+# Print a version string.
+scriptversion=2019-01-04.17; # UTC
+
+# Bootstrap this package from checked-out sources.
+
+# Copyright (C) 2003-2019 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# Originally written by Paul Eggert.  The canonical version of this
+# script is maintained as build-aux/bootstrap in gnulib, however, to
+# be useful to your project, you should place a copy of it under
+# version control in the top-level directory of your project.  The
+# intent is that all customization can be done with a bootstrap.conf
+# file also maintained in your version control; gnulib comes with a
+# template build-aux/bootstrap.conf to get you started.
+
+# Please report bugs or propose patches to bug-gnulib@gnu.org.
+
+nl='
+'
+
+# Ensure file names are sorted consistently across platforms.
+LC_ALL=C
+export LC_ALL
+
+# Ensure that CDPATH is not set.  Otherwise, the output from cd
+# would cause trouble in at least one use below.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+local_gl_dir=gl
+
+# Honor $PERL, but work even if there is none.
+PERL="${PERL-perl}"
+
+me=$0
+
+default_gnulib_url=git://git.sv.gnu.org/gnulib
+
+usage() {
+  cat <<EOF
+Usage: $me [OPTION]...
+Bootstrap this package from the checked-out sources.
+
+Options:
+ --gnulib-srcdir=DIRNAME  specify the local directory where gnulib
+                          sources reside.  Use this if you already
+                          have gnulib sources on your machine, and
+                          do not want to waste your bandwidth downloading
+                          them again.  Defaults to \$GNULIB_SRCDIR
+ --bootstrap-sync         if this bootstrap script is not identical to
+                          the version in the local gnulib sources,
+                          update this script, and then restart it with
+                          /bin/sh or the shell \$CONFIG_SHELL
+ --no-bootstrap-sync      do not check whether bootstrap is out of sync
+ --copy                   copy files instead of creating symbolic links
+ --force                  attempt to bootstrap even if the sources seem
+                          not to have been checked out
+ --no-git                 do not use git to update gnulib.  Requires that
+                          --gnulib-srcdir point to a correct gnulib snapshot
+ --skip-po                do not download po files
+
+If the file $me.conf exists in the same directory as this script, its
+contents are read as shell variables to configure the bootstrap.
+
+For build prerequisites, environment variables like \$AUTOCONF and \$AMTAR
+are honored.
+
+Gnulib sources can be fetched in various ways:
+
+ * If this package is in a git repository with a 'gnulib' submodule
+   configured, then that submodule is initialized and updated and sources
+   are fetched from there.  If \$GNULIB_SRCDIR is set (directly or via
+   --gnulib-srcdir) and is a git repository, then it is used as a reference.
+
+ * Otherwise, if \$GNULIB_SRCDIR is set (directly or via --gnulib-srcdir),
+   then sources are fetched from that local directory.  If it is a git
+   repository and \$GNULIB_REVISION is set, then that revision is checked
+   out.
+
+ * Otherwise, if this package is in a git repository with a 'gnulib'
+   submodule configured, then that submodule is initialized and updated and
+   sources are fetched from there.
+
+ * Otherwise, if the 'gnulib' directory does not exist, Gnulib sources are
+   cloned into that directory using git from \$GNULIB_URL, defaulting to
+   $default_gnulib_url.
+   If \$GNULIB_REVISION is set, then that revision is checked out.
+
+ * Otherwise, the existing Gnulib sources in the 'gnulib' directory are
+   used.  If it is a git repository and \$GNULIB_REVISION is set, then that
+   revision is checked out.
+
+If you maintain a package and want to pin a particular revision of the
+Gnulib sources that has been tested with your package, then there are two
+possible approaches: either configure a 'gnulib' submodule with the
+appropriate revision, or set \$GNULIB_REVISION (and if necessary
+\$GNULIB_URL) in $me.conf.
+
+Running without arguments will suffice in most cases.
+EOF
+}
+
+# warnf_ FORMAT-STRING ARG1...
+warnf_ ()
+{
+  warnf_format_=$1
+  shift
+  nl='
+'
+  case $* in
+    *$nl*) me_=$(printf "$me"|tr "$nl|" '??')
+       printf "$warnf_format_" "$@" | sed "s|^|$me_: |" ;;
+    *) printf "$me: $warnf_format_" "$@" ;;
+  esac >&2
+}
+
+# warn_ WORD1...
+warn_ ()
+{
+  # If IFS does not start with ' ', set it and emit the warning in a subshell.
+  case $IFS in
+    ' '*) warnf_ '%s\n' "$*";;
+    *)    (IFS=' '; warn_ "$@");;
+  esac
+}
+
+# die WORD1...
+die() { warn_ "$@"; exit 1; }
+
+# Configuration.
+
+# Name of the Makefile.am
+gnulib_mk=gnulib.mk
+
+# List of gnulib modules needed.
+gnulib_modules=
+
+# Any gnulib files needed that are not in modules.
+gnulib_files=
+
+: ${AUTOPOINT=autopoint}
+: ${AUTORECONF=autoreconf}
+
+# A function to be called right after gnulib-tool is run.
+# Override it via your own definition in bootstrap.conf.
+bootstrap_post_import_hook() { :; }
+
+# A function to be called after everything else in this script.
+# Override it via your own definition in bootstrap.conf.
+bootstrap_epilogue() { :; }
+
+# The command to download all .po files for a specified domain into a
+# specified directory.  Fill in the first %s with the destination
+# directory and the second with the domain name.
+po_download_command_format=\
+"wget --mirror --level=1 -nd -q -A.po -P '%s' \
+ https://translationproject.org/latest/%s/"
+
+# Prefer a non-empty tarname (4th argument of AC_INIT if given), else
+# fall back to the package name (1st argument with munging)
+extract_package_name='
+  /^AC_INIT(\[*/{
+     s///
+     /^[^,]*,[^,]*,[^,]*,[ []*\([^][ ,)]\)/{
+       s//\1/
+       s/[],)].*//
+       p
+       q
+     }
+     s/[],)].*//
+     s/^GNU //
+     y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
+     s/[^abcdefghijklmnopqrstuvwxyz0123456789_]/-/g
+     p
+  }
+'
+package=$(sed -n "$extract_package_name" configure.ac) \
+  || die 'cannot find package name in configure.ac'
+gnulib_name=lib$package
+
+build_aux=build-aux
+source_base=lib
+m4_base=m4
+doc_base=doc
+tests_base=tests
+gnulib_extra_files="
+        build-aux/install-sh
+        build-aux/mdate-sh
+        build-aux/texinfo.tex
+        build-aux/depcomp
+        build-aux/config.guess
+        build-aux/config.sub
+        doc/INSTALL
+"
+
+# Additional gnulib-tool options to use.  Use "\newline" to break lines.
+gnulib_tool_option_extras=
+
+# Other locale categories that need message catalogs.
+EXTRA_LOCALE_CATEGORIES=
+
+# Additional xgettext options to use.  Use "\\\newline" to break lines.
+XGETTEXT_OPTIONS='\\\
+ --flag=_:1:pass-c-format\\\
+ --flag=N_:1:pass-c-format\\\
+ --flag=error:3:c-format --flag=error_at_line:5:c-format\\\
+'
+
+# Package bug report address and copyright holder for gettext files
+COPYRIGHT_HOLDER='Free Software Foundation, Inc.'
+MSGID_BUGS_ADDRESS=bug-$package@gnu.org
+
+# Files we don't want to import.
+excluded_files=
+
+# File that should exist in the top directory of a checked out hierarchy,
+# but not in a distribution tarball.
+checkout_only_file=README-hacking
+
+# Whether to use copies instead of symlinks.
+copy=false
+
+# Set this to '.cvsignore .gitignore' in bootstrap.conf if you want
+# those files to be generated in directories like lib/, m4/, and po/.
+# Or set it to 'auto' to make this script select which to use based
+# on which version control system (if any) is used in the source directory.
+vc_ignore=auto
+
+# Set this to true in bootstrap.conf to enable --bootstrap-sync by
+# default.
+bootstrap_sync=false
+
+# Use git to update gnulib sources
+use_git=true
+
+check_exists() {
+  if test "$1" = "--verbose"; then
+    ($2 --version </dev/null) >/dev/null 2>&1
+    if test $? -ge 126; then
+      # If not found, run with diagnostics as one may be
+      # presented with env variables to set to find the right version
+      ($2 --version </dev/null)
+    fi
+  else
+    ($1 --version </dev/null) >/dev/null 2>&1
+  fi
+
+  test $? -lt 126
+}
+
+# find_tool ENVVAR NAMES...
+# -------------------------
+# Search for a required program.  Use the value of ENVVAR, if set,
+# otherwise find the first of the NAMES that can be run.
+# If found, set ENVVAR to the program name, die otherwise.
+#
+# FIXME: code duplication, see also gnu-web-doc-update.
+find_tool ()
+{
+  find_tool_envvar=$1
+  shift
+  find_tool_names=$@
+  eval "find_tool_res=\$$find_tool_envvar"
+  if test x"$find_tool_res" = x; then
+    for i; do
+      if check_exists $i; then
+        find_tool_res=$i
+        break
+      fi
+    done
+  fi
+  if test x"$find_tool_res" = x; then
+    warn_ "one of these is required: $find_tool_names;"
+    die   "alternatively set $find_tool_envvar to a compatible tool"
+  fi
+  eval "$find_tool_envvar=\$find_tool_res"
+  eval "export $find_tool_envvar"
+}
+
+# Override the default configuration, if necessary.
+# Make sure that bootstrap.conf is sourced from the current directory
+# if we were invoked as "sh bootstrap".
+case "$0" in
+  */*) test -r "$0.conf" && . "$0.conf" ;;
+  *) test -r "$0.conf" && . ./"$0.conf" ;;
+esac
+
+if test "$vc_ignore" = auto; then
+  vc_ignore=
+  test -d .git && vc_ignore=.gitignore
+  test -d CVS && vc_ignore="$vc_ignore .cvsignore"
+fi
+
+if test x"$gnulib_modules$gnulib_files$gnulib_extra_files" = x; then
+  use_gnulib=false
+else
+  use_gnulib=true
+fi
+
+# Translate configuration into internal form.
+
+# Parse options.
+
+for option
+do
+  case $option in
+  --help)
+    usage
+    exit;;
+  --gnulib-srcdir=*)
+    GNULIB_SRCDIR=${option#--gnulib-srcdir=};;
+  --skip-po)
+    SKIP_PO=t;;
+  --force)
+    checkout_only_file=;;
+  --copy)
+    copy=true;;
+  --bootstrap-sync)
+    bootstrap_sync=true;;
+  --no-bootstrap-sync)
+    bootstrap_sync=false;;
+  --no-git)
+    use_git=false;;
+  *)
+    die "$option: unknown option";;
+  esac
+done
+
+$use_git || test -d "$GNULIB_SRCDIR" \
+  || die "Error: --no-git requires --gnulib-srcdir"
+
+if test -n "$checkout_only_file" && test ! -r "$checkout_only_file"; then
+  die "Bootstrapping from a non-checked-out distribution is risky."
+fi
+
+# Strip blank and comment lines to leave significant entries.
+gitignore_entries() {
+  sed '/^#/d; /^$/d' "$@"
+}
+
+# If $STR is not already on a line by itself in $FILE, insert it at the start.
+# Entries are inserted at the start of the ignore list to ensure existing
+# entries starting with ! are not overridden.  Such entries support
+# whitelisting exceptions after a more generic blacklist pattern.
+insert_if_absent() {
+  file=$1
+  str=$2
+  test -f $file || touch $file
+  test -r $file || die "Error: failed to read ignore file: $file"
+  duplicate_entries=$(gitignore_entries $file | sort | uniq -d)
+  if [ "$duplicate_entries" ] ; then
+    die "Error: Duplicate entries in $file: " $duplicate_entries
+  fi
+  linesold=$(gitignore_entries $file | wc -l)
+  linesnew=$( { echo "$str"; cat $file; } | gitignore_entries | sort -u | wc -l)
+  if [ $linesold != $linesnew ] ; then
+    { echo "$str" | cat - $file > $file.bak && mv $file.bak $file; } \
+      || die "insert_if_absent $file $str: failed"
+  fi
+}
+
+# Adjust $PATTERN for $VC_IGNORE_FILE and insert it with
+# insert_if_absent.
+insert_vc_ignore() {
+  vc_ignore_file="$1"
+  pattern="$2"
+  case $vc_ignore_file in
+  *.gitignore)
+    # A .gitignore entry that does not start with '/' applies
+    # recursively to subdirectories, so prepend '/' to every
+    # .gitignore entry.
+    pattern=$(echo "$pattern" | sed s,^,/,);;
+  esac
+  insert_if_absent "$vc_ignore_file" "$pattern"
+}
+
+# Die if there is no AC_CONFIG_AUX_DIR($build_aux) line in configure.ac.
+found_aux_dir=no
+grep '^[	 ]*AC_CONFIG_AUX_DIR(\['"$build_aux"'\])' configure.ac \
+    >/dev/null && found_aux_dir=yes
+grep '^[	 ]*AC_CONFIG_AUX_DIR('"$build_aux"')' configure.ac \
+    >/dev/null && found_aux_dir=yes
+test $found_aux_dir = yes \
+  || die "configure.ac lacks 'AC_CONFIG_AUX_DIR([$build_aux])'; add it"
+
+# If $build_aux doesn't exist, create it now, otherwise some bits
+# below will malfunction.  If creating it, also mark it as ignored.
+if test ! -d $build_aux; then
+  mkdir $build_aux
+  for dot_ig in x $vc_ignore; do
+    test $dot_ig = x && continue
+    insert_vc_ignore $dot_ig $build_aux
+  done
+fi
+
+# Note this deviates from the version comparison in automake
+# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a
+# but this should suffice as we won't be specifying old
+# version formats or redundant trailing .0 in bootstrap.conf.
+# If we did want full compatibility then we should probably
+# use m4_version_compare from autoconf.
+sort_ver() { # sort -V is not generally available
+  ver1="$1"
+  ver2="$2"
+
+  # split on '.' and compare each component
+  i=1
+  while : ; do
+    p1=$(echo "$ver1" | cut -d. -f$i)
+    p2=$(echo "$ver2" | cut -d. -f$i)
+    if [ ! "$p1" ]; then
+      echo "$1 $2"
+      break
+    elif [ ! "$p2" ]; then
+      echo "$2 $1"
+      break
+    elif [ ! "$p1" = "$p2" ]; then
+      if [ "$p1" -gt "$p2" ] 2>/dev/null; then # numeric comparison
+        echo "$2 $1"
+      elif [ "$p2" -gt "$p1" ] 2>/dev/null; then # numeric comparison
+        echo "$1 $2"
+      else # numeric, then lexicographic comparison
+        lp=$(printf "$p1\n$p2\n" | LANG=C sort -n | tail -n1)
+        if [ "$lp" = "$p2" ]; then
+          echo "$1 $2"
+        else
+          echo "$2 $1"
+        fi
+      fi
+      break
+    fi
+    i=$(($i+1))
+  done
+}
+
+get_version_sed='
+# Move version to start of line.
+s/.*[v ]\([0-9]\)/\1/
+
+# Skip lines that do not start with version.
+/^[0-9]/!d
+
+# Remove characters after the version.
+s/[^.a-z0-9-].*//
+
+# The first component must be digits only.
+s/^\([0-9]*\)[a-z-].*/\1/
+
+#the following essentially does s/5.005/5.5/
+s/\.0*\([1-9]\)/.\1/g
+p
+q'
+
+get_version() {
+  app=$1
+
+  $app --version >/dev/null 2>&1 || { $app --version; return 1; }
+
+  $app --version 2>&1 | sed -n "$get_version_sed"
+}
+
+check_versions() {
+  ret=0
+
+  while read app req_ver; do
+    # We only need libtoolize from the libtool package.
+    if test "$app" = libtool; then
+      app=libtoolize
+    fi
+    # Exempt git if --no-git is in effect.
+    if test "$app" = git; then
+      $use_git || continue
+    fi
+    # Honor $APP variables ($TAR, $AUTOCONF, etc.)
+    appvar=$(echo $app | LC_ALL=C tr '[a-z]-' '[A-Z]_')
+    test "$appvar" = TAR && appvar=AMTAR
+    case $appvar in
+        GZIP) ;; # Do not use $GZIP:  it contains gzip options.
+        PERL::*) ;; # Keep perl modules as-is
+        *) eval "app=\${$appvar-$app}" ;;
+    esac
+
+    # Handle the still-experimental Automake-NG programs specially.
+    # They remain named as the mainstream Automake programs ("automake",
+    # and "aclocal") to avoid gratuitous incompatibilities with
+    # pre-existing usages (by, say, autoreconf, or custom autogen.sh
+    # scripts), but correctly identify themselves (as being part of
+    # "GNU automake-ng") when asked their version.
+    case $app in
+      automake-ng|aclocal-ng)
+        app=${app%-ng}
+        ($app --version | grep '(GNU automake-ng)') >/dev/null 2>&1 || {
+          warn_ "Error: '$app' not found or not from Automake-NG"
+          ret=1
+          continue
+        } ;;
+      # Another check is for perl modules.  These can be written as
+      # e.g. perl::XML::XPath in case of XML::XPath module, etc.
+      perl::*)
+        # Extract module name
+        app="${app#perl::}"
+        if ! $PERL -m"$app" -e 'exit 0' >/dev/null 2>&1; then
+          warn_ "Error: perl module '$app' not found"
+          ret=1
+        fi
+        continue
+        ;;
+    esac
+    if [ "$req_ver" = "-" ]; then
+      # Merely require app to exist; not all prereq apps are well-behaved
+      # so we have to rely on $? rather than get_version.
+      if ! check_exists --verbose $app; then
+        warn_ "Error: '$app' not found"
+        ret=1
+      fi
+    else
+      # Require app to produce a new enough version string.
+      inst_ver=$(get_version $app)
+      if [ ! "$inst_ver" ]; then
+        warn_ "Error: '$app' not found"
+        ret=1
+      else
+        latest_ver=$(sort_ver $req_ver $inst_ver | cut -d' ' -f2)
+        if [ ! "$latest_ver" = "$inst_ver" ]; then
+          warnf_ '%s\n'                                        \
+              "Error: '$app' version == $inst_ver is too old"  \
+              "       '$app' version >= $req_ver is required"
+          ret=1
+        fi
+      fi
+    fi
+  done
+
+  return $ret
+}
+
+print_versions() {
+  echo "Program    Min_version"
+  echo "----------------------"
+  printf %s "$buildreq"
+  echo "----------------------"
+  # can't depend on column -t
+}
+
+# Find sha1sum, named gsha1sum on MacPorts, shasum on Mac OS X 10.6.
+# Also find the compatible sha1 utility on the BSDs
+if test x"$SKIP_PO" = x; then
+  find_tool SHA1SUM sha1sum gsha1sum shasum sha1
+fi
+
+use_libtool=0
+# We'd like to use grep -E, to see if any of LT_INIT,
+# AC_PROG_LIBTOOL, AM_PROG_LIBTOOL is used in configure.ac,
+# but that's not portable enough (e.g., for Solaris).
+grep '^[	 ]*A[CM]_PROG_LIBTOOL' configure.ac >/dev/null \
+  && use_libtool=1
+grep '^[	 ]*LT_INIT' configure.ac >/dev/null \
+  && use_libtool=1
+if test $use_libtool = 1; then
+  find_tool LIBTOOLIZE glibtoolize libtoolize
+fi
+
+# gnulib-tool requires at least automake and autoconf.
+# If either is not listed, add it (with minimum version) as a prerequisite.
+case $buildreq in
+  *automake*) ;;
+  *) buildreq="automake 1.9
+$buildreq" ;;
+esac
+case $buildreq in
+  *autoconf*) ;;
+  *) buildreq="autoconf 2.59
+$buildreq" ;;
+esac
+
+# When we can deduce that gnulib-tool will require patch,
+# and when patch is not already listed as a prerequisite, add it, too.
+if test -d "$local_gl_dir" \
+    && ! find "$local_gl_dir" -name '*.diff' -exec false {} +; then
+  case $buildreq in
+    *patch*) ;;
+    *) buildreq="patch -
+$buildreq" ;;
+  esac
+fi
+
+if ! printf "$buildreq" | check_versions; then
+  echo >&2
+  if test -f README-prereq; then
+    die "See README-prereq for how to get the prerequisite programs"
+  else
+    die "Please install the prerequisite programs"
+  fi
+fi
+
+# Warn the user if autom4te appears to be broken; this causes known
+# issues with at least gettext 0.18.3.
+probe=$(echo 'm4_quote([hi])' | autom4te -l M4sugar -t 'm4_quote:$%' -)
+if test "x$probe" != xhi; then
+  warn_ "WARNING: your autom4te wrapper eats stdin;"
+  warn_ "if bootstrap fails, consider upgrading your autotools"
+fi
+
+echo "$0: Bootstrapping from checked-out $package sources..."
+
+# See if we can use gnulib's git-merge-changelog merge driver.
+if $use_git && test -d .git && check_exists git; then
+  if git config merge.merge-changelog.driver >/dev/null ; then
+    :
+  elif check_exists git-merge-changelog; then
+    echo "$0: initializing git-merge-changelog driver"
+    git config merge.merge-changelog.name 'GNU-style ChangeLog merge driver'
+    git config merge.merge-changelog.driver 'git-merge-changelog %O %A %B'
+  else
+    echo "$0: consider installing git-merge-changelog from gnulib"
+  fi
+fi
+
+
+cleanup_gnulib() {
+  status=$?
+  rm -fr "$gnulib_path"
+  exit $status
+}
+
+git_modules_config () {
+  test -f .gitmodules && git config --file .gitmodules "$@"
+}
+
+if $use_gnulib; then
+  if $use_git; then
+    gnulib_path=$(git_modules_config submodule.gnulib.path)
+    test -z "$gnulib_path" && gnulib_path=gnulib
+  fi
+
+  # Get gnulib files.  Populate $GNULIB_SRCDIR, possibly updating a
+  # submodule, for use in the rest of the script.
+
+  case ${GNULIB_SRCDIR--} in
+  -)
+    # Note that $use_git is necessarily true in this case.
+    if git_modules_config submodule.gnulib.url >/dev/null; then
+      echo "$0: getting gnulib files..."
+      git submodule init -- "$gnulib_path" || exit $?
+      git submodule update -- "$gnulib_path" || exit $?
+
+    elif [ ! -d "$gnulib_path" ]; then
+      echo "$0: getting gnulib files..."
+
+      trap cleanup_gnulib 1 2 13 15
+
+      shallow=
+      if test -z "$GNULIB_REVISION"; then
+        git clone -h 2>&1 | grep -- --depth > /dev/null && shallow='--depth 2'
+      fi
+      git clone $shallow ${GNULIB_URL:-$default_gnulib_url} "$gnulib_path" \
+        || cleanup_gnulib
+
+      trap - 1 2 13 15
+    fi
+    GNULIB_SRCDIR=$gnulib_path
+    ;;
+  *)
+    # Use GNULIB_SRCDIR directly or as a reference.
+    if $use_git && test -d "$GNULIB_SRCDIR"/.git && \
+          git_modules_config submodule.gnulib.url >/dev/null; then
+      echo "$0: getting gnulib files..."
+      if git submodule -h|grep -- --reference > /dev/null; then
+        # Prefer the one-liner available in git 1.6.4 or newer.
+        git submodule update --init --reference "$GNULIB_SRCDIR" \
+          "$gnulib_path" || exit $?
+      else
+        # This fallback allows at least git 1.5.5.
+        if test -f "$gnulib_path"/gnulib-tool; then
+          # Since file already exists, assume submodule init already complete.
+          git submodule update -- "$gnulib_path" || exit $?
+        else
+          # Older git can't clone into an empty directory.
+          rmdir "$gnulib_path" 2>/dev/null
+          git clone --reference "$GNULIB_SRCDIR" \
+            "$(git_modules_config submodule.gnulib.url)" "$gnulib_path" \
+            && git submodule init -- "$gnulib_path" \
+            && git submodule update -- "$gnulib_path" \
+            || exit $?
+        fi
+      fi
+      GNULIB_SRCDIR=$gnulib_path
+    fi
+    ;;
+  esac
+
+  if test -d "$GNULIB_SRCDIR"/.git && test -n "$GNULIB_REVISION" \
+     && ! git_modules_config submodule.gnulib.url >/dev/null; then
+    (cd "$GNULIB_SRCDIR" && git checkout "$GNULIB_REVISION") || cleanup_gnulib
+  fi
+
+  # $GNULIB_SRCDIR now points to the version of gnulib to use, and
+  # we no longer need to use git or $gnulib_path below here.
+
+  if $bootstrap_sync; then
+    cmp -s "$0" "$GNULIB_SRCDIR/build-aux/bootstrap" || {
+      echo "$0: updating bootstrap and restarting..."
+      case $(sh -c 'echo "$1"' -- a) in
+        a) ignored=--;;
+        *) ignored=ignored;;
+      esac
+      exec sh -c \
+        'cp "$1" "$2" && shift && exec "${CONFIG_SHELL-/bin/sh}" "$@"' \
+        $ignored "$GNULIB_SRCDIR/build-aux/bootstrap" \
+        "$0" "$@" --no-bootstrap-sync
+    }
+  fi
+
+  gnulib_tool=$GNULIB_SRCDIR/gnulib-tool
+  <$gnulib_tool || exit $?
+fi
+
+# Get translations.
+
+download_po_files() {
+  subdir=$1
+  domain=$2
+  echo "$me: getting translations into $subdir for $domain..."
+  cmd=$(printf "$po_download_command_format" "$subdir" "$domain")
+  eval "$cmd"
+}
+
+# Mirror .po files to $po_dir/.reference and copy only the new
+# or modified ones into $po_dir.  Also update $po_dir/LINGUAS.
+# Note po files that exist locally only are left in $po_dir but will
+# not be included in LINGUAS and hence will not be distributed.
+update_po_files() {
+  # Directory containing primary .po files.
+  # Overwrite them only when we're sure a .po file is new.
+  po_dir=$1
+  domain=$2
+
+  # Mirror *.po files into this dir.
+  # Usually contains *.s1 checksum files.
+  ref_po_dir="$po_dir/.reference"
+
+  test -d $ref_po_dir || mkdir $ref_po_dir || return
+  download_po_files $ref_po_dir $domain \
+    && ls "$ref_po_dir"/*.po 2>/dev/null |
+      sed 's|.*/||; s|\.po$||' > "$po_dir/LINGUAS" || return
+
+  langs=$(cd $ref_po_dir && echo *.po | sed 's/\.po//g')
+  test "$langs" = '*' && langs=x
+  for po in $langs; do
+    case $po in x) continue;; esac
+    new_po="$ref_po_dir/$po.po"
+    cksum_file="$ref_po_dir/$po.s1"
+    if ! test -f "$cksum_file" ||
+        ! test -f "$po_dir/$po.po" ||
+        ! $SHA1SUM -c "$cksum_file" < "$new_po" > /dev/null 2>&1; then
+      echo "$me: updated $po_dir/$po.po..."
+      cp "$new_po" "$po_dir/$po.po" \
+          && $SHA1SUM < "$new_po" > "$cksum_file" || return
+    fi
+  done
+}
+
+case $SKIP_PO in
+'')
+  if test -d po; then
+    update_po_files po $package || exit
+  fi
+
+  if test -d runtime-po; then
+    update_po_files runtime-po $package-runtime || exit
+  fi;;
+esac
+
+symlink_to_dir()
+{
+  src=$1/$2
+  dst=${3-$2}
+
+  test -f "$src" && {
+
+    # If the destination directory doesn't exist, create it.
+    # This is required at least for "lib/uniwidth/cjk.h".
+    dst_dir=$(dirname "$dst")
+    if ! test -d "$dst_dir"; then
+      mkdir -p "$dst_dir"
+
+      # If we've just created a directory like lib/uniwidth,
+      # tell version control system(s) it's ignorable.
+      # FIXME: for now, this does only one level
+      parent=$(dirname "$dst_dir")
+      for dot_ig in x $vc_ignore; do
+        test $dot_ig = x && continue
+        ig=$parent/$dot_ig
+        insert_vc_ignore $ig "${dst_dir##*/}"
+      done
+    fi
+
+    if $copy; then
+      {
+        test ! -h "$dst" || {
+          echo "$me: rm -f $dst" &&
+          rm -f "$dst"
+        }
+      } &&
+      test -f "$dst" &&
+      cmp -s "$src" "$dst" || {
+        echo "$me: cp -fp $src $dst" &&
+        cp -fp "$src" "$dst"
+      }
+    else
+      # Leave any existing symlink alone, if it already points to the source,
+      # so that broken build tools that care about symlink times
+      # aren't confused into doing unnecessary builds.  Conversely, if the
+      # existing symlink's timestamp is older than the source, make it afresh,
+      # so that broken tools aren't confused into skipping needed builds.  See
+      # <https://lists.gnu.org/r/bug-gnulib/2011-05/msg00326.html>.
+      test -h "$dst" &&
+      src_ls=$(ls -diL "$src" 2>/dev/null) && set $src_ls && src_i=$1 &&
+      dst_ls=$(ls -diL "$dst" 2>/dev/null) && set $dst_ls && dst_i=$1 &&
+      test "$src_i" = "$dst_i" &&
+      both_ls=$(ls -dt "$src" "$dst") &&
+      test "X$both_ls" = "X$dst$nl$src" || {
+        dot_dots=
+        case $src in
+        /*) ;;
+        *)
+          case /$dst/ in
+          *//* | */../* | */./* | /*/*/*/*/*/)
+             die "invalid symlink calculation: $src -> $dst";;
+          /*/*/*/*/)    dot_dots=../../../;;
+          /*/*/*/)      dot_dots=../../;;
+          /*/*/)        dot_dots=../;;
+          esac;;
+        esac
+
+        echo "$me: ln -fs $dot_dots$src $dst" &&
+        ln -fs "$dot_dots$src" "$dst"
+      }
+    fi
+  }
+}
+
+version_controlled_file() {
+  parent=$1
+  file=$2
+  if test -d .git; then
+    git rm -n "$file" > /dev/null 2>&1
+  elif test -d .svn; then
+    svn log -r HEAD "$file" > /dev/null 2>&1
+  elif test -d CVS; then
+    grep -F "/${file##*/}/" "$parent/CVS/Entries" 2>/dev/null |
+             grep '^/[^/]*/[0-9]' > /dev/null
+  else
+    warn_ "no version control for $file?"
+    false
+  fi
+}
+
+# NOTE: we have to be careful to run both autopoint and libtoolize
+# before gnulib-tool, since gnulib-tool is likely to provide newer
+# versions of files "installed" by these two programs.
+# Then, *after* gnulib-tool (see below), we have to be careful to
+# run autoreconf in such a way that it does not run either of these
+# two just-pre-run programs.
+
+# Import from gettext.
+with_gettext=yes
+grep '^[	 ]*AM_GNU_GETTEXT_VERSION(' configure.ac >/dev/null || \
+    with_gettext=no
+
+if test $with_gettext = yes || test $use_libtool = 1; then
+
+  tempbase=.bootstrap$$
+  trap "rm -f $tempbase.0 $tempbase.1" 1 2 13 15
+
+  > $tempbase.0 > $tempbase.1 &&
+  find . ! -type d -print | sort > $tempbase.0 || exit
+
+  if test $with_gettext = yes; then
+    # Released autopoint has the tendency to install macros that have been
+    # obsoleted in current gnulib, so run this before gnulib-tool.
+    echo "$0: $AUTOPOINT --force"
+    $AUTOPOINT --force || exit
+  fi
+
+  # Autoreconf runs aclocal before libtoolize, which causes spurious
+  # warnings if the initial aclocal is confused by the libtoolized
+  # (or worse out-of-date) macro directory.
+  # libtoolize 1.9b added the --install option; but we support back
+  # to libtoolize 1.5.22, where the install action was default.
+  if test $use_libtool = 1; then
+    install=
+    case $($LIBTOOLIZE --help) in
+      *--install*) install=--install ;;
+    esac
+    echo "running: $LIBTOOLIZE $install --copy"
+    $LIBTOOLIZE $install --copy
+  fi
+
+  find . ! -type d -print | sort >$tempbase.1
+  old_IFS=$IFS
+  IFS=$nl
+  for file in $(comm -13 $tempbase.0 $tempbase.1); do
+    IFS=$old_IFS
+    parent=${file%/*}
+    version_controlled_file "$parent" "$file" || {
+      for dot_ig in x $vc_ignore; do
+        test $dot_ig = x && continue
+        ig=$parent/$dot_ig
+        insert_vc_ignore "$ig" "${file##*/}"
+      done
+    }
+  done
+  IFS=$old_IFS
+
+  rm -f $tempbase.0 $tempbase.1
+  trap - 1 2 13 15
+fi
+
+# Import from gnulib.
+
+if $use_gnulib; then
+  gnulib_tool_options="\
+   --no-changelog\
+   --aux-dir=$build_aux\
+   --doc-base=$doc_base\
+   --lib=$gnulib_name\
+   --m4-base=$m4_base/\
+   --source-base=$source_base/\
+   --tests-base=$tests_base\
+   --local-dir=$local_gl_dir\
+   $gnulib_tool_option_extras\
+  "
+  if test $use_libtool = 1; then
+    case "$gnulib_tool_options " in
+      *' --libtool '*) ;;
+      *) gnulib_tool_options="$gnulib_tool_options --libtool" ;;
+    esac
+  fi
+  echo "$0: $gnulib_tool $gnulib_tool_options --import ..."
+  $gnulib_tool $gnulib_tool_options --import $gnulib_modules \
+    || die "gnulib-tool failed"
+
+  for file in $gnulib_files; do
+    symlink_to_dir "$GNULIB_SRCDIR" $file \
+      || die "failed to symlink $file"
+  done
+fi
+
+bootstrap_post_import_hook \
+  || die "bootstrap_post_import_hook failed"
+
+# Don't proceed if there are uninitialized submodules.  In particular,
+# the next step will remove dangling links, which might be links into
+# uninitialized submodules.
+#
+# Uninitialized submodules are listed with an initial dash.
+if $use_git && git submodule | grep '^-' >/dev/null; then
+  die "some git submodules are not initialized. "     \
+      "Run 'git submodule init' and bootstrap again."
+fi
+
+# Remove any dangling symlink matching "*.m4" or "*.[ch]" in some
+# gnulib-populated directories.  Such .m4 files would cause aclocal to fail.
+# The following requires GNU find 4.2.3 or newer.  Considering the usual
+# portability constraints of this script, that may seem a very demanding
+# requirement, but it should be ok.  Ignore any failure, which is fine,
+# since this is only a convenience to help developers avoid the relatively
+# unusual case in which a symlinked-to .m4 file is git-removed from gnulib
+# between successive runs of this script.
+find "$m4_base" "$source_base" \
+  -depth \( -name '*.m4' -o -name '*.[ch]' \) \
+  -type l -xtype l -delete > /dev/null 2>&1
+
+# Invoke autoreconf with --force --install to ensure upgrades of tools
+# such as ylwrap.
+AUTORECONFFLAGS="--verbose --install --force -I $m4_base $ACLOCAL_FLAGS"
+
+# Some systems (RHEL 5) are using ancient autotools, for which the
+# --no-recursive option had not been invented.  Detect that lack and
+# omit the option when it's not supported.  FIXME in 2017: remove this
+# hack when RHEL 5 autotools are updated, or when they become irrelevant.
+case $($AUTORECONF --help) in
+  *--no-recursive*) AUTORECONFFLAGS="$AUTORECONFFLAGS --no-recursive";;
+esac
+
+# Tell autoreconf not to invoke autopoint or libtoolize; they were run above.
+echo "running: AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS"
+AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS \
+  || die "autoreconf failed"
+
+# Get some extra files from gnulib, overriding existing files.
+for file in $gnulib_extra_files; do
+  case $file in
+  */INSTALL) dst=INSTALL;;
+  build-aux/*) dst=$build_aux/${file#build-aux/};;
+  *) dst=$file;;
+  esac
+  symlink_to_dir "$GNULIB_SRCDIR" $file $dst \
+    || die "failed to symlink $file"
+done
+
+if test $with_gettext = yes; then
+  # Create gettext configuration.
+  echo "$0: Creating po/Makevars from po/Makevars.template ..."
+  rm -f po/Makevars
+  sed '
+    /^EXTRA_LOCALE_CATEGORIES *=/s/=.*/= '"$EXTRA_LOCALE_CATEGORIES"'/
+    /^COPYRIGHT_HOLDER *=/s/=.*/= '"$COPYRIGHT_HOLDER"'/
+    /^MSGID_BUGS_ADDRESS *=/s|=.*|= '"$MSGID_BUGS_ADDRESS"'|
+    /^XGETTEXT_OPTIONS *=/{
+      s/$/ \\/
+      a\
+          '"$XGETTEXT_OPTIONS"' $${end_of_xgettext_options+}
+    }
+  ' po/Makevars.template >po/Makevars \
+    || die 'cannot generate po/Makevars'
+
+  # If the 'gettext' module is in use, grab the latest Makefile.in.in.
+  # If only the 'gettext-h' module is in use, assume autopoint already
+  # put the correct version of this file into place.
+  case $gnulib_modules in
+  *gettext-h*) ;;
+  *gettext*)
+    cp $GNULIB_SRCDIR/build-aux/po/Makefile.in.in po/Makefile.in.in \
+      || die "cannot create po/Makefile.in.in"
+    ;;
+  esac
+
+  if test -d runtime-po; then
+    # Similarly for runtime-po/Makevars, but not quite the same.
+    rm -f runtime-po/Makevars
+    sed '
+      /^DOMAIN *=.*/s/=.*/= '"$package"'-runtime/
+      /^subdir *=.*/s/=.*/= runtime-po/
+      /^MSGID_BUGS_ADDRESS *=/s/=.*/= bug-'"$package"'@gnu.org/
+      /^XGETTEXT_OPTIONS *=/{
+        s/$/ \\/
+        a\
+            '"$XGETTEXT_OPTIONS_RUNTIME"' $${end_of_xgettext_options+}
+      }
+    ' po/Makevars.template >runtime-po/Makevars \
+    || die 'cannot generate runtime-po/Makevars'
+
+    # Copy identical files from po to runtime-po.
+    (cd po && cp -p Makefile.in.in *-quot *.header *.sed *.sin ../runtime-po)
+  fi
+fi
+
+bootstrap_epilogue
+
+echo "$0: done.  Now you can run './configure'."
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/SOURCES/bootstrap.conf b/SOURCES/bootstrap.conf
new file mode 100644
index 0000000..6b043fc
--- /dev/null
+++ b/SOURCES/bootstrap.conf
@@ -0,0 +1,101 @@
+# Bootstrap configuration.
+
+# Copyright (C) 2006-2019 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+
+GNULIB_REVISION=d271f868a8df9bbec29049d01e056481b7a1a263
+
+# gnulib modules used by this package.
+# mbswidth is used by gnulib-fix-width.diff's changes to argp rather than
+# directly.
+gnulib_modules="
+  argp
+  base64
+  error
+  fnmatch
+  getdelim
+  getline
+  gettext-h
+  gitlog-to-changelog
+  mbswidth
+  progname
+  realloc-gnu
+  regex
+  save-cwd
+"
+
+gnulib_tool_option_extras="\
+  --no-conditional-dependencies \
+  --no-vc-files \
+"
+
+gnulib_name=libgnu
+source_base=grub-core/lib/gnulib
+gnulib_extra_files="
+  build-aux/install-sh
+  build-aux/mdate-sh
+  build-aux/texinfo.tex
+  build-aux/depcomp
+  build-aux/config.guess
+  build-aux/config.sub
+"
+
+# Additional xgettext options to use.  Use "\\\newline" to break lines.
+XGETTEXT_OPTIONS=$XGETTEXT_OPTIONS'\\\
+ --from-code=UTF-8\\\
+'
+
+checkout_only_file=
+copy=true
+vc_ignore=
+
+SKIP_PO=t
+
+# Build prerequisites
+buildreq="\
+autoconf   2.63
+automake   1.11
+gettext    0.18.3
+git        1.5.5
+tar        -
+"
+
+# bootstrap doesn't give us a reasonable way to stop Automake from
+# overwriting this, so we just copy our version aside and put it back later.
+cp -a INSTALL INSTALL.grub
+
+bootstrap_post_import_hook () {
+  set -e
+  for patchname in fix-base64 fix-null-deref fix-null-state-deref fix-regcomp-uninit-token \
+      fix-regexec-null-deref fix-uninit-structure fix-unused-value fix-width no-abort; do
+    patch -d grub-core/lib/gnulib -p2 \
+      < "grub-core/lib/gnulib-patches/$patchname.patch"
+  done
+  for patchname in \
+      0001-Support-POTFILES-shell \
+      0002-Handle-gettext_printf-shell-function \
+      0003-Make-msgfmt-output-in-little-endian \
+      0004-Use-SHELL-rather-than-bin-sh; do
+    patch -d po -p3 \
+      < "po/gettext-patches/$patchname.patch"
+  done
+  FROM_BOOTSTRAP=1 ./autogen.sh
+  set +e  # bootstrap expects this
+}
+
+bootstrap_epilogue () {
+  mv INSTALL.grub INSTALL
+}
diff --git a/SOURCES/gitignore b/SOURCES/gitignore
new file mode 100644
index 0000000..f6a1bd0
--- /dev/null
+++ b/SOURCES/gitignore
@@ -0,0 +1,277 @@
+#
+# Ignore patterns in this directory and all subdirectories.
+#
+*.1
+*.8
+*.a
+*.exec
+*.exec.exe
+*.image
+*.image.exe
+*.img
+*.log
+*.lst
+*.marker
+*.mod
+*.o
+*.pf2
+*.pp
+*.trs
+*~
+.deps-core/
+.deps-util/
+.deps/
+.dirstamp
+DISTLIST
+GPATH
+GRTAGS
+GSYMS
+GTAGS
+Makefile
+Makefile.in
+ascii.bitmaps
+genkernsyms.sh
+gensymlist.sh
+grub-bin2h
+grub-emu
+grub-emu-lite
+grub-emu-lite.exe
+grub-emu.exe
+grub-macho2img
+grub_emu_init.c
+grub_emu_init.h
+grub_probe_init.c
+grub_probe_init.h
+grub_script.tab.c
+grub_script.tab.h
+grub_script.yy.c
+grub_script.yy.h
+grub_script_check_init.c
+grub_script_check_init.h
+grub_setup_init.c
+grub_setup_init.h
+mdate-sh
+mod-*.c
+update-grub_lib
+widthspec.bin
+
+#
+# Ignore patterns relative to this .gitignore file's directory.
+#
+/00_header
+/10_*
+/20_linux_xen
+/30_os-prober
+/30_uefi-firmware
+/40_custom
+/41_custom
+/ABOUT-NLS
+/ChangeLog
+/INSTALL.grub
+/Makefile.util.am
+/Makefile.utilgcry.def
+/aclocal.m4
+/ahci_test
+/ascii.h
+/autom4te.cache/
+/btrfs_test
+/build-aux/
+/build-grub-gen-asciih
+/build-grub-gen-widthspec
+/build-grub-mkfont
+/cdboot_test
+/cmp_test
+/compile
+/config-util.h
+/config-util.h.in
+/config.cache
+/config.guess
+/config.h
+/config.log
+/config.status
+/config.sub
+/configure
+/contrib
+/core_compress_test
+/cpio_test
+/date_test
+/depcomp
+/docs/*.info
+/docs/*.info-[0-9]*
+/docs/stamp-1
+/docs/stamp-vti
+/docs/version-dev.texi
+/docs/version.texi
+/ehci_test
+/example_grub_script_test
+/example_scripted_test
+/example_unit_test
+/exfat_test
+/ext234_test
+/f2fs_test
+/fat_test
+/fddboot_test
+/file_filter_test
+/garbage-gen
+/garbage-gen.exe
+/gettext_strings_test
+/gnulib/
+/grub-2.[0-9]*/
+/grub-2.[0-9]*.tar.gz
+/grub-bios-setup
+/grub-bios-setup.exe
+/grub-core/*.module
+/grub-core/*.module.exe
+/grub-core/*.pp
+/grub-core/Makefile.core.am
+/grub-core/Makefile.gcry.def
+/grub-core/bootinfo.txt
+/grub-core/build-grub-module-verifier
+/grub-core/build-grub-pe2elf.exe
+/grub-core/contrib
+/grub-core/gdb_grub
+/grub-core/genmod.sh
+/grub-core/gensyminfo.sh
+/grub-core/gentrigtables
+/grub-core/gentrigtables.exe
+/grub-core/gmodule.pl
+/grub-core/grub.chrp
+/grub-core/kernel.img.bin
+/grub-core/lib/gnulib
+/grub-core/lib/libgcrypt-grub
+/grub-core/modinfo.sh
+/grub-core/rs_decoder.h
+/grub-core/symlist.c
+/grub-core/symlist.h
+/grub-core/trigtables.c
+/grub-core/unidata.c
+/grub-editenv
+/grub-editenv.exe
+/grub-file
+/grub-file.exe
+/grub-fs-tester
+/grub-fstest
+/grub-fstest.exe
+/grub-glue-efi
+/grub-glue-efi.exe
+/grub-install
+/grub-install.exe
+/grub-kbdcomp
+/grub-macbless
+/grub-macbless.exe
+/grub-menulst2cfg
+/grub-menulst2cfg.exe
+/grub-mk*
+/grub-mount
+/grub-ofpathname
+/grub-ofpathname.exe
+/grub-probe
+/grub-probe.exe
+/grub-reboot
+/grub-render-label
+/grub-render-label.exe
+/grub-script-check
+/grub-script-check.exe
+/grub-set-default
+/grub-shell
+/grub-shell-tester
+/grub-sparc64-setup
+/grub-sparc64-setup.exe
+/grub-syslinux2cfg
+/grub-syslinux2cfg.exe
+/grub_cmd_date
+/grub_cmd_echo
+/grub_cmd_regexp
+/grub_cmd_set_date
+/grub_cmd_sleep
+/grub_cmd_test
+/grub_cmd_tr
+/grub_fstest_init.c
+/grub_fstest_init.h
+/grub_func_test
+/grub_script_blanklines
+/grub_script_blockarg
+/grub_script_break
+/grub_script_comments
+/grub_script_continue
+/grub_script_dollar
+/grub_script_echo1
+/grub_script_echo_keywords
+/grub_script_escape_comma
+/grub_script_eval
+/grub_script_expansion
+/grub_script_final_semicolon
+/grub_script_for1
+/grub_script_functions
+/grub_script_gettext
+/grub_script_if
+/grub_script_leading_whitespace
+/grub_script_no_commands
+/grub_script_not
+/grub_script_return
+/grub_script_setparams
+/grub_script_shift
+/grub_script_strcmp
+/grub_script_test
+/grub_script_vars1
+/grub_script_while1
+/gzcompress_test
+/hddboot_test
+/help_test
+/hfs_test
+/hfsplus_test
+/include/grub/cpu
+/include/grub/gcrypt/g10lib.h
+/include/grub/gcrypt/gcrypt.h
+/include/grub/machine
+/install-sh
+/iso9660_test
+/jfs_test
+/lib/libgcrypt-grub
+/libgrub_a_init.c
+/lzocompress_test
+/m4/
+/minixfs_test
+/missing
+/netboot_test
+/nilfs2_test
+/ntfs_test
+/ohci_test
+/partmap_test
+/pata_test
+/po/*.gmo
+/po/*.mo
+/po/*.po
+/po/LINGUAS
+/po/Makefile.in.in
+/po/Makevars
+/po/Makevars.template
+/po/POTFILES
+/po/POTFILES-shell.in
+/po/POTFILES.in
+/po/Rules-quot
+/po/grub.pot
+/po/remove-potcdate.sed
+/po/stamp-po
+/printf_test
+/priority_queue_unit_test
+/pseries_test
+/reiserfs_test
+/romfs_test
+/squashfs_test
+/stamp-h
+/stamp-h.in
+/stamp-h1
+/syslinux_test
+/tar_test
+/test_sha512sum
+/test_unset
+/tests/syslinux/ubuntu10.04_grub.cfg
+/texinfo.tex
+/udf_test
+/uhci_test
+/util/bash-completion.d/grub
+/widthspec.h
+/xfs_test
+/xzcompress_test
+/zfs_test
diff --git a/SOURCES/grub.macros b/SOURCES/grub.macros
new file mode 100644
index 0000000..6169745
--- /dev/null
+++ b/SOURCES/grub.macros
@@ -0,0 +1,642 @@
+# vim:filetype=spec
+# Modules always contain just 32-bit code
+%global _libdir %{_exec_prefix}/lib
+%global _binaries_in_noarch_packages_terminate_build 0
+#%%undefine _missing_build_ids_terminate_build
+%{expand:%%{!?buildsubdir:%%global buildsubdir grub-%{tarversion}}}
+%{expand:%%{!?_licensedir:%%global license %%%%doc}}
+
+%global _configure ../configure
+
+%if %{?_with_ccache: 1}%{?!_with_ccache: 0}
+%global cc_equals CC=/usr/%{_lib}/ccache/gcc
+%else
+%global cc_equals %{nil}
+%endif
+
+%global cflags_sed						\\\
+	sed							\\\
+		-e 's/-O. //g'					\\\
+		-e 's/-fplugin=annobin //g'			\\\
+		-e 's,-specs=[[:alnum:]/_-]*annobin[[:alnum:]_-]* ,,g' \\\
+		-e 's/-fstack-protector[[:alpha:]-]\\+//g'	\\\
+		-e 's/-Wp,-D_FORTIFY_SOURCE=[[:digit:]]\\+//g'	\\\
+		-e 's/--param=ssp-buffer-size=4//g'		\\\
+		-e 's/-mregparm=3/-mregparm=4/g'		\\\
+		-e 's/-fexceptions//g'				\\\
+		-e 's/-fcf-protection//g'			\\\
+		-e 's/-fasynchronous-unwind-tables//g'		\\\
+		-e 's/^/ -fno-strict-aliasing /'		\\\
+		%{nil}
+
+%global host_cflags %{expand:%%(echo %{build_cflags} %{?_hardening_cflags} | %{cflags_sed})}
+%global legacy_host_cflags					\\\
+	%{expand:%%(echo %{host_cflags} |			\\\
+	%{cflags_sed}						\\\
+		-e 's/-m64//g'					\\\
+		-e 's/-mcpu=power[[:alnum:]]\\+/-mcpu=power6/g'	\\\
+	)}
+%global efi_host_cflags %{expand:%%(echo %{host_cflags})}
+
+%global target_cflags %{expand:%%(echo %{build_cflags} | %{cflags_sed})}
+%global legacy_target_cflags					\\\
+	%{expand:%%(echo %{target_cflags} | 			\\\
+	%{cflags_sed}						\\\
+		-e 's/-m64//g'					\\\
+		-e 's/-mcpu=power[[:alnum:]]\\+/-mcpu=power6/g'	\\\
+	)}
+%global efi_target_cflags %{expand:%%(echo %{target_cflags})}
+
+%global ldflags_sed						\\\
+	sed							\\\
+		-e 's,-specs=[[:alnum:]/_-]*annobin[[:alnum:]_-]* ,,g' \\\
+		-e 's/^$//'					\\\
+		%{nil}
+
+%global host_ldflags %{expand:%%(echo %{build_ldflags} %{?_hardening_ldflags} | %{ldflags_sed})}
+%global legacy_host_ldflags					\\\
+	%{expand:%%(echo %{host_ldflags} |			\\\
+	%{ldflags_sed}						\\\
+	)}
+%global efi_host_ldflags %{expand:%%(echo %{host_ldflags})}
+
+%global target_ldflags %{expand:%%(echo %{build_ldflags} -static | %{ldflags_sed})}
+%global legacy_target_ldflags					\\\
+	%{expand:%%(echo %{target_ldflags} | 			\\\
+	%{ldflags_sed}						\\\
+	)}
+%global efi_target_ldflags %{expand:%%(echo %{target_ldflags})}
+
+%global with_efi_arch 0
+%global with_alt_efi_arch 0
+%global with_legacy_arch 0
+%global with_emu_arch 1
+%global emuarch %{_arch}
+%global grubefiarch %{nil}
+%global grublegacyarch %{nil}
+
+# sparc is always compiled 64 bit
+%ifarch %{sparc}
+%global target_cpu_name sparc64
+%global _target_platform %{target_cpu_name}-%{_vendor}-%{_target_os}%{?_gnu}
+%global legacy_target_cpu_name %{_arch}
+%global legacy_package_arch ieee1275
+%global platform ieee1275
+%endif
+# ppc is always compiled 64 bit
+%ifarch ppc ppc64 ppc64le
+# GRUB emu fails to build on ppc64le
+%global with_emu_arch 0
+%global target_cpu_name %{_arch}
+%global legacy_target_cpu_name powerpc
+%global legacy_package_arch %{_arch}
+%global legacy_grub_dir powerpc-ieee1275
+%global _target_platform %{target_cpu_name}-%{_vendor}-%{_target_os}%{?_gnu}
+%global platform ieee1275
+%endif
+
+
+%global efi_only aarch64 %{arm} riscv64
+%global efi_arch x86_64 ia64 %{efi_only}
+%ifarch %{efi_arch}
+%global with_efi_arch 1
+%else
+%global with_efi_arch 0
+%endif
+%ifarch %{efi_only}
+%global with_efi_only 1
+%else
+%global with_efi_only 0
+%endif
+%{!?with_efi_arch:%global without_efi_arch 0}
+%{?with_efi_arch:%global without_efi_arch 1}
+%{!?with_efi_only:%global without_efi_only 0}
+%{?with_efi_only:%global without_efi_only 1}
+
+### fixme
+%ifarch aarch64 %{arm} riscv64
+%global efi_modules " "
+%else
+%global efi_modules " backtrace chain tpm usb usbserial_common usbserial_pl2303 usbserial_ftdi usbserial_usbdebug keylayouts at_keyboard "
+%endif
+
+%ifarch aarch64 %{arm} riscv64
+%global legacy_provides -l
+%endif
+
+%ifarch %{ix86}
+%global efiarch ia32
+%global target_cpu_name i386
+%global grub_target_name i386-efi
+%global package_arch efi-ia32
+
+%global legacy_target_cpu_name i386
+%global legacy_package_arch pc
+%global platform pc
+%endif
+
+%ifarch x86_64
+%global efiarch x64
+%global target_cpu_name %{_arch}
+%global grub_target_name %{_arch}-efi
+%global package_arch efi-x64
+
+%global legacy_target_cpu_name i386
+%global legacy_package_arch pc
+%global platform pc
+
+%global alt_efi_arch ia32
+%global alt_target_cpu_name i386
+%global alt_grub_target_name i386-efi
+%global alt_platform efi
+%global alt_package_arch efi-ia32
+
+%global alt_efi_host_cflags %{expand:%%(echo %{efi_host_cflags})}
+%global alt_efi_target_cflags					\\\
+	%{expand:%%(echo %{target_cflags} |			\\\
+	%{cflags_sed}						\\\
+		-e 's/-m64//g'					\\\
+	)}
+%endif
+
+%ifarch aarch64
+%global emuarch arm64
+%global efiarch aa64
+%global target_cpu_name aarch64
+%global grub_target_name arm64-efi
+%global package_arch efi-aa64
+%endif
+
+%ifarch riscv64
+%global emuarch riscv64
+%global efiarch riscv64
+%global target_cpu_name riscv64
+%global grub_target_name riscv64-efi
+%global package_arch efi-riscv64
+%endif
+
+%ifarch %{arm}
+%global efiarch arm
+%global target_cpu_name arm
+%global grub_target_name arm-efi
+%global package_arch efi-arm
+%global efi_target_cflags						\\\
+	%{expand:%%(echo %{optflags} |					\\\
+	%{cflags_sed}							\\\
+		-e 's/-march=armv7-a[[:alnum:]+-]*/&+nofp/g'		\\\
+		-e 's/-mfpu=[[:alnum:]-]\\+//g'				\\\
+		-e 's/-mfloat-abi=[[:alpha:]]\\+/-mfloat-abi=soft/g'	\\\
+	)}
+%endif
+
+%global _target_platform %{target_cpu_name}-%{_vendor}-%{_target_os}%{?_gnu}
+%global _alt_target_platform %{alt_target_cpu_name}-%{_vendor}-%{_target_os}%{?_gnu}
+
+%ifarch %{efi_arch}
+%global with_efi_arch 1
+%global grubefiname grub%{efiarch}.efi
+%global grubeficdname gcd%{efiarch}.efi
+%global grubefiarch %{target_cpu_name}-efi
+%ifarch %{ix86}
+%global with_efi_modules 0
+%global without_efi_modules 1
+%else
+%global with_efi_modules 1
+%global without_efi_modules 0
+%endif
+%endif
+
+%if 0%{?alt_efi_arch:1}
+%global with_alt_efi_arch 1
+%global grubaltefiname grub%{alt_efi_arch}.efi
+%global grubalteficdname gcd%{alt_efi_arch}.efi
+%global grubaltefiarch %{alt_target_cpu_name}-efi
+%endif
+
+%ifnarch %{efi_only}
+%global with_legacy_arch 1
+%global grublegacyarch %{legacy_target_cpu_name}-%{platform}
+%global moduledir %{legacy_target_cpu_name}-%{platform}
+%endif
+
+%global evr %{epoch}:%{version}-%{release}
+
+%ifarch x86_64
+%global with_efi_common 1
+%global with_legacy_modules 0
+%global with_legacy_common 0
+%else
+%global with_efi_common 0
+%global with_legacy_common 1
+%global with_legacy_modules 1
+%endif
+
+%define define_legacy_variant()						\
+%{expand:%%package %%{1}}						\
+Summary:	Bootloader with support for Linux, Multiboot, and more	\
+Provides:	%{name} = %{evr}					\
+Obsoletes:	%{name} < %{evr}					\
+Requires:	%{name}-common = %{evr}					\
+Requires:	%{name}-tools-minimal = %{evr}				\
+Requires:	%{name}-%{1}-modules = %{evr}				\
+Requires:	gettext which file					\
+Requires:	%{name}-tools = %{evr}					\
+Requires(pre):	dracut							\
+Requires(post): dracut							\
+%{expand:%%description %%{1}}						\
+%{desc}									\
+This subpackage provides support for %{1} systems.			\
+									\
+%{expand:%%{?!buildsubdir:%%define buildsubdir grub-%%{1}-%{tarversion}}}\
+%{expand:%%if 0%%{with_legacy_modules}					\
+%%package %%{1}-modules							\
+Summary:	Modules used to build custom grub images		\
+BuildArch:	noarch							\
+Requires:	%%{name}-common = %%{evr}				\
+%%description %%{1}-modules						\
+%%{desc}								\
+This subpackage provides support for rebuilding your own grub.efi.	\
+%%endif									\
+}									\
+									\
+%{expand:%%{?!buildsubdir:%%define buildsubdir grub-%%{1}-%{tarversion}}}\
+%{expand:%%package %%{1}-tools}						\
+Summary:	Support tools for GRUB.					\
+Requires:	gettext os-prober which file system-logos		\
+Requires:	%{name}-common = %{evr}					\
+Requires:	%{name}-tools-minimal = %{evr}				\
+Requires:	os-prober >= 1.58-11					\
+Requires:	gettext which file					\
+									\
+%{expand:%%description %%{1}-tools}					\
+%{desc}									\
+This subpackage provides tools for support of %%{1} platforms.		\
+%{nil}
+
+%define define_efi_variant(o)						\
+%{expand:%%package %{1}}						\
+Summary:	GRUB for EFI systems.					\
+Requires:	efi-filesystem						\
+Requires:	%{name}-common = %{evr}					\
+Requires:	%{name}-tools-minimal >= %{evr}				\
+Requires:	%{name}-tools = %{evr}					\
+Provides:	%{name}-efi = %{evr}					\
+%{?legacy_provides:Provides:	%{name} = %{evr}}			\
+%{-o:Obsoletes:	%{name}-efi < %{evr}}					\
+									\
+%{expand:%%description %{1}}						\
+%{desc}									\
+This subpackage provides support for %{1} systems.			\
+									\
+%{expand:%%{?!buildsubdir:%%define buildsubdir grub-%{1}-%{tarversion}}}\
+%{expand:%if 0%{?with_efi_modules}					\
+%{expand:%%package %{1}-modules}					\
+Summary:	Modules used to build custom grub.efi images		\
+BuildArch:	noarch							\
+Requires:	%{name}-common = %{evr}					\
+Provides:	%{name}-efi-modules = %{evr}				\
+Obsoletes:	%{name}-efi-modules < %{evr}				\
+%{expand:%%description %{1}-modules}					\
+%{desc}									\
+This subpackage provides support for rebuilding your own grub.efi.	\
+%endif}									\
+									\
+%{expand:%%package %{1}-cdboot}						\
+Summary:	Files used to boot removeable media with EFI		\
+Requires:	%{name}-common = %{evr}					\
+Provides:	%{name}-efi-cdboot = %{evr}				\
+%{expand:%%description %{1}-cdboot}					\
+%{desc}									\
+This subpackage provides optional components of grub used with removeable media on %{1} systems.\
+%{nil}
+
+%global do_common_setup()					\
+%setup -q -n grub-%{tarversion}					\
+rm -fv docs/*.info						\
+cp %{SOURCE6} .gitignore					\
+cp %{SOURCE7} bootstrap						\
+cp %{SOURCE8} bootstrap.conf					\
+cp %{SOURCE9} ./grub-core/tests/strtoull_test.c			\
+cp %{SOURCE2} gnulib-%{gnulibversion}.tar.gz			\
+tar -zxf gnulib-%{gnulibversion}.tar.gz				\
+mv gnulib-%{gnulibversion} gnulib				\
+git init							\
+echo '![[:digit:]][[:digit:]]_*.in' > util/grub.d/.gitignore	\
+echo '!*.[[:digit:]]' > util/.gitignore				\
+echo '!config.h' > include/grub/emu/.gitignore			\
+git config user.email "%{name}-owner@fedoraproject.org"		\
+git config user.name "Fedora Ninjas"				\
+git config gc.auto 0						\
+rm -f configure							\
+git add .							\
+git commit -a -q -m "%{tarversion} baseline."			\
+#git apply --index --whitespace=nowarn %{SOURCE3}		\
+#git commit -a -q -m "%{tarversion} master."			\
+git am --whitespace=nowarn %%{patches} </dev/null		\
+./bootstrap							\
+%{nil}
+
+%define do_efi_configure()					\
+%configure							\\\
+	%{cc_equals}						\\\
+	HOST_CFLAGS="%{3}"					\\\
+	HOST_CPPFLAGS="-I$(pwd)"				\\\
+	HOST_LDFLAGS="%{efi_host_ldflags}"			\\\
+	TARGET_CFLAGS="%{2}"					\\\
+	TARGET_CPPFLAGS="-I$(pwd)"				\\\
+	TARGET_LDFLAGS="%{efi_target_ldflags}"			\\\
+	--with-platform=efi					\\\
+	--with-utils=host					\\\
+	--target=%{1}						\\\
+	--with-grubdir=%{name}					\\\
+	--program-transform-name=s,grub,%{name},		\\\
+	--disable-werror || ( cat config.log ; exit 1 )		\
+git add .							\
+git commit -m "After efi configure"				\
+%{nil}
+
+%define do_efi_build_modules()					\
+make %{?_smp_mflags} ascii.h widthspec.h			\
+make %{?_smp_mflags} -C grub-core				\
+%{nil}
+
+%define do_efi_build_all()					\
+make %{?_smp_mflags}						\
+%{nil}
+
+%define do_efi_link_utils()					\
+for x in grub-mkimage ; do					\\\
+	ln ../grub-%{1}-%{tarversion}/${x} ./ ;			\\\
+done								\
+%{nil}
+
+%define do_install_protected_file()				\
+touch %{1}.conf							\
+echo %{1} > %{1}.conf						\
+install -d -m 755 ${RPM_BUILD_ROOT}/etc/dnf/protected.d/	\
+install -m 644 %{1}.conf ${RPM_BUILD_ROOT}/etc/dnf/protected.d/ \
+rm -f %{1}.conf							\
+%{nil}
+
+%ifarch x86_64 aarch64 %{arm} riscv64
+%define mkimage()						\
+%{4}./grub-mkimage -O %{1} -o %{2}.orig				\\\
+	-p /EFI/%{efi_vendor} -d grub-core ${GRUB_MODULES}	\\\
+	--sbat %{4}./sbat.csv					\
+%{4}./grub-mkimage -O %{1} -o %{3}.orig				\\\
+	-p /EFI/BOOT -d grub-core ${GRUB_MODULES}		\\\
+	--sbat %{4}./sbat.csv					\
+%{expand:%%define ___pesign_client_cert %{?___pesign_client_cert}%{!?___pesign_client_cert:%{__pesign_client_cert}}} \
+%{?__pesign_client_cert:%{expand:%%define __pesign_client_cert %{___pesign_client_cert}}} \
+%{expand:%%{pesign -s -i %%{2}.orig -o %%{2}.onesig -a %%{5} -c %%{6} -n %%{7}}}	\
+%{expand:%%{pesign -s -i %%{3}.orig -o %%{3}.onesig -a %%{5} -c %%{6} -n %%{7}}}	\
+%{expand:%%define __pesign_client_cert %{name}-signer} \
+%{expand:%%{pesign -s -i %%{2}.onesig -o %%{2} -a %%{8} -c %%{9} -n %%{10}}}	\
+%{expand:%%{pesign -s -i %%{3}.onesig -o %%{3} -a %%{8} -c %%{9} -n %%{10}}}	\
+%{nil}
+%else
+%define mkimage()						\
+%{4}./grub-mkimage -O %{1} -o %{2}				\\\
+	-p /EFI/%{efi_vendor} -d grub-core ${GRUB_MODULES}	\
+%{4}./grub-mkimage -O %{1} -o %{3}				\\\
+	-p /EFI/BOOT -d grub-core ${GRUB_MODULES}		\
+%{nil}
+%endif
+
+%define do_efi_build_images()					\
+GRUB_MODULES="	all_video boot blscfg btrfs			\\\
+		cat configfile cryptodisk			\\\
+		echo efi_netfs efifwsetup efinet ext2 f2fs	\\\
+		fat font gcry_rijndael gcry_rsa gcry_serpent 	\\\
+		gcry_sha256 gcry_twofish gcry_whirlpool   	\\\
+		gfxmenu gfxterm gzio				\\\
+		halt hfsplus http increment iso9660 jpeg	\\\
+		loadenv loopback linux lvm lsefi lsefimmap luks	\\\
+		luks2 mdraid09 mdraid1x minicmd net		\\\
+		normal part_apple part_msdos part_gpt		\\\
+		password_pbkdf2 pgp png reboot			\\\
+		regexp search search_fs_uuid search_fs_file	\\\
+		search_label serial sleep syslinuxcfg test tftp	\\\
+		version video xfs zstd "			\
+GRUB_MODULES+=%{efi_modules}					\
+%{expand:%%{mkimage %{1} %{2} %{3} %{4} %{5} %{6} %{7} %{8} %{9} %{10}}}			\
+%{nil}
+
+%define do_primary_efi_build()					\
+cd grub-%{1}-%{tarversion}					\
+%{expand:%%do_efi_configure %%{4} %%{5} %%{6}}			\
+%do_efi_build_all						\
+%{expand:%%do_efi_build_images %{grub_target_name} %{2} %{3} ./  %{7} %{8} %{9} %{10} %{11} %{12}} \
+cd ..								\
+%{nil}
+
+%define do_alt_efi_build()					\
+cd grub-%{1}-%{tarversion}					\
+%{expand:%%do_efi_configure %%{4} %%{5} %%{6}}			\
+%do_efi_build_modules						\
+%{expand:%%do_efi_link_utils %{grubefiarch}}			\
+%{expand:%%do_efi_build_images %{alt_grub_target_name} %{2} %{3} ../grub-%{grubefiarch}-%{tarversion}/ %{7} %{8} %{9} %{10} %{11} %{12}} \
+cd ..								\
+%{nil}
+
+%define do_legacy_build()					\
+cd grub-%{1}-%{tarversion}					\
+%configure							\\\
+	%{cc_equals}						\\\
+	HOST_CFLAGS="%{legacy_host_cflags}"			\\\
+	HOST_CPPFLAGS="-I$(pwd)"				\\\
+	HOST_LDFLAGS="%{legacy_host_ldflags}"			\\\
+	TARGET_CFLAGS="%{legacy_target_cflags}"			\\\
+	TARGET_CPPFLAGS="-I$(pwd)"				\\\
+	TARGET_LDFLAGS="%{legacy_target_ldflags}"		\\\
+	--with-platform=%{platform}				\\\
+	--with-utils=host					\\\
+	--target=%{_target_platform}				\\\
+	--with-grubdir=%{name}					\\\
+	--program-transform-name=s,grub,%{name},		\\\
+	--disable-werror || ( cat config.log ; exit 1 )		\
+git add .							\
+git commit -m "After legacy configure"				\
+make %{?_smp_mflags}						\
+cd ..								\
+%{nil}
+
+%define do_emu_build()						\
+cd grub-emu-%{tarversion}					\
+%configure							\\\
+	%{cc_equals}						\\\
+	HOST_CFLAGS="%{legacy_host_cflags}"			\\\
+	HOST_CPPFLAGS="-I$(pwd)"				\\\
+	HOST_LDFLAGS="%{legacy_host_ldflags}"			\\\
+	--with-platform=emu					\\\
+	--with-grubdir=%{name}					\\\
+	--program-transform-name=s,grub,%{name},		\\\
+	--disable-werror || ( cat config.log ; exit 1 )		\
+git add .							\
+git commit -m "After emu configure"				\
+make %{?_smp_mflags} ascii.h widthspec.h			\
+make %{?_smp_mflags} -C grub-core/lib/gnulib			\
+make %{?_smp_mflags} -C grub-core				\
+cd ..								\
+%{nil}
+
+%define do_alt_efi_install()					\
+cd grub-%{1}-%{tarversion}					\
+install -d -m 755 $RPM_BUILD_ROOT/usr/lib/grub/%{grubaltefiarch}/ \
+find . '(' -iname gdb_grub					\\\
+	-o -iname kernel.exec					\\\
+	-o -iname kernel.img					\\\
+	-o -iname config.h					\\\
+	-o -iname gmodule.pl					\\\
+	-o -iname modinfo.sh					\\\
+	-o -iname '*.lst'					\\\
+	-o -iname '*.mod'					\\\
+	')'							\\\
+	-exec cp {} $RPM_BUILD_ROOT/usr/lib/grub/%{grubaltefiarch}/ \\\; \
+find $RPM_BUILD_ROOT -type f -iname "*.mod*" -exec chmod a-x {} '\;'	\
+install -m 700 %{2} $RPM_BUILD_ROOT%{efi_esp_dir}/%{2}	\
+install -m 700 %{3} $RPM_BUILD_ROOT%{efi_esp_dir}/%{3} \
+%{expand:%%do_install_protected_file %{name}-%{alt_package_arch}} \
+cd ..								\
+%{nil}
+
+%define do_efi_install()					\
+cd grub-%{1}-%{tarversion}					\
+make DESTDIR=$RPM_BUILD_ROOT install				\
+if [ -f $RPM_BUILD_ROOT%{_infodir}/grub.info ]; then		\
+	rm -f $RPM_BUILD_ROOT%{_infodir}/grub.info		\
+fi								\
+if [ -f $RPM_BUILD_ROOT%{_infodir}/grub-dev.info ]; then	\
+	rm -f $RPM_BUILD_ROOT%{_infodir}/grub-dev.info		\
+fi								\
+find $RPM_BUILD_ROOT -iname "*.module" -exec chmod a-x {} '\;'	\
+ln -s ../boot/%{name}/grub.cfg					\\\
+	$RPM_BUILD_ROOT%{_sysconfdir}/%{name}-efi.cfg		\
+install -m 700 %{2} $RPM_BUILD_ROOT%{efi_esp_dir}/%{2}		\
+install -m 700 %{3} $RPM_BUILD_ROOT%{efi_esp_dir}/%{3}		\
+%ifarch %{arm}							\
+install -D -m 700 %{2} $RPM_BUILD_ROOT%{efi_esp_boot}/BOOTARM.EFI \
+%endif								\
+install -D -m 700 unicode.pf2					\\\
+	${RPM_BUILD_ROOT}/boot/%{name}/fonts/unicode.pf2		\
+${RPM_BUILD_ROOT}/%{_bindir}/%{name}-editenv			\\\
+		${RPM_BUILD_ROOT}/boot/%{name}/grubenv create	\
+%{expand:%%do_install_protected_file %{name}-%{package_arch}}	\
+cd ..								\
+%{nil}
+
+%define do_legacy_install()					\
+cd grub-%{1}-%{tarversion}					\
+make DESTDIR=$RPM_BUILD_ROOT install				\
+if [ -f $RPM_BUILD_ROOT%{_infodir}/grub.info ]; then		\
+	rm -f $RPM_BUILD_ROOT%{_infodir}/grub.info		\
+fi								\
+if [ -f $RPM_BUILD_ROOT%{_infodir}/grub-dev.info ]; then	\
+	rm -f $RPM_BUILD_ROOT%{_infodir}/grub-dev.info		\
+fi								\
+if [ -f $RPM_BUILD_ROOT/%{_libdir}/grub/%{1}/%{name}.chrp ]; then \
+	mv $RPM_BUILD_ROOT/%{_libdir}/grub/%{1}/%{name}.chrp	\\\
+	   $RPM_BUILD_ROOT/%{_libdir}/grub/%{1}/%{name}.chrp	\
+fi								\
+if [ %{3} -eq 0 ]; then						\
+	${RPM_BUILD_ROOT}/%{_bindir}/%{name}-editenv		\\\
+		${RPM_BUILD_ROOT}/boot/%{name}/grubenv create	\
+fi								\
+%{expand:%%do_install_protected_file %{name}-%{legacy_package_arch}} \
+cd ..								\
+%{nil}
+
+%define do_emu_install()					\
+cd grub-emu-%{tarversion}					\
+make DESTDIR=$RPM_BUILD_ROOT install -C grub-core		\
+if [ -f $RPM_BUILD_ROOT%{_infodir}/grub.info ]; then		\
+	rm -f $RPM_BUILD_ROOT%{_infodir}/grub.info		\
+fi								\
+if [ -f $RPM_BUILD_ROOT%{_infodir}/grub-dev.info ]; then	\
+	rm -f $RPM_BUILD_ROOT%{_infodir}/grub-dev.info		\
+fi								\
+if [ -f $RPM_BUILD_ROOT/%{_libdir}/grub/%{1}/%{name}.chrp ]; then \
+	mv $RPM_BUILD_ROOT/%{_libdir}/grub/%{1}/%{name}.chrp	\\\
+	   $RPM_BUILD_ROOT/%{_libdir}/grub/%{1}/grub.chrp	\
+fi								\
+cd ..								\
+%{nil}
+
+%define do_common_install()					\
+install -d -m 0755 						\\\
+	$RPM_BUILD_ROOT%{_datarootdir}/locale/en\@quot		\\\
+	$RPM_BUILD_ROOT%{_datarootdir}/locale/en		\\\
+	$RPM_BUILD_ROOT%{_infodir}/				\
+cp -a $RPM_BUILD_ROOT%{_datarootdir}/locale/en\@quot		\\\
+	$RPM_BUILD_ROOT%{_datarootdir}/locale/en		\
+cp docs/grub.info $RPM_BUILD_ROOT%{_infodir}/%{name}.info	\
+cp docs/grub-dev.info						\\\
+	$RPM_BUILD_ROOT%{_infodir}/%{name}-dev.info		\
+install -d -m 0700 ${RPM_BUILD_ROOT}%{efi_esp_dir}/		\
+install -d -m 0700 ${RPM_BUILD_ROOT}/boot/%{name}/		\
+install -d -m 0700 ${RPM_BUILD_ROOT}/boot/loader/entries	\
+install -d -m 0700 ${RPM_BUILD_ROOT}/boot/%{name}/themes/system	\
+install -d -m 0700 ${RPM_BUILD_ROOT}%{_sysconfdir}/default	\
+install -d -m 0700 ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig	\
+touch ${RPM_BUILD_ROOT}%{_sysconfdir}/default/grub		\
+ln -sf ../default/grub						\\\
+	${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig/grub		\
+touch ${RPM_BUILD_ROOT}/boot/%{name}/grub.cfg			\
+ln -s ../boot/%{name}/grub.cfg					\\\
+	${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}.cfg		\
+%{nil}
+
+%define define_legacy_variant_files()				\
+%{expand:%%files %{1}}						\
+%defattr(-,root,root,-)						\
+%config(noreplace) %{_sysconfdir}/%{name}.cfg			\
+%ghost %config(noreplace) /boot/%{name}/grub.cfg		\
+%dir %attr(0700,root,root)/boot/loader/entries			\
+%attr(0644,root,root) %config(noreplace) /etc/dnf/protected.d/%{name}-%{1}.conf \
+								\
+%{expand:%if 0%{?with_legacy_modules}				\
+%{expand:%%files %{1}-modules}					\
+%defattr(-,root,root)						\
+%dir %{_libdir}/grub/%{2}/					\
+%{_libdir}/grub/%{2}/*						\
+%exclude %{_libdir}/grub/%{2}/*.module				\
+%exclude %{_libdir}/grub/%{2}/{boot,boot_hybrid,cdboot,diskboot,lzma_decompress,pxeboot}.image \
+%exclude %{_libdir}/grub/%{2}/*.o				\
+%else								\
+%%exclude %%{_libdir}/grub/%%{grublegacyarch}/*			\
+%endif}								\
+%{nil}
+
+%define define_efi_variant_files()				\
+%{expand:%%files %{1}}						\
+%defattr(-,root,root,-)						\
+%config(noreplace) %{_sysconfdir}/%{name}.cfg			\
+%config(noreplace) %{_sysconfdir}/%{name}-efi.cfg		\
+%attr(0700,root,root)%{efi_esp_dir}/%{2}			\
+%ifarch %{arm}							\
+%attr(0700,root,root)%{efi_esp_boot}/BOOTARM.EFI		\
+%endif								\
+%attr(0700,root,root)/boot/%{name}/fonts			\
+%dir %attr(0700,root,root)/boot/loader/entries			\
+%ghost %config(noreplace) /boot/%{name}/grub.cfg		\
+%ghost %config(noreplace) %attr(0700,root,root)%{efi_esp_dir}/grub.cfg	\
+%config(noreplace) %verify(not size mode md5 mtime) /boot/%{name}/grubenv	\
+%attr(0644,root,root) %config(noreplace) /etc/dnf/protected.d/%{name}-%{1}.conf \
+%{expand:%if 0%{?without_efi_modules}				\
+%exclude %{_libdir}/grub/%{6}					\
+%exclude %{_libdir}/grub/%{6}/*					\
+%endif}								\
+								\
+%{expand:%if 0%{?with_efi_modules}				\
+%{expand:%%files %{1}-modules}					\
+%defattr(-,root,root,-)						\
+%dir %{_libdir}/grub/%{6}/					\
+%{_libdir}/grub/%{6}/*						\
+%exclude %{_libdir}/grub/%{6}/*.module				\
+%endif}								\
+								\
+%{expand:%%files %{1}-cdboot}					\
+%defattr(-,root,root,-)						\
+%attr(0700,root,root)%{efi_esp_dir}/%{3}			\
+%attr(0700,root,root)/boot/%{name}/fonts			\
+%{nil}
diff --git a/SOURCES/grub.patches b/SOURCES/grub.patches
new file mode 100644
index 0000000..f474e1f
--- /dev/null
+++ b/SOURCES/grub.patches
@@ -0,0 +1,221 @@
+Patch0001: 0001-Revert-templates-Fix-user-facing-typo-with-an-incorr.patch
+Patch0002: 0002-Revert-templates-Properly-disable-the-os-prober-by-d.patch
+Patch0003: 0003-Revert-templates-Disable-the-os-prober-by-default.patch
+Patch0004: 0004-Add-support-for-Linux-EFI-stub-loading.patch
+Patch0005: 0005-Rework-linux-command.patch
+Patch0006: 0006-Rework-linux16-command.patch
+Patch0007: 0007-Add-secureboot-support-on-efi-chainloader.patch
+Patch0008: 0008-Make-any-of-the-loaders-that-link-in-efi-mode-honor-.patch
+Patch0009: 0009-Handle-multi-arch-64-on-32-boot-in-linuxefi-loader.patch
+Patch0010: 0010-re-write-.gitignore.patch
+Patch0011: 0011-IBM-client-architecture-CAS-reboot-support.patch
+Patch0012: 0012-for-ppc-reset-console-display-attr-when-clear-screen.patch
+Patch0013: 0013-Disable-GRUB-video-support-for-IBM-power-machines.patch
+Patch0014: 0014-Move-bash-completion-script-922997.patch
+Patch0015: 0015-Allow-fallback-to-include-entries-by-title-not-just-.patch
+Patch0016: 0016-Make-exit-take-a-return-code.patch
+Patch0017: 0017-Make-efi-machines-load-an-env-block-from-a-variable.patch
+Patch0018: 0018-Migrate-PPC-from-Yaboot-to-Grub2.patch
+Patch0019: 0019-Add-fw_path-variable-revised.patch
+Patch0020: 0020-Pass-x-hex-hex-straight-through-unmolested.patch
+Patch0021: 0021-blscfg-add-blscfg-module-to-parse-Boot-Loader-Specif.patch
+Patch0022: 0022-Add-devicetree-loading.patch
+Patch0023: 0023-Don-t-write-messages-to-the-screen.patch
+Patch0024: 0024-Don-t-print-GNU-GRUB-header.patch
+Patch0025: 0025-Don-t-add-to-highlighted-row.patch
+Patch0026: 0026-Message-string-cleanups.patch
+Patch0027: 0027-Fix-border-spacing-now-that-we-aren-t-displaying-it.patch
+Patch0028: 0028-Use-the-correct-indentation-for-the-term-help-text.patch
+Patch0029: 0029-Indent-menu-entries.patch
+Patch0030: 0030-Fix-margins.patch
+Patch0031: 0031-Use-2-instead-of-1-for-our-right-hand-margin-so-line.patch
+Patch0032: 0032-Enable-pager-by-default.-985860.patch
+Patch0033: 0033-F10-doesn-t-work-on-serial-so-don-t-tell-the-user-to.patch
+Patch0034: 0034-Don-t-say-GNU-Linux-in-generated-menus.patch
+Patch0035: 0035-Don-t-draw-a-border-around-the-menu.patch
+Patch0036: 0036-Use-the-standard-margin-for-the-timeout-string.patch
+Patch0037: 0037-Add-.eh_frame-to-list-of-relocations-stripped.patch
+Patch0038: 0038-Don-t-require-a-password-to-boot-entries-generated-b.patch
+Patch0039: 0039-Don-t-emit-Booting-.-message.patch
+Patch0040: 0040-Replace-a-lot-of-man-pages-with-slightly-nicer-ones.patch
+Patch0041: 0041-use-fw_path-prefix-when-fallback-searching-for-grub-.patch
+Patch0042: 0042-Try-mac-guid-etc-before-grub.cfg-on-tftp-config-file.patch
+Patch0043: 0043-Generate-OS-and-CLASS-in-10_linux-from-etc-os-releas.patch
+Patch0044: 0044-Minimize-the-sort-ordering-for-.debug-and-rescue-ker.patch
+Patch0045: 0045-Try-prefix-if-fw_path-doesn-t-work.patch
+Patch0046: 0046-Use-Distribution-Package-Sort-for-grub2-mkconfig-112.patch
+Patch0047: 0047-Make-grub2-mkconfig-construct-titles-that-look-like-.patch
+Patch0048: 0048-Add-friendly-grub2-password-config-tool-985962.patch
+Patch0049: 0049-tcp-add-window-scaling-support.patch
+Patch0050: 0050-efinet-and-bootp-add-support-for-dhcpv6.patch
+Patch0051: 0051-Add-grub-get-kernel-settings-and-use-it-in-10_linux.patch
+Patch0052: 0052-bz1374141-fix-incorrect-mask-for-ppc64.patch
+Patch0053: 0053-Make-grub_fatal-also-backtrace.patch
+Patch0054: 0054-Fix-up-some-man-pages-rpmdiff-noticed.patch
+Patch0055: 0055-Make-our-info-pages-say-grub2-where-appropriate.patch
+Patch0056: 0056-macos-just-build-chainloader-entries-don-t-try-any-x.patch
+Patch0057: 0057-grub2-btrfs-Add-ability-to-boot-from-subvolumes.patch
+Patch0058: 0058-export-btrfs_subvol-and-btrfs_subvolid.patch
+Patch0059: 0059-grub2-btrfs-03-follow_default.patch
+Patch0060: 0060-grub2-btrfs-04-grub2-install.patch
+Patch0061: 0061-grub2-btrfs-05-grub2-mkconfig.patch
+Patch0062: 0062-grub2-btrfs-06-subvol-mount.patch
+Patch0063: 0063-Fallback-to-old-subvol-name-scheme-to-support-old-sn.patch
+Patch0064: 0064-Grub-not-working-correctly-with-btrfs-snapshots-bsc-.patch
+Patch0065: 0065-Add-grub_efi_allocate_pool-and-grub_efi_free_pool-wr.patch
+Patch0066: 0066-Use-grub_efi_.-memory-helpers-where-reasonable.patch
+Patch0067: 0067-Add-PRIxGRUB_EFI_STATUS-and-use-it.patch
+Patch0068: 0068-don-t-use-int-for-efi-status.patch
+Patch0069: 0069-make-GRUB_MOD_INIT-declare-its-function-prototypes.patch
+Patch0070: 0070-Don-t-guess-boot-efi-as-HFS-on-ppc-machines-in-grub-.patch
+Patch0071: 0071-20_linux_xen-load-xen-or-multiboot-2-modules-as-need.patch
+Patch0072: 0072-Make-pmtimer-tsc-calibration-not-take-51-seconds-to-.patch
+Patch0073: 0073-align-struct-efi_variable-better.patch
+Patch0074: 0074-Add-BLS-support-to-grub-mkconfig.patch
+Patch0075: 0075-Don-t-attempt-to-backtrace-on-grub_abort-for-grub-em.patch
+Patch0076: 0076-Add-linux-and-initrd-commands-for-grub-emu.patch
+Patch0077: 0077-Add-grub2-switch-to-blscfg.patch
+Patch0078: 0078-make-better-backtraces.patch
+Patch0079: 0079-normal-don-t-draw-our-startup-message-if-debug-is-se.patch
+Patch0080: 0080-Work-around-some-minor-include-path-weirdnesses.patch
+Patch0081: 0081-Make-it-possible-to-enabled-build-id-sha1.patch
+Patch0082: 0082-Add-grub_qdprintf-grub_dprintf-without-the-file-line.patch
+Patch0083: 0083-Make-a-gdb-dprintf-that-tells-us-load-addresses.patch
+Patch0084: 0084-Fixup-for-newer-compiler.patch
+Patch0085: 0085-Don-t-attempt-to-export-the-start-and-_start-symbols.patch
+Patch0086: 0086-Fixup-for-newer-compiler.patch
+Patch0087: 0087-Add-support-for-non-Ethernet-network-cards.patch
+Patch0088: 0088-net-read-bracketed-ipv6-addrs-and-port-numbers.patch
+Patch0089: 0089-bootp-New-net_bootp6-command.patch
+Patch0090: 0090-efinet-UEFI-IPv6-PXE-support.patch
+Patch0091: 0091-grub.texi-Add-net_bootp6-doument.patch
+Patch0092: 0092-bootp-Add-processing-DHCPACK-packet-from-HTTP-Boot.patch
+Patch0093: 0093-efinet-Setting-network-from-UEFI-device-path.patch
+Patch0094: 0094-efinet-Setting-DNS-server-from-UEFI-protocol.patch
+Patch0095: 0095-Support-UEFI-networking-protocols.patch
+Patch0096: 0096-AUDIT-0-http-boot-tracker-bug.patch
+Patch0097: 0097-grub-editenv-Add-incr-command-to-increment-integer-v.patch
+Patch0098: 0098-Add-auto-hide-menu-support.patch
+Patch0099: 0099-Add-grub-set-bootflag-utility.patch
+Patch0100: 0100-docs-Add-grub-boot-indeterminate.service-example.patch
+Patch0101: 0101-gentpl-add-disable-support.patch
+Patch0102: 0102-gentpl-add-pc-firmware-type.patch
+Patch0103: 0103-efinet-also-use-the-firmware-acceleration-for-http.patch
+Patch0104: 0104-efi-http-Make-root_url-reflect-the-protocol-hostname.patch
+Patch0105: 0105-Make-it-so-we-can-tell-configure-which-cflags-utils-.patch
+Patch0106: 0106-module-verifier-make-it-possible-to-run-checkers-on-.patch
+Patch0107: 0107-Rework-how-the-fdt-command-builds.patch
+Patch0108: 0108-Disable-non-wordsize-allocations-on-arm.patch
+Patch0109: 0109-Prepend-prefix-when-HTTP-path-is-relative.patch
+Patch0110: 0110-Make-grub_error-more-verbose.patch
+Patch0111: 0111-Make-reset-an-alias-for-the-reboot-command.patch
+Patch0112: 0112-Add-a-version-command.patch
+Patch0113: 0113-Add-more-dprintf-and-nerf-dprintf-in-script.c.patch
+Patch0114: 0114-arm-arm64-loader-Better-memory-allocation-and-error-.patch
+Patch0115: 0115-Try-to-pick-better-locations-for-kernel-and-initrd.patch
+Patch0116: 0116-Attempt-to-fix-up-all-the-places-Wsign-compare-error.patch
+Patch0117: 0117-Don-t-use-Wno-sign-compare-Wno-conversion-Wno-error-.patch
+Patch0118: 0118-x86-efi-Use-bounce-buffers-for-reading-to-addresses-.patch
+Patch0119: 0119-x86-efi-Re-arrange-grub_cmd_linux-a-little-bit.patch
+Patch0120: 0120-x86-efi-Make-our-own-allocator-for-kernel-stuff.patch
+Patch0121: 0121-x86-efi-Allow-initrd-params-cmdline-allocations-abov.patch
+Patch0122: 0122-Fix-getroot.c-s-trampolines.patch
+Patch0123: 0123-Do-not-allow-stack-trampolines-anywhere.patch
+Patch0124: 0124-Reimplement-boot_counter.patch
+Patch0125: 0125-Fix-menu-entry-selection-based-on-ID-and-title.patch
+Patch0126: 0126-Make-the-menu-entry-users-option-argument-to-be-opti.patch
+Patch0127: 0127-Add-efi-export-env-and-efi-load-env-commands.patch
+Patch0128: 0128-Make-it-possible-to-subtract-conditions-from-debug.patch
+Patch0129: 0129-Export-all-variables-from-the-initial-context-when-c.patch
+Patch0130: 0130-grub.d-Split-out-boot-success-reset-from-menu-auto-h.patch
+Patch0131: 0131-Fix-systemctl-kexec-exit-status-check.patch
+Patch0132: 0132-Print-grub-emu-linux-loader-messages-as-debug.patch
+Patch0133: 0133-Don-t-assume-that-boot-commands-will-only-return-on-.patch
+Patch0134: 0134-Fix-undefined-references-for-fdt-when-building-with-.patch
+Patch0135: 0135-Do-better-in-bootstrap.conf.patch
+Patch0136: 0136-Use-git-to-apply-gnulib-patches.patch
+Patch0137: 0137-Fix-build-error-with-the-fdt-module-on-risc-v.patch
+Patch0138: 0138-grub-set-bootflag-Update-comment-about-running-as-ro.patch
+Patch0139: 0139-grub-set-bootflag-Write-new-env-to-tmpfile-and-then-.patch
+Patch0140: 0140-grub.d-Fix-boot_indeterminate-getting-set-on-boot_su.patch
+Patch0141: 0141-Also-define-GRUB_EFI_MAX_ALLOCATION_ADDRESS-for-RISC.patch
+Patch0142: 0142-chainloader-Define-machine-types-for-RISC-V.patch
+Patch0143: 0143-Add-start-symbol-for-RISC-V.patch
+Patch0144: 0144-bootstrap.conf-Force-autogen.sh-to-use-python3.patch
+Patch0145: 0145-efi-http-Export-fw-http-_path-variables-to-make-them.patch
+Patch0146: 0146-efi-http-Enclose-literal-IPv6-addresses-in-square-br.patch
+Patch0147: 0147-efi-net-Allow-to-specify-a-port-number-in-addresses.patch
+Patch0148: 0148-efi-ip4_config-Improve-check-to-detect-literal-IPv6-.patch
+Patch0149: 0149-efi-net-Print-a-debug-message-if-parsing-the-address.patch
+Patch0150: 0150-kern-term-Also-accept-F8-as-a-user-interrupt-key.patch
+Patch0151: 0151-efi-Set-image-base-address-before-jumping-to-the-PE-.patch
+Patch0152: 0152-tpm-Don-t-propagate-TPM-measurement-errors-to-the-ve.patch
+Patch0153: 0153-x86-efi-Reduce-maximum-bounce-buffer-size-to-16-MiB.patch
+Patch0154: 0154-http-Prepend-prefix-when-the-HTTP-path-is-relative-a.patch
+Patch0155: 0155-Fix-a-missing-return-in-efi-export-env-and-efi-load-.patch
+Patch0156: 0156-efi-dhcp-fix-some-allocation-error-checking.patch
+Patch0157: 0157-efi-http-fix-some-allocation-error-checking.patch
+Patch0158: 0158-efi-ip-46-_config.c-fix-some-potential-allocation-ov.patch
+Patch0159: 0159-efilinux-Fix-integer-overflows-in-grub_cmd_initrd.patch
+Patch0160: 0160-linuxefi-fail-kernel-validation-without-shim-protoco.patch
+Patch0161: 0161-Fix-const-char-pointers-in-grub-core-net-bootp.c.patch
+Patch0162: 0162-Fix-const-char-pointers-in-grub-core-net-efi-ip4_con.patch
+Patch0163: 0163-Fix-const-char-pointers-in-grub-core-net-efi-ip6_con.patch
+Patch0164: 0164-Fix-const-char-pointers-in-grub-core-net-efi-net.c.patch
+Patch0165: 0165-Fix-const-char-pointers-in-grub-core-net-efi-pxe.c.patch
+Patch0166: 0166-Add-systemd-integration-scripts-to-make-systemctl-re.patch
+Patch0167: 0167-systemd-integration.sh-Also-set-old-menu_show_once-g.patch
+Patch0168: 0168-at_keyboard-use-set-1-when-keyboard-is-in-Translate-.patch
+Patch0169: 0169-grub-install-disable-support-for-EFI-platforms.patch
+Patch0170: 0170-New-with-debug-timestamps-configure-flag-to-prepend-.patch
+Patch0171: 0171-Added-debug-statements-to-grub_disk_open-and-grub_di.patch
+Patch0172: 0172-Introduce-function-grub_debug_is_enabled-void-return.patch
+Patch0173: 0173-Don-t-clear-screen-when-debugging-is-enabled.patch
+Patch0174: 0174-grub_file_-instrumentation-new-file-debug-tag.patch
+Patch0175: 0175-ieee1275-Avoiding-many-unecessary-open-close.patch
+Patch0176: 0176-ieee1275-powerpc-implements-fibre-channel-discovery-.patch
+Patch0177: 0177-ieee1275-powerpc-enables-device-mapper-discovery.patch
+Patch0178: 0178-Add-at_keyboard_fallback_set-var-to-force-the-set-ma.patch
+Patch0179: 0179-Add-suport-for-signing-grub-with-an-appended-signatu.patch
+Patch0180: 0180-docs-grub-Document-signing-grub-under-UEFI.patch
+Patch0181: 0181-docs-grub-Document-signing-grub-with-an-appended-sig.patch
+Patch0182: 0182-dl-provide-a-fake-grub_dl_set_persistent-for-the-emu.patch
+Patch0183: 0183-pgp-factor-out-rsa_pad.patch
+Patch0184: 0184-crypto-move-storage-for-grub_crypto_pk_-to-crypto.c.patch
+Patch0185: 0185-posix_wrap-tweaks-in-preparation-for-libtasn1.patch
+Patch0186: 0186-libtasn1-import-libtasn1-4.16.0.patch
+Patch0187: 0187-libtasn1-disable-code-not-needed-in-grub.patch
+Patch0188: 0188-libtasn1-changes-for-grub-compatibility.patch
+Patch0189: 0189-libtasn1-compile-into-asn1-module.patch
+Patch0190: 0190-test_asn1-test-module-for-libtasn1.patch
+Patch0191: 0191-grub-install-support-embedding-x509-certificates.patch
+Patch0192: 0192-appended-signatures-import-GNUTLS-s-ASN.1-descriptio.patch
+Patch0193: 0193-appended-signatures-parse-PKCS-7-signedData-and-X.50.patch
+Patch0194: 0194-appended-signatures-support-verifying-appended-signa.patch
+Patch0195: 0195-appended-signatures-verification-tests.patch
+Patch0196: 0196-appended-signatures-documentation.patch
+Patch0197: 0197-ieee1275-enter-lockdown-based-on-ibm-secure-boot.patch
+Patch0198: 0198-ieee1275-drop-HEAP_MAX_ADDR-HEAP_MIN_SIZE.patch
+Patch0199: 0199-ieee1275-claim-more-memory.patch
+Patch0200: 0200-ieee1275-request-memory-with-ibm-client-architecture.patch
+Patch0201: 0201-appendedsig-x509-Also-handle-the-Extended-Key-Usage-.patch
+Patch0202: 0202-ieee1275-ofdisk-retry-on-open-failure.patch
+Patch0203: 0203-01_menu_auto_hide.in-fix-a-then-than-typo.patch
+Patch0204: 0204-Fix-disabling-grub-rpm-sort.patch
+Patch0205: 0205-Don-t-check-for-rpmvercmp-in-librpm.patch
+Patch0206: 0206-Allow-chainloading-EFI-apps-from-loop-mounts.patch
+Patch0207: 0207-efinet-Add-DHCP-proxy-support.patch
+Patch0208: 0208-fs-ext2-Ignore-checksum-seed-incompat-feature.patch
+Patch0209: 0209-Don-t-update-the-cmdline-when-generating-legacy-menu.patch
+Patch0210: 0210-Suppress-gettext-error-message.patch
+Patch0211: 0211-grub-boot-success.timer-Only-run-if-not-in-a-contain.patch
+Patch0212: 0212-grub-set-password-Always-use-boot-grub2-user.cfg-as-.patch
+Patch0213: 0213-Remove-outdated-URL-for-BLS-document.patch
+Patch0214: 0214-templates-Check-for-EFI-at-runtime-instead-of-config.patch
+Patch0215: 0215-efi-Print-an-error-if-boot-to-firmware-setup-is-not-.patch
+Patch0216: 0216-arm64-Fix-EFI-loader-kernel-image-allocation.patch
+Patch0217: 0217-normal-main-Discover-the-device-to-read-the-config-f.patch
+Patch0218: 0218-powerpc-adjust-setting-of-prefix-for-signed-binary-c.patch
+Patch0219: 0219-powerpc-fix-prefix-signed-grub-special-case-for-Powe.patch
+Patch0220: 0220-Arm-check-for-the-PE-magic-for-the-compiled-arch.patch
+Patch0221: 0221-fs-xfs-Fix-unreadable-filesystem-with-v4-superblock.patch
diff --git a/SOURCES/redhatsecureboot301.cer b/SOURCES/redhatsecureboot301.cer
new file mode 100644
index 0000000..4ff8b79
Binary files /dev/null and b/SOURCES/redhatsecureboot301.cer differ
diff --git a/SOURCES/redhatsecureboot502.cer b/SOURCES/redhatsecureboot502.cer
new file mode 100644
index 0000000..be0b5e2
Binary files /dev/null and b/SOURCES/redhatsecureboot502.cer differ
diff --git a/SOURCES/redhatsecurebootca3.cer b/SOURCES/redhatsecurebootca3.cer
new file mode 100644
index 0000000..b235400
Binary files /dev/null and b/SOURCES/redhatsecurebootca3.cer differ
diff --git a/SOURCES/redhatsecurebootca5.cer b/SOURCES/redhatsecurebootca5.cer
new file mode 100644
index 0000000..dfb0284
Binary files /dev/null and b/SOURCES/redhatsecurebootca5.cer differ
diff --git a/SOURCES/sbat.csv.in b/SOURCES/sbat.csv.in
new file mode 100755
index 0000000..61aa093
--- /dev/null
+++ b/SOURCES/sbat.csv.in
@@ -0,0 +1,3 @@
+sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
+grub,1,Free Software Foundation,grub,@@VERSION@@,https//www.gnu.org/software/grub/
+grub.rhel,1,Red Hat Enterprise Linux,grub2,@@VERSION_RELEASE@@,mail:secalert@redhat.com
diff --git a/SOURCES/strtoull_test.c b/SOURCES/strtoull_test.c
new file mode 100644
index 0000000..7da615f
--- /dev/null
+++ b/SOURCES/strtoull_test.c
@@ -0,0 +1,63 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2016 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/test.h>
+#include <grub/dl.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static void
+strtoull_testcase (const char *input, int base, unsigned long long expected,
+		   int num_digits, grub_err_t error)
+{
+  char *output;
+  unsigned long long value;
+  grub_errno = 0;
+  value = grub_strtoull(input, &output, base);
+  grub_test_assert (grub_errno == error,
+		    "unexpected error. Expected %d, got %d. Input \"%s\"",
+		    error, grub_errno, input);
+  if (grub_errno)
+    {
+      grub_errno = 0;
+      return;
+    }
+  grub_test_assert (input + num_digits == output,
+		    "unexpected number of digits. Expected %d, got %d, input \"%s\"",
+		    num_digits, (int) (output - input), input);
+  grub_test_assert (value == expected,
+		    "unexpected return value. Expected %llu, got %llu, input \"\%s\"",
+		    expected, value, input);
+}
+
+static void
+strtoull_test (void)
+{
+  strtoull_testcase ("9", 0, 9, 1, GRUB_ERR_NONE);
+  strtoull_testcase ("0xaa", 0, 0xaa, 4, GRUB_ERR_NONE);
+  strtoull_testcase ("0xff", 0, 0xff, 4, GRUB_ERR_NONE);
+  strtoull_testcase ("0", 10, 0, 1, GRUB_ERR_NONE);
+  strtoull_testcase ("8", 8, 0, 0, GRUB_ERR_BAD_NUMBER);
+  strtoull_testcase ("38", 8, 3, 1, GRUB_ERR_NONE);
+  strtoull_testcase ("7", 8, 7, 1, GRUB_ERR_NONE);
+  strtoull_testcase ("1]", 16, 1, 1, GRUB_ERR_NONE);
+  strtoull_testcase ("18446744073709551616", 10, 0, 0, GRUB_ERR_OUT_OF_RANGE);
+}
+
+
+GRUB_FUNCTIONAL_TEST (strtoull_test, strtoull_test);
diff --git a/SPECS/grub2.spec b/SPECS/grub2.spec
new file mode 100644
index 0000000..a3301b1
--- /dev/null
+++ b/SPECS/grub2.spec
@@ -0,0 +1,1717 @@
+# This package calls binutils components directly and would need to pass
+# in flags to enable the LTO plugins
+# Disable LTO
+%global _lto_cflags %{nil}
+
+%undefine _hardened_build
+
+%global tarversion 2.06
+%undefine _missing_build_ids_terminate_build
+%global _configure_gnuconfig_hack 0
+
+%global gnulibversion fixes
+
+Name:		grub2
+Epoch:		1
+Version:	2.06
+Release:	6%{?dist}
+Summary:	Bootloader with support for Linux, Multiboot and more
+License:	GPLv3+
+URL:		http://www.gnu.org/software/grub/
+Obsoletes:	grub < 1:0.98
+Source0:	https://ftp.gnu.org/gnu/grub/grub-%{tarversion}.tar.xz
+Source1:	grub.macros
+Source2:	gnulib-%{gnulibversion}.tar.gz
+Source3:	99-grub-mkconfig.install
+Source4:	http://unifoundry.com/pub/unifont/unifont-13.0.06/font-builds/unifont-13.0.06.pcf.gz
+Source5:	theme.tar.bz2
+Source6:	gitignore
+Source7:	bootstrap
+Source8:	bootstrap.conf
+Source9:	strtoull_test.c
+Source10:	20-grub.install
+Source11:	grub.patches
+Source12:	sbat.csv.in
+Source13:	redhatsecurebootca3.cer
+Source14:	redhatsecureboot301.cer
+Source15:	redhatsecurebootca5.cer
+Source16:	redhatsecureboot502.cer
+
+%include %{SOURCE1}
+
+BuildRequires:	gcc efi-srpm-macros
+BuildRequires:	flex bison binutils python3
+BuildRequires:	ncurses-devel xz-devel bzip2-devel
+BuildRequires:	freetype-devel libusb-devel
+BuildRequires:	fuse-devel
+BuildRequires:	rpm-devel rpm-libs
+BuildRequires:	autoconf automake device-mapper-devel
+BuildRequires:	freetype-devel gettext-devel git
+BuildRequires:	texinfo
+BuildRequires:	dejavu-sans-fonts
+BuildRequires:	help2man
+# For %%_userunitdir macro
+BuildRequires:	systemd
+%ifarch %{efi_arch}
+BuildRequires:	pesign >= 0.99-8
+%endif
+%if %{?_with_ccache: 1}%{?!_with_ccache: 0}
+BuildRequires:	ccache
+%endif
+
+ExcludeArch:	s390 s390x
+Obsoletes:	%{name} <= %{evr}
+
+%if 0%{with_legacy_arch}
+Requires:	%{name}-%{legacy_package_arch} = %{evr}
+%else
+Requires:	%{name}-%{package_arch} = %{evr}
+%endif
+
+%global desc \
+The GRand Unified Bootloader (GRUB) is a highly configurable and \
+customizable bootloader with modular architecture.  It supports a rich \
+variety of kernel formats, file systems, computer architectures and \
+hardware devices.\
+%{nil}
+
+# generate with do-rebase
+%include %{SOURCE11}
+
+%description
+%{desc}
+
+%package common
+Summary:	grub2 common layout
+BuildArch:	noarch
+Conflicts:	grubby < 8.40-18
+Requires(post): util-linux
+
+%description common
+This package provides some directories which are required by various grub2
+subpackages.
+
+%package tools
+Summary:	Support tools for GRUB.
+Obsoletes:	%{name}-tools < %{evr}
+Requires:	%{name}-common = %{epoch}:%{version}-%{release}
+Requires:	gettext os-prober which file
+Requires(pre):	dracut
+Requires(post):	dracut
+
+%description tools
+%{desc}
+This subpackage provides tools for support of all platforms.
+
+%ifarch x86_64
+%package tools-efi
+Summary:	Support tools for GRUB.
+Requires:	gettext os-prober which file
+Requires:	%{name}-common = %{epoch}:%{version}-%{release}
+Obsoletes:	%{name}-tools < %{evr}
+
+%description tools-efi
+%{desc}
+This subpackage provides tools for support of EFI platforms.
+%endif
+
+%package tools-minimal
+Summary:	Support tools for GRUB.
+Requires:	gettext
+Requires:	%{name}-common = %{epoch}:%{version}-%{release}
+Obsoletes:	%{name}-tools < %{evr}
+
+%description tools-minimal
+%{desc}
+This subpackage provides tools for support of all platforms.
+
+%package tools-extra
+Summary:	Support tools for GRUB.
+Requires:	gettext os-prober which file
+Requires:	%{name}-tools-minimal = %{epoch}:%{version}-%{release}
+Requires:	%{name}-common = %{epoch}:%{version}-%{release}
+Obsoletes:	%{name}-tools < %{evr}
+
+%description tools-extra
+%{desc}
+This subpackage provides tools for support of all platforms.
+
+%if 0%{with_efi_arch}
+%{expand:%define_efi_variant %%{package_arch} -o}
+%endif
+%if 0%{with_alt_efi_arch}
+%{expand:%define_efi_variant %%{alt_package_arch}}
+%endif
+%if 0%{with_legacy_arch}
+%{expand:%define_legacy_variant %%{legacy_package_arch}}
+%endif
+
+%if 0%{with_emu_arch}
+%package emu
+Summary:	GRUB user-space emulation.
+Requires:	%{name}-tools-minimal = %{epoch}:%{version}-%{release}
+
+%description emu
+%{desc}
+This subpackage provides the GRUB user-space emulation support of all platforms.
+
+%package emu-modules
+Summary:	GRUB user-space emulation modules.
+Requires:	%{name}-tools-minimal = %{epoch}:%{version}-%{release}
+
+%description emu-modules
+%{desc}
+This subpackage provides the GRUB user-space emulation modules.
+%endif
+
+%prep
+%do_common_setup
+%if 0%{with_efi_arch}
+mkdir grub-%{grubefiarch}-%{tarversion}
+grep -A100000 '# stuff "make" creates' .gitignore > grub-%{grubefiarch}-%{tarversion}/.gitignore
+cp %{SOURCE4} grub-%{grubefiarch}-%{tarversion}/unifont.pcf.gz
+sed -e "s,@@VERSION@@,%{version},g" -e "s,@@VERSION_RELEASE@@,%{version}-%{release},g" \
+    %{SOURCE12} > grub-%{grubefiarch}-%{tarversion}/sbat.csv
+git add grub-%{grubefiarch}-%{tarversion}
+%endif
+%if 0%{with_alt_efi_arch}
+mkdir grub-%{grubaltefiarch}-%{tarversion}
+grep -A100000 '# stuff "make" creates' .gitignore > grub-%{grubaltefiarch}-%{tarversion}/.gitignore
+cp %{SOURCE4} grub-%{grubaltefiarch}-%{tarversion}/unifont.pcf.gz
+git add grub-%{grubaltefiarch}-%{tarversion}
+%endif
+%if 0%{with_legacy_arch}
+mkdir grub-%{grublegacyarch}-%{tarversion}
+grep -A100000 '# stuff "make" creates' .gitignore > grub-%{grublegacyarch}-%{tarversion}/.gitignore
+cp %{SOURCE4} grub-%{grublegacyarch}-%{tarversion}/unifont.pcf.gz
+git add grub-%{grublegacyarch}-%{tarversion}
+%endif
+%if 0%{with_emu_arch}
+mkdir grub-emu-%{tarversion}
+grep -A100000 '# stuff "make" creates' .gitignore > grub-emu-%{tarversion}/.gitignore
+cp %{SOURCE4} grub-emu-%{tarversion}/unifont.pcf.gz
+git add grub-emu-%{tarversion}
+%endif
+git commit -m "After making subdirs"
+
+%build
+%if 0%{with_efi_arch}
+%{expand:%do_primary_efi_build %%{grubefiarch} %%{grubefiname} %%{grubeficdname} %%{_target_platform} %%{efi_target_cflags} %%{efi_host_cflags} %{SOURCE13} %{SOURCE14} redhatsecureboot301 %{SOURCE15} %{SOURCE16} redhatsecureboot502}
+%endif
+%if 0%{with_alt_efi_arch}
+%{expand:%do_alt_efi_build %%{grubaltefiarch} %%{grubaltefiname} %%{grubalteficdname} %%{_alt_target_platform} %%{alt_efi_target_cflags} %%{alt_efi_host_cflags} %{SOURCE13} %{SOURCE14} redhatsecureboot301 %{SOURCE15} %{SOURCE16} redhatsecureboot502}
+%endif
+%if 0%{with_legacy_arch}
+%{expand:%do_legacy_build %%{grublegacyarch}}
+%endif
+%if 0%{with_emu_arch}
+%{expand:%do_emu_build}
+%endif
+makeinfo --info --no-split -I docs -o docs/grub-dev.info \
+	docs/grub-dev.texi
+makeinfo --info --no-split -I docs -o docs/grub.info \
+	docs/grub.texi
+makeinfo --html --no-split -I docs -o docs/grub-dev.html \
+	docs/grub-dev.texi
+makeinfo --html --no-split -I docs -o docs/grub.html \
+	docs/grub.texi
+
+%install
+set -e
+rm -fr $RPM_BUILD_ROOT
+
+%do_common_install
+%if 0%{with_efi_arch}
+%{expand:%do_efi_install %%{grubefiarch} %%{grubefiname} %%{grubeficdname}}
+%endif
+%if 0%{with_alt_efi_arch}
+%{expand:%do_alt_efi_install %%{grubaltefiarch} %%{grubaltefiname} %%{grubalteficdname}}
+%endif
+%if 0%{with_legacy_arch}
+%{expand:%do_legacy_install %%{grublegacyarch} %%{alt_grub_target_name} 0%{with_efi_arch}}
+%endif
+%if 0%{with_emu_arch}
+%{expand:%do_emu_install %%{package_arch}}
+%endif
+rm -f $RPM_BUILD_ROOT%{_infodir}/dir
+ln -s %{name}-set-password ${RPM_BUILD_ROOT}/%{_sbindir}/%{name}-setpassword
+echo '.so man8/%{name}-set-password.8' > ${RPM_BUILD_ROOT}/%{_datadir}/man/man8/%{name}-setpassword.8
+%ifnarch x86_64
+rm -vf ${RPM_BUILD_ROOT}/%{_bindir}/%{name}-render-label
+rm -vf ${RPM_BUILD_ROOT}/%{_sbindir}/%{name}-bios-setup
+rm -vf ${RPM_BUILD_ROOT}/%{_sbindir}/%{name}-macbless
+%endif
+%{expand:%%do_install_protected_file %{name}-tools-minimal}
+
+%find_lang grub
+
+# Install kernel-install scripts
+install -d -m 0755 %{buildroot}%{_prefix}/lib/kernel/install.d/
+install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ %{SOURCE10}
+install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ %{SOURCE3}
+install -d -m 0755 %{buildroot}%{_sysconfdir}/kernel/install.d/
+# Install systemd user service to set the boot_success flag
+install -D -m 0755 -t %{buildroot}%{_userunitdir} \
+	docs/grub-boot-success.{timer,service}
+install -d -m 0755 %{buildroot}%{_userunitdir}/timers.target.wants
+ln -s ../grub-boot-success.timer \
+	%{buildroot}%{_userunitdir}/timers.target.wants
+# Install systemd system-update unit to set boot_indeterminate for offline-upd
+install -D -m 0755 -t %{buildroot}%{_unitdir} docs/grub-boot-indeterminate.service
+install -d -m 0755 %{buildroot}%{_unitdir}/system-update.target.wants
+install -d -m 0755 %{buildroot}%{_unitdir}/reboot.target.wants
+ln -s ../grub-boot-indeterminate.service \
+	%{buildroot}%{_unitdir}/system-update.target.wants
+ln -s ../%{name}-systemd-integration.service \
+	%{buildroot}%{_unitdir}/reboot.target.wants
+
+# Don't run debuginfo on all the grub modules and whatnot; it just
+# rejects them, complains, and slows down extraction.
+%global finddebugroot "%{_builddir}/%{?buildsubdir}/debug"
+
+%global dip RPM_BUILD_ROOT=%{finddebugroot} %{__debug_install_post}
+%define __debug_install_post (						\
+	mkdir -p %{finddebugroot}/usr					\
+	mv ${RPM_BUILD_ROOT}/usr/bin %{finddebugroot}/usr/bin		\
+	mv ${RPM_BUILD_ROOT}/usr/sbin %{finddebugroot}/usr/sbin		\
+	%{dip}								\
+	install -m 0755 -d %{buildroot}/usr/lib/ %{buildroot}/usr/src/	\
+	cp -al %{finddebugroot}/usr/lib/debug/				\\\
+		%{buildroot}/usr/lib/debug/				\
+	cp -al %{finddebugroot}/usr/src/debug/				\\\
+		%{buildroot}/usr/src/debug/ )				\
+	mv %{finddebugroot}/usr/bin %{buildroot}/usr/bin		\
+	mv %{finddebugroot}/usr/sbin %{buildroot}/usr/sbin		\
+	%{nil}
+
+%undefine buildsubdir
+
+%pre tools
+if [ -f /boot/%{name}/user.cfg ]; then
+    if grep -q '^GRUB_PASSWORD=' /boot/%{name}/user.cfg ; then
+	sed -i 's/^GRUB_PASSWORD=/GRUB2_PASSWORD=/' /boot/%{name}/user.cfg
+    fi
+elif [ -f %{efi_esp_dir}/user.cfg ]; then
+    if grep -q '^GRUB_PASSWORD=' %{efi_esp_dir}/user.cfg ; then
+	sed -i 's/^GRUB_PASSWORD=/GRUB2_PASSWORD=/' \
+	    %{efi_esp_dir}/user.cfg
+    fi
+elif [ -f /etc/grub.d/01_users ] && \
+	grep -q '^password_pbkdf2 root' /etc/grub.d/01_users ; then
+    if [ -f %{efi_esp_dir}/grub.cfg ]; then
+	# on EFI we don't get permissions on the file, but
+	# the directory is protected.
+	grep '^password_pbkdf2 root' /etc/grub.d/01_users | \
+		sed 's/^password_pbkdf2 root \(.*\)$/GRUB2_PASSWORD=\1/' \
+	    > %{efi_esp_dir}/user.cfg
+    fi
+    if [ -f /boot/%{name}/grub.cfg ]; then
+	install -m 0600 /dev/null /boot/%{name}/user.cfg
+	chmod 0600 /boot/%{name}/user.cfg
+	grep '^password_pbkdf2 root' /etc/grub.d/01_users | \
+		sed 's/^password_pbkdf2 root \(.*\)$/GRUB2_PASSWORD=\1/' \
+	    > /boot/%{name}/user.cfg
+    fi
+fi
+
+%posttrans common
+set -eu
+
+EFI_HOME=%{efi_esp_dir}
+GRUB_HOME=/boot/%{name}
+ESP_PATH=/boot/efi
+
+if ! mountpoint -q ${ESP_PATH}; then
+    exit 0 # no ESP mounted, nothing to do
+fi
+
+if test ! -f ${EFI_HOME}/grub.cfg; then
+    # there's no config in ESP, create one
+    grub2-mkconfig -o ${EFI_HOME}/grub.cfg
+fi
+
+if grep -q "configfile" ${EFI_HOME}/grub.cfg; then
+    exit 0 # already unified, nothing to do
+fi
+
+# create a stub grub2 config in EFI
+BOOT_UUID=$(%{name}-probe --target=fs_uuid ${GRUB_HOME})
+GRUB_DIR=$(%{name}-mkrelpath ${GRUB_HOME})
+
+cat << EOF > ${EFI_HOME}/grub.cfg.stb
+search --no-floppy --fs-uuid --set=dev ${BOOT_UUID}
+set prefix=(\$dev)${GRUB_DIR}
+export \$prefix
+configfile \$prefix/grub.cfg
+EOF
+
+if test -f ${EFI_HOME}/grubenv; then
+    cp -a ${EFI_HOME}/grubenv ${EFI_HOME}/grubenv.rpmsave
+    mv --force ${EFI_HOME}/grubenv ${GRUB_HOME}/grubenv
+fi
+
+cp -a ${EFI_HOME}/grub.cfg ${EFI_HOME}/grub.cfg.rpmsave
+cp -a ${EFI_HOME}/grub.cfg ${GRUB_HOME}/
+mv ${EFI_HOME}/grub.cfg.stb ${EFI_HOME}/grub.cfg
+
+%files common -f grub.lang
+%dir %{_libdir}/grub/
+%dir %{_datarootdir}/grub/
+%attr(0700,root,root) %dir %{_sysconfdir}/grub.d
+%{_prefix}/lib/kernel/install.d/20-grub.install
+%{_prefix}/lib/kernel/install.d/99-grub-mkconfig.install
+%dir %{_datarootdir}/grub
+%exclude %{_datarootdir}/grub/*
+%dir /boot/%{name}
+%dir /boot/%{name}/themes/
+%dir /boot/%{name}/themes/system
+%attr(0700,root,root) %dir /boot/%{name}
+%exclude /boot/%{name}/*
+%dir %attr(0700,root,root) %{efi_esp_dir}
+%exclude %{efi_esp_dir}/*
+%ghost %config(noreplace) %verify(not size mode md5 mtime) /boot/%{name}/grubenv
+%license COPYING
+%doc THANKS
+%doc docs/grub.html
+%doc docs/grub-dev.html
+%doc docs/font_char_metrics.png
+
+%files tools-minimal
+%{_sbindir}/%{name}-get-kernel-settings
+%{_sbindir}/%{name}-probe
+%attr(4755, root, root) %{_sbindir}/%{name}-set-bootflag
+%{_sbindir}/%{name}-set-default
+%{_sbindir}/%{name}-set*password
+%{_bindir}/%{name}-editenv
+%{_bindir}/%{name}-mkpasswd-pbkdf2
+%{_bindir}/%{name}-mount
+%attr(0644,root,root) %config(noreplace) /etc/dnf/protected.d/%{name}-tools-minimal.conf
+
+%{_datadir}/man/man3/%{name}-get-kernel-settings*
+%{_datadir}/man/man8/%{name}-set-default*
+%{_datadir}/man/man8/%{name}-set*password*
+%{_datadir}/man/man1/%{name}-editenv*
+%{_datadir}/man/man1/%{name}-mkpasswd-*
+
+%ifarch x86_64
+%files tools-efi
+%{_bindir}/%{name}-glue-efi
+%{_bindir}/%{name}-render-label
+%{_sbindir}/%{name}-macbless
+%{_datadir}/man/man1/%{name}-glue-efi*
+%{_datadir}/man/man1/%{name}-render-label*
+%{_datadir}/man/man8/%{name}-macbless*
+%endif
+
+%files tools
+%attr(0644,root,root) %ghost %config(noreplace) %{_sysconfdir}/default/grub
+%config %{_sysconfdir}/grub.d/??_*
+%{_sysconfdir}/grub.d/README
+%{_userunitdir}/grub-boot-success.timer
+%{_userunitdir}/grub-boot-success.service
+%{_userunitdir}/timers.target.wants
+%{_unitdir}/grub-boot-indeterminate.service
+%{_unitdir}/system-update.target.wants
+%{_unitdir}/%{name}-systemd-integration.service
+%{_unitdir}/reboot.target.wants
+%{_unitdir}/systemd-logind.service.d
+%{_infodir}/%{name}*
+%{_datarootdir}/grub/*
+%{_sbindir}/%{name}-install
+%exclude %{_datarootdir}/grub/themes
+%exclude %{_datarootdir}/grub/*.h
+%{_datarootdir}/bash-completion/completions/grub
+%{_sbindir}/%{name}-mkconfig
+%{_sbindir}/%{name}-switch-to-blscfg
+%{_sbindir}/%{name}-rpm-sort
+%{_sbindir}/%{name}-reboot
+%{_bindir}/%{name}-file
+%{_bindir}/%{name}-menulst2cfg
+%{_bindir}/%{name}-mkimage
+%{_bindir}/%{name}-mkrelpath
+%{_bindir}/%{name}-script-check
+%{_libexecdir}/%{name}
+%{_datadir}/man/man?/*
+
+# exclude man pages from tools-extra
+%exclude %{_datadir}/man/man8/%{name}-sparc64-setup*
+%exclude %{_datadir}/man/man1/%{name}-fstest*
+%exclude %{_datadir}/man/man1/%{name}-glue-efi*
+%exclude %{_datadir}/man/man1/%{name}-kbdcomp*
+%exclude %{_datadir}/man/man1/%{name}-mkfont*
+%exclude %{_datadir}/man/man1/%{name}-mklayout*
+%exclude %{_datadir}/man/man1/%{name}-mknetdir*
+%exclude %{_datadir}/man/man1/%{name}-mkrescue*
+%exclude %{_datadir}/man/man1/%{name}-mkstandalone*
+%exclude %{_datadir}/man/man1/%{name}-syslinux2cfg*
+
+# exclude man pages from tools-minimal
+%exclude %{_datadir}/man/man3/%{name}-get-kernel-settings*
+%exclude %{_datadir}/man/man8/%{name}-set-default*
+%exclude %{_datadir}/man/man8/%{name}-set*password*
+%exclude %{_datadir}/man/man1/%{name}-editenv*
+%exclude %{_datadir}/man/man1/%{name}-mkpasswd-*
+%exclude %{_datadir}/man/man8/%{name}-macbless*
+%exclude %{_datadir}/man/man1/%{name}-render-label*
+
+%if %{with_legacy_arch}
+%{_sbindir}/%{name}-install
+%ifarch x86_64
+%{_sbindir}/%{name}-bios-setup
+%else
+%exclude %{_sbindir}/%{name}-bios-setup
+%exclude %{_datadir}/man/man8/%{name}-bios-setup*
+%endif
+%ifarch %{sparc}
+%{_sbindir}/%{name}-sparc64-setup
+%else
+%exclude %{_sbindir}/%{name}-sparc64-setup
+%exclude %{_datadir}/man/man8/%{name}-sparc64-setup*
+%endif
+%ifarch %{sparc} ppc ppc64 ppc64le
+%{_sbindir}/%{name}-ofpathname
+%else
+%exclude %{_sbindir}/%{name}-ofpathname
+%exclude %{_datadir}/man/man8/%{name}-ofpathname*
+%endif
+%endif
+
+%files tools-extra
+%{_bindir}/%{name}-fstest
+%{_bindir}/%{name}-kbdcomp
+%{_bindir}/%{name}-mkfont
+%{_bindir}/%{name}-mklayout
+%{_bindir}/%{name}-mknetdir
+%ifnarch %{sparc}
+%{_bindir}/%{name}-mkrescue
+%{_datadir}/man/man1/%{name}-mkrescue*
+%else
+%exclude %{_datadir}/man/man1/%{name}-mkrescue*
+%endif
+%{_bindir}/%{name}-mkstandalone
+%{_bindir}/%{name}-syslinux2cfg
+%{_sysconfdir}/sysconfig/grub
+%{_datadir}/man/man1/%{name}-fstest*
+%{_datadir}/man/man1/%{name}-kbdcomp*
+%{_datadir}/man/man1/%{name}-mkfont*
+%{_datadir}/man/man1/%{name}-mklayout*
+%{_datadir}/man/man1/%{name}-mknetdir*
+%{_datadir}/man/man1/%{name}-mkstandalone*
+%{_datadir}/man/man1/%{name}-syslinux2cfg*
+%exclude %{_bindir}/%{name}-glue-efi
+%exclude %{_sbindir}/%{name}-sparc64-setup
+%exclude %{_sbindir}/%{name}-ofpathname
+%exclude %{_datadir}/man/man1/%{name}-glue-efi*
+%exclude %{_datadir}/man/man8/%{name}-ofpathname*
+%exclude %{_datadir}/man/man8/%{name}-sparc64-setup*
+%exclude %{_datarootdir}/grub/themes/starfield
+
+%if 0%{with_efi_arch}
+%{expand:%define_efi_variant_files %%{package_arch} %%{grubefiname} %%{grubeficdname} %%{grubefiarch} %%{target_cpu_name} %%{grub_target_name}}
+%endif
+%if 0%{with_alt_efi_arch}
+%{expand:%define_efi_variant_files %%{alt_package_arch} %%{grubaltefiname} %%{grubalteficdname} %%{grubaltefiarch} %%{alt_target_cpu_name} %%{alt_grub_target_name}}
+%endif
+%if 0%{with_legacy_arch}
+%{expand:%define_legacy_variant_files %%{legacy_package_arch} %%{grublegacyarch}}
+%endif
+
+%if 0%{with_emu_arch}
+%files emu
+%{_bindir}/%{name}-emu*
+%{_datadir}/man/man1/%{name}-emu*
+
+%files emu-modules
+%{_libdir}/grub/%{emuarch}-emu/*
+%exclude %{_libdir}/grub/%{emuarch}-emu/*.module
+%endif
+
+%changelog
+* Tue Oct 05 2021 Robbie Harwood <rharwood@redhat.com> - 2.06-6
+- Fix booting with XFSv4 partitions
+  Resolves: rhbz#2006993
+
+* Thu Sep 30 2021 Peter Jones <pjones@redhat.com> - 2.06-5
+- Rebuild for correct signatures once more.
+  Resolves: rhbz#1976771
+
+* Thu Sep 30 2021 Peter Jones <pjones@redhat.com> - 2.06-4
+- Rebuild for correct signatures
+  Resolves: rhbz#1976771
+
+* Mon Sep 27 2021 Robbie Harwood <rharwood@redhat.com> - 2.06-3
+- Rebuild for gating + rpminspect
+  Resolves: rhbz#1976771
+
+* Wed Sep 22 2021 Robbie Harwood <rharwood@redhat.com> - 2.06-2
+- Rebuild because our CI infrastructure doesn't work right
+  Resolves: rhbz#1976771
+
+* Tue Aug 31 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.06-1
+- Update to 2.06 final release and ton of fixes
+  Resolves: rhbz#1976771
+
+* Wed Jun 23 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.06~rc1-9
+- Fix kernel cmdline params getting overwritten on ppc64le
+  Resolves: rhbz#1973564
+
+* Mon May 03 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.06~rc1-8
+- Add XFS needsrepair support
+  Resolves: rhbz#1940165
+
+* Mon Apr 26 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.06~rc1-7
+- Find and claim more memory for ieee1275 (dja)
+  Resolves: rhbz#1873860
+
+* Wed Apr 14 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.06~rc1-6
+- Add XFS bigtime support (cmaiolino)
+  Resolves: rhbz#1940165
+
+* Tue Apr 13 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.06~rc1-5
+- Use RHEL distro SBAT data also for CentOS Stream
+  Related: rhbz#1947696
+
+* Fri Apr 09 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.06~rc1-4
+- Update distro SBAT entry to contain information about CentOS Stream
+  Related: rhbz#1947696
+
+* Thu Mar 25 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.06~rc1-3
+- Prevent %%posttrans scriptlet to fail if grubenv isn't present in the ESP
+
+* Wed Mar 24 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.06~rc1-2
+- Fix a couple of merge mistakes made when rebasing to 2.06~rc1
+  Resolves: rhbz#1940524
+
+* Fri Mar 12 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.06~rc1-1
+- Update to 2.06~rc1 to fix a bunch of CVEs
+  Resolves: CVE-2020-14372
+  Resolves: CVE-2020-25632
+  Resolves: CVE-2020-25647
+  Resolves: CVE-2020-27749
+  Resolves: CVE-2020-27779
+  Resolves: CVE-2021-20225
+  Resolves: CVE-2021-20233
+
+* Thu Mar 11 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.04-39
+- Fix config file generation failing due invalid petitboot version value
+  Resolves: rhbz#1921479
+
+* Fri Mar 05 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.04-38
+- Fix keyboards that report IBM PC AT scan codes (rmetrich)
+
+* Thu Feb 25 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.04-37
+- Don't attempt to unify if there is no grub.cfg on EFI (gicmo)
+  Resolves: rhbz#1933085
+
+* Mon Feb 22 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.04-36
+- Switch EFI users to new unified config
+  Resolves: rhbz#1918817
+- Fix ESC key no longer showing the menu
+  Resolves: rhbz#1928595
+
+* Mon Feb 08 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.04-35
+- Remove -fcf-protection compiler flag to allow i386 builds (law)
+  Related: rhbz#1915452
+- Unify GRUB configuration file location across all platforms
+  Related: rhbz#1918817
+- Add 'at_keyboard_fallback_set' var to force the set manually (rmetrich)
+- Add appended signatures support for ppc64le LPAR Secure Boot (daxtens)
+
+* Tue Jan 12 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.04-34
+- at_keyboard: use set 1 when keyboard is in Translate mode (rmetrich)
+
+* Thu Dec 31 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-33
+- Add DNF protected.d fragments for GRUB packages
+  Resolves: rhbz#1874541
+ - Include keylayouts and at_keyboard modules in EFI builds
+ - Add GRUB enhanced debugging features
+ - ieee1275: Avoiding many unecessary open/close
+ - ieee1275: device mapper and fibre channel discovery support
+ - Fix tps-rpmtest failing due /boot/grub2/grubenv attributes mismatch
+
+* Thu Nov 12 2020 Peter Hazenberg <fedoraproject@haas-en-berg.nl> - 2.04-32
+- Fixed some typos in grub-install.8 man page
+
+* Mon Aug 31 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-31
+- Roll over TFTP block counter to prevent timeouts with data packets
+  Resolves: rhbz#1869335
+
+* Fri Aug 21 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-30
+- Set TFTP blocksize to 1428 instead of 2048 to avoid IP fragmentation
+  Resolves: rhbz#1869335
+
+* Fri Aug 21 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-29
+- Fix TFTP timeouts when trying to fetch files larger than 65535 KiB
+  Resolves: rhbz#1869335
+
+* Wed Aug 12 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-28
+- Add support for "systemctl reboot --boot-loader-menu=xx" (hdegoede)
+  Related: rhbz#1857389
+
+* Mon Aug 10 2020 Peter Jones <pjones@redhat.com> - 2.04-27
+- Attempt to enable dual-signing in f33
+- "Minor" bug fixes.  For f33:
+  Resolves: CVE-2020-10713
+  Resolves: CVE-2020-14308
+  Resolves: CVE-2020-14309
+  Resolves: CVE-2020-14310
+  Resolves: CVE-2020-14311
+  Resolves: CVE-2020-15705
+  Resolves: CVE-2020-15706
+  Resolves: CVE-2020-15707
+
+* Tue Jun 30 2020 Jeff Law <law@redhat.com> - 2.04-26
+- Disable LTO
+
+* Thu Jun 18 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-25
+- Only mark GRUB as BLS supported in OSTree systems with a boot partition
+
+* Mon Jun 08 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-24
+- http: Prepend prefix when the HTTP path is relative as done in efi/http
+- Fix build with rpm-4.16 (thierry.vignaud)
+
+* Fri Jun 05 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-23
+- Install GRUB as \EFI\BOOT\BOOTARM.EFI in armv7hl
+
+* Tue May 26 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-22
+- Fix an out of memory error when loading large initrd images
+  Resolves: rhbz#1838633
+
+* Wed May 20 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-21
+- Don't update BLS files that aren't managed by GRUB scripts
+  Resolves: rhbz#1837783
+
+* Mon May 18 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-20
+- Only enable the tpm module for EFI platforms
+
+* Sat May 16 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-19
+- Enable tpm module and make system to boot even if TPM measurements fail
+  Resolves: rhbz#1836433
+
+* Thu May 14 2020 Adam Williamson <awilliam@redhat.com> - 2.04-18
+- 10_linux.in: restore existence check in `get_sorted_bls`
+  Resolves: rhbz#1836020
+
+* Wed May 13 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-17
+- Store cmdline in BLS snippets instead of using a grubenv variable
+
+* Tue May 12 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-16
+- Fix a segfault in grub2-editenv when attempting to shrink a variable
+
+* Thu Apr 30 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-15
+- blscfg: Lookup default_kernelopts variable as fallback for options
+  Related: rhbz#1765297
+- 10_linux.in: fix early exit due error when reading petitboot version
+  Resolves: rhbz#1827397
+
+* Thu Apr 23 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-14
+- efi: Set image base address before jumping to the PE/COFF entry point
+  Resolves: rhbz#1825411
+
+* Thu Apr 16 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-13
+- Make the grub-switch-to-blscfg and 10_linux scripts more robust
+
+* Thu Apr 02 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-12
+- Merge 10_linux_bls logic into 10_linux and avoid issues if blsdir is set
+
+* Thu Mar 26 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-11
+- grub-switch-to-blscfg: Update grub2 binary in ESP for OSTree systems
+  Related: rhbz#1751272
+
+* Tue Mar 17 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-10
+- Fix for entries having an empty initrd command and HTTP boot issues
+  Resolves: rhbz#1806022
+
+* Thu Jan 16 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-9
+- Add riscv64 support to grub.macros and RISC-V build fixes (davidlt)
+- blscfg: Always use the root variable to search for BLS snippets
+- bootstrap.conf: Force autogen.sh to use python3
+
+* Mon Jan 13 2020 Javier Martinez Canillas <javierm@redhat.com> - 2.04-8
+- Make the blscfg module honour the GRUB_SAVEDEFAULT option (fritz)
+  Resolves: rhbz#1704926
+
+* Mon Jan 06 2020 Peter Jones <pjones@redhat.com> - 2.04-7
+- Add zstd to the EFI module list.
+  Related: rhbz#1418336
+
+* Thu Dec 05 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.04-6
+- Various grub2 cleanups (pbrobinson)
+- Another fix for blscfg variable expansion support
+- blscfg: Add support for sorting the plus ('+') higher than base version
+  Resolves: rhbz#1767395
+
+* Wed Nov 27 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.04-5
+- blscfg: add a space char when appending fields for variable expansion
+- grub.d: Fix boot_indeterminate getting set on boot_success=0 boot
+
+* Tue Nov 26 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.04-4
+- grub-set-bootflag: Write new env to tmpfile and then rename (hdegoede)
+  Resolves: CVE-2019-14865
+  Resolves: rhbz#1776580
+
+* Thu Oct 17 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.04-3
+- 20-grub-install: Don't add an id field to generated BLS snippets
+- 99-grub-mkconfig: Disable BLS usage for Xen machines
+  Resolves: rhbz#1703700
+- Don't add a class option to menu entries generated for ppc64le
+  Resolves: rhbz#1758225
+- 10_linux.in: Also use GRUB_CMDLINE_LINUX_DEFAULT to set kernelopts
+- blscfg: Don't hardcode an env var as fallback for the BLS options field
+  Resolves: rhbz#1710483
+
+* Wed Sep 18 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.04-2
+- A couple of RISC-V fixes
+- Remove grub2-tools %%posttrans scriptlet that migrates to a BLS config
+- Add blscfg device tree support
+  Resolves: rhbz#1751307
+
+* Thu Aug 15 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.04-1
+- Update to 2.04
+  Resolves: rhbz#1727279
+
+* Wed Aug 07 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-97
+- Include regexp module in EFI builds
+
+* Thu Aug 01 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-96
+- Manual build for the Fedora 31 mass rebuild to succeed
+
+* Thu Jul 25 2019 Fedora Release Engineering <releng@fedoraproject.org>
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
+
+* Thu Jul 18 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-94
+- 20-grub-install: Restore default SELinux security contexts for BLS files
+  Resolves: rhbz#1726020
+
+* Wed Jul 17 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-93
+- Add btrfs snapshot submenu when BLS configuration is used
+- Move grub2-probe to the grub2-tools-minimal subpackage
+  Resolves: rhbz#1715994
+
+* Tue Jul 16 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-92
+- Cleanup our patchset to reduce the number of patches
+
+* Sat Jul 13 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-91
+- Includes security modules in Grub2 EFI builds (benjamin.doron)
+  Resolves: rhbz#1722938
+- Enable again multiboot and multiboot2 modules on EFI builds
+  Resolves: rhbz#1703872
+
+* Fri Jul 05 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-90
+- Fix failure to request grub.cfg over HTTP
+- Some ARM fixes (pbrobinson)
+- Preserve multi-device workflows (Yclept Nemo)
+
+* Thu Jun 27 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-89
+- Fix --bls-directory option comment in grub2-switch-to-blscfg man page
+  Resolves: rhbz#1714835
+- 10_linux_bls: use '=' to separate --id argument due a Petitboot bug
+- grub-set-bootflag: Print an error if failing to read from grubenv
+  Resolves: rhbz#1702354
+- 10_linux: generate BLS section even if no kernels are found in /boot
+- 10_linux: don't search for OSTree kernels
+
+* Tue Jun 18 2019 Sergio Durigan Junior <sergiodj@redhat.com> - 2.02-88
+- Use '-g' instead of '-g3' when compiling grub2.
+  Resolves: rhbz#1708780
+
+* Tue Jun 11 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-87
+- Rebuild for RPM 4.15
+
+* Mon Jun 10 22:13:19 CET 2019 Igor Gnatenko <ignatenkobrain@fedoraproject.org>
+- Rebuild for RPM 4.15
+
+* Mon Jun 10 15:42:01 CET 2019 Igor Gnatenko <ignatenkobrain@fedoraproject.org>
+- Rebuild for RPM 4.15
+
+* Mon May 20 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-84
+- Don't try to switch to a BLS config if GRUB_ENABLE_BLSCFG is already set
+
+* Wed May 15 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-83
+- Fix error messages wrongly being printed when executing blscfg command
+  Resolves: rhbz#1699761
+- Remove bogus load_env after blscfg command in 10_linux
+
+* Tue May 07 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-82
+- Make blscfg module compatible at least up to the Fedora 19 GRUB core
+  Related: rhbz#1652806
+
+* Fri May 03 2019 Neal Gompa <ngompa13@gmail.com> - 2.02-81
+- Add grub2-mount to grub2-tools-minimal subpackage
+  Resolves: rhbz#1471267
+
+* Fri May 03 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-80
+- Add grub2-emu subpackage
+
+* Fri May 03 2019 Tim Landscheidt <tim@tim-landscheidt.de> - 2.02-79
+- Fix description of grub2-pc
+  Resolves: rhbz#1484298
+
+* Thu Apr 18 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-78
+- Add 10_reset_boot_success to Makefile
+  Related: rhbz#17010
+
+* Thu Apr 18 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-77
+- Never remove boot loader configuration for other boot loaders from the ESP.
+  This would render machines with sd-boot unbootable (zbyszek)
+- Execute grub2-switch-to-blscfg script in %%posttrans instead of %%post
+- grub.d: Split out boot success reset from menu auto hide script (lorbus)
+- HTTP boot: strncmp returns 0 on equal (stephen)
+- Some grub2-emu fixes and make it to not print unnecessary messages
+- Don't assume that boot commands will only return on fail
+
+* Thu Mar 28 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-76
+- 10_linux_bls: don't add --users option to generated menu entries
+  Resolves: rhbz#1693515
+
+* Tue Mar 26 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-75
+- A set of EFI fixes to support arm64 QCom UEFI firmwares (pbrobinson)
+
+* Fri Mar 22 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-74
+- Fix some BLS snippets not being displayed in the GRUB menu
+  Resolves: rhbz#1691232
+- Fix possible undefined behaviour due wrong grub_efi_status_t type (pjones)
+
+* Wed Mar 20 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-73
+- Only set blsdir if /boot/loader/entries is in a btrfs or zfs partition
+  Related: rhbz#1688453
+
+* Mon Mar 11 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-72
+- Avoid grub2-efi package to overwrite existing /boot/grub2/grubenv file
+  Resolves: rhbz#1687323
+- Switch to BLS in tools package %%post scriptlet
+  Resolves: rhbz#1652806
+
+* Wed Feb 27 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-71
+- 20-grub-install: Replace, rather than overwrite, the existing kernel (pjones)
+  Resolves: rhbz#1642402
+- 99-grub-mkconfig: Don't update grubenv generating entries on ppc64le
+  Related: rhbz#1637875
+- blscfg: fallback to default_kernelopts if BLS option field isn't set
+  Related: rhbz#1625124
+- grub-switch-to-blscfg: copy increment.mod for legacy BIOS and ppc64
+  Resolves: rhbz#1652806
+
+* Fri Feb 15 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-70
+- Check if blsdir exists before attempting to get it's real path
+  Resolves: rhbz#1677415
+
+* Wed Feb 13 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-69
+- Don't make grub_strtoull() print an error if no conversion is performed
+  Resolves: rhbz#1674512
+- Set blsdir if the BLS directory path isn't one of the looked up by default
+  Resolves: rhbz#1657240
+
+* Mon Feb 04 2019 Javier Martinez Canillas <javierm@redhat.com> - 2.02-68
+- Don't build the grub2-efi-ia32-* packages on i686 (pjones)
+- Add efi-export-env and efi-load-env commands (pjones)
+- Make it possible to subtract conditions from debug= (pjones)
+- Try to set -fPIE and friends on libgnu.a (pjones)
+- Add more options to blscfg command to make it more flexible
+- Add support for prepend early initrds to the BLS entries
+- Fix grub.cfg-XXX look up when booting over TFTP
+
+* Fri Feb 01 2019 Fedora Release Engineering <releng@fedoraproject.org>
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
+
+* Mon Dec 17 2018 Javier Martinez Canillas <javierm@redhat.com> - 2.02-66
+- Don't exclude /etc/grub.d/01_fallback_counting anymore
+
+* Tue Dec 11 2018 Javier Martinez Canillas <javierm@redhat.com> - 2.02-65
+- BLS files should only be copied by grub-switch-to-blscfg if BLS isn't set
+  Related: rhbz#1638117
+- Fix get_entry_number() wrongly dereferencing the tail pointer
+  Resolves: rhbz#1654936
+- Make grub2-mkconfig to honour GRUB_CMDLINE_LINUX in /etc/default/grub
+  Resolves: rhbz#1637875
+
+* Fri Nov 30 2018 Javier Martinez Canillas <javierm@redhat.com> - 2.02-64
+- Add comments and revert logic changes in 01_fallback_counting
+- Remove quotes when reading ID value from /etc/os-release
+  Related: rhbz#1650706
+- blscfg: expand grub_users before passing to grub_normal_add_menu_entry()
+  Resolves: rhbz#1650706
+- Drop buggy downstream patch "efinet: retransmit if our device is busy"
+  Resolves: rhbz#1649048
+- Make the menu entry users option argument to be optional
+  Related: rhbz#1652434
+- 10_linux_bls: add missing menu entries options
+  Resolves: rhbz#1652434
+- Drop "Be more aggro about actually using the *configured* network device."
+  Resolves: rhbz#1654388
+- Fix menu entry selection based on title
+  Resolves: rhbz#1654936
+
+* Wed Nov 21 2018 Javier Martinez Canillas <javierm@redhat.com> - 2.02-63
+- add 10_linux_bls grub.d snippet to generate menu entries from BLS files
+  Resolves: rhbz#1636013
+- Only set kernelopts in grubenv if it wasn't set before
+  Resolves: rhbz#1636466
+- kernel-install: Remove existing initramfs if it's older than the kernel (pjones)
+  Resolves: rhbz#1638405
+- Update the saved entry correctly after a kernel install (pjones)
+  Resolves: rhbz#1638117
+- blscfg: sort everything with rpm *package* comparison (pjones)
+  Related: rhbz#1638103
+- blscfg: Make 10_linux_bls sort the same way as well
+  Related: rhbz#1638103
+- don't set saved_entry on grub2-mkconfig
+  Resolves: rhbz#1636466
+- Fix menu entry selection based on ID and title (pjones)
+  Resolves: rhbz#1640979
+- Don't unconditionally set default entry when installing debug kernels
+  Resolves: rhbz#1636346
+- Remove installkernel-bls script
+  Related: rhbz#1647721
+
+* Thu Oct 04 2018 Peter Jones <pjones@redhat.com> - 2.02-62
+- Exclude /etc/grub.d/01_fallback_counting until we work through some design
+  questions.
+  Resolves: rhbz#1614637
+
+* Wed Oct 03 2018 Peter Jones <pjones@redhat.com> - 2.02-61
+- Fix the fallback counting script even harder. Apparently, this wasn't
+  tested well enough.
+  Resolves: rhbz#1614637
+
+* Tue Oct 02 2018 Peter Jones <pjones@redhat.com> - 2.02-60
+- Fix grub.cfg boot counting snippet generation (lorbus)
+  Resolves: rhbz#1614637
+- Fix spurrious allocation error reporting on EFI boot
+  Resolves: rhbz#1635319
+- Stop doing TPM on BIOS *again*.  It just doesn't work.
+  Related: rhbz#1579835
+- Make blscfg module loadable on older grub2 i386-pc and powerpc-ieee1275
+  builds
+- Fix execstack cropping up in grub2-tools
+- Ban stack trampolines with compiler flags.
+
+* Tue Sep 25 2018 Hans de Goede <hdegoede@redhat.com> - 2.02-59
+- Stop using pkexec for grub2-set-bootflag, it does not work under gdm
+  instead make it suid root (it was written with this in mind)
+
+* Tue Sep 25 2018 Peter Jones <pjones@redhat.com>
+- Use bounce buffers for addresses above 4GB
+- Allow initramfs, cmdline, and params >4GB, but not kernel
+
+* Wed Sep 12 2018 Peter Jones <pjones@redhat.com> - 2.02-58
+- Add 2 conditions to boot-success timer and service:
+  - Don't run it for system users
+  Resolves: rhbz#1592201
+  - Don't run it when pkexec isn't available
+  Resolves: rhbz#1619445
+- Use -Wsign-compare -Wconversion -Wextra in the build.
+
+* Tue Sep 11 2018 Peter Jones <pjones@redhat.com> - 2.02-57
+- Limit grub_malloc() on x86_64 to < 31bit addresses, as some devices seem to
+  have a colossally broken storage controller (or UEFI driver) that can't do
+  DMA to higher memory addresses, but fails silently.
+  Resolves: rhbz#1626844 (possibly really resolving it this time.)
+- Also integrate Hans's attempt to fix the related error from -54, but do it
+  the other way around: try the low addresses first and *then* the high one if
+  the allocation fails.  This way we'll get low regions by default, and if
+  kernel/initramfs don't fit anywhere, it'll try the higher addresses.
+  Related: rhbz#1624532
+- Coalesce all the intermediate debugging junk from -54/-55/-56.
+
+* Tue Sep 11 2018 Peter Jones <pjones@redhat.com> - 2.02-56
+- Don't mangle fw_path even harder.
+  Resolves: rhbz#1626844
+- Fix reboot being missing on some platforms, and make it alias to
+  "reset" as well.
+- More dprintf().
+
+* Mon Sep 10 2018 Peter Jones <pjones@redhat.com> - 2.02-55
+- Fix UEFI memory problem in a different way.
+  Related: rhbz#1624532
+- Don't mangle fw_path with a / unless we're on http
+  Resolves: rhbz#1626844
+
+* Fri Sep 07 2018 Kevin Fenzi <kevin@scrye.com> - 2.02-54
+- Add patch from https://github.com/rhboot/grub2/pull/30 to fix uefi booting
+  Resolves: rhbz#1624532
+
+* Thu Aug 30 2018 Peter Jones <pjones@redhat.com> - 2.02-53
+- Fix AArch64 machines with no RAM latched lower than 1GB
+  Resolves: rhbz#1615969
+- Set http_path and http_url when HTTP booting
+- Hopefully slightly better error reporting in some cases
+- Better allocation of kernel+initramfs on x86_64 and aarch64
+  Resolves: rhbz#1572126
+
+* Mon Aug 20 2018 Peter Jones <pjones@redhat.com> - 2.02-52
+- Update conflicts on grubby not to care about %%{?dist}
+
+* Sun Aug 19 2018 Peter Jones <pjones@redhat.com> - 2.02-51
+- Make it quieter.
+
+* Thu Aug 16 2018 Peter Jones <pjones@redhat.com> - 2.02-50
+- Fix arm32 off-by-one error on reading the PE header.
+
+* Tue Aug 14 2018 Peter Jones <pjones@redhat.com> - 2.02-50
+- Fix typo in /etc/grub.d/01_fallback_counting
+  Resolves: rhbz#1614637
+
+* Fri Aug 10 2018 Javier Martinez Canillas <javierm@redhat.com> - 2.02-50
+- Add an installkernel script for BLS configurations
+
+* Tue Aug 07 2018 Peter Jones <pjones@redhat.com> - 2.02-49
+- Temporarily make -cdboot perms 0700 again.
+
+* Fri Aug 03 2018 Peter Jones <pjones@redhat.com> - 2.02-48
+- Kill .note.gnu.property with fire.
+  Resolves: rhbz#1612339
+
+* Thu Aug 02 2018 Peter Jones <pjones@redhat.com> - 2.02-47
+- Enable armv7 EFI builds.  This was way harder than I expected.
+
+* Tue Jul 17 2018 Peter Jones <pjones@redhat.com> - 2.02-46
+- Fix some minor BLS issues
+- Rework the FDT module linking to make aarch64 build and boot right
+
+* Mon Jul 16 2018 pjones <pjones@redhat.com> - 2.02-45
+- Rework SB patches and 10_linux.in changes even harder.
+  Resolves: rhbz#1601578
+
+* Mon Jul 16 2018 Hans de Goede <hdegoede@redhat.com> - 2.02-44
+- Make the user session automatically set the boot_success grubenv flag
+- Make offline-updates increment the boot_indeterminate grubenv variable
+
+* Mon Jul 16 2018 pjones <pjones@redhat.com> - 2.02-43
+- Rework SB patches and 10_linux.in changes
+
+* Fri Jul 13 2018 Peter Jones <pjones@redhat.com> - 2.02-42
+- Revert broken moduledir fix *again*.
+
+* Thu Jul 12 2018 Peter Jones <pjones@redhat.com> - 2.02-41
+- Fix our linuxefi/linux command reunion.
+
+* Wed Jul 11 2018 Peter Jones <pjones@redhat.com> - 2.02-40
+- Port several fixes from the F28 tree and a WIP tree.
+
+* Mon Jul 09 2018 pjones <pjones@redhat.com> - 2.02-39
+- Fix my fix of grub2-switch-to-blscfg (javierm and pjones)
+
+* Mon Jul 02 2018 Javier Martinez Canillas <javierm@redhat.com> - 2.02-38
+- Use BLS fragment filename as menu entry id and for sort criterion
+
+* Tue Jun 26 2018 Javier Martinez Canillas <javierm@redhat.com>
+- Cleanups and fixes for grub2-switch-to-blscfg (pjones)
+- Use /boot/loader/entries as BLS dir also on EFI (javierm)
+
+* Tue Jun 19 2018 Peter Jones <pjones@redhat.com> - 2.02-37
+- Fix some TPM errors on 32-bit (hdegoede)
+- More fixups to avoid compiler changes (pjones)
+- Put lsmmap into the EFI builds (pjones)
+  Related: rhbz#1572126
+- Make 20-grub.install to exit if there is no machine ID set (javierm)
+  Resolves: rhbz#1576573
+- More fixes for BLS (javierm)
+  Resolves: rhbz#1588184
+
+* Wed May 16 2018 Peter Jones <pjones@redhat.com> - 2.02-37.fc29
+- Fixups to work with gcc 8
+- Experimental https boot support on UEFI
+- XFS fixes for sparse inode support
+  Resolves: rhbz#1575797
+
+* Thu May 10 2018 Javier Martinez Canillas <javierm@redhat.com> - 2.02-36
+- Use version field to sort BLS entries if id field isn't defined
+- Add version field to BLS fragments generated by 20-grub.install
+
+* Tue Apr 24 2018 Peter Jones <pjones@redhat.com> - 2.02-35
+- A couple of fixes needed by Fedora Atomic - javierm
+
+* Mon Apr 23 2018 Peter Jones <pjones@redhat.com> - 2.02-34
+- Put the os-prober dep back in - we need to change test plans and criteria
+  before it can go.
+  Resolves: rhbz#1569411
+
+* Wed Apr 11 2018 Peter Jones <pjones@redhat.com> - 2.02-33
+- Work around some issues with older automake found in CentOS.
+- Make multiple initramfs images work in BLS.
+
+* Wed Apr 11 2018 Javier Martinez Canillas <javierm@redhat.com> - 2.02-32
+- Make 20-grub.install to generate debug BLS when MAKEDEBUG is set.
+
+* Fri Apr 06 2018 Peter Jones <pjones@redhat.com> - 2.02-31
+- Pull in some TPM fixes I missed.
+
+* Fri Apr 06 2018 Peter Jones <pjones@redhat.com> - 2.02-30
+- Enable TPM measurements
+- Set the default boot entry to the first entry when we're using BLS.
+
+* Tue Apr 03 2018 Peter Jones <pjones@redhat.com> - 2.02-29
+- Fix for BLS paths on BIOS / non-UEFI (javierm)
+
+* Fri Mar 16 2018 Peter Jones <pjones@redhat.com> - 2.02-28
+- Install kernel-install scripts. (javierm)
+- Add grub2-switch-to-blscfg
+
+* Tue Mar 06 2018 Peter Jones <pjones@redhat.com> - 2.02-27
+- Build the blscfg module in on EFI builds.
+
+* Wed Feb 28 2018 Peter Jones <pjones@redhat.com> - 2.02-26
+- Try to fix things for new compiler madness.
+  I really don't know why gcc decided __attribute__((packed)) on a "typedef
+  struct" should imply __attribute__((align (1))) and that it should have a
+  warning that it does so.  The obvious behavior would be to keep the alignment
+  of the first element unless it's used in another object or type that /also/
+  hask the packed attribute.  Why should it change the default alignment at
+  all?
+- Merge in the BLS patches Javier and I wrote.
+- Attempt to fix pmtimer initialization failures to not be super duper slow.
+
+* Fri Feb 09 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org>
+- Escape macros in %%changelog
+
+* Tue Jan 23 2018 Peter Jones <pjones@redhat.com> - 2.02-24
+- Fix a merge error from 2.02-21 that affected kernel loading on Aarch64.
+  Related: rhbz#1519311
+  Related: rhbz#1506704
+  Related: rhbz#1502312
+
+* Fri Jan 19 2018 Peter Jones <pjones@redhat.com> - 2.02-23
+- Only nerf annobin, not -fstack-crash-protection.
+- Fix a conflict on /boot/efi directory permissions between -cdboot and the
+  normal bootloader.
+
+* Thu Jan 18 2018 Peter Jones <pjones@redhat.com> - 2.02-22
+- Nerf some gcc 7.2.1-6 'features' that cause grub to crash on start.
+
+* Thu Jan 18 2018 Peter Jones <pjones@redhat.com> - 2.02-21
+- Fix grub2-efi-modules provides/obsoletes generation
+  Resolves: rhbz#1506704
+- *Also* build grub-efi-ia32{,-*,!-modules} packages for i686 builds
+  Resolves: rhbz#1502312
+- Make everything under /boot/efi be mode 0700, since that's what FAT will
+  show anyway.
+
+* Wed Jan 17 2018 Peter Jones <pjones@redhat.com> - 2.02-20
+- Update to newer upstream for F28
+- Pull in patches for Apollo Lake hardware
+  Resolves: rhbz#1519311
+
+* Tue Oct 24 2017 Peter Jones <pjones@redhat.com> - 2.02-19
+- Handle xen module loading (somewhat) better
+  Resolves: rhbz#1486002
+
+* Wed Sep 20 2017 Peter Jones <pjones@redhat.com> - 2.02-18
+- Make grub2-efi-aa64 provide grub2
+  Resolves: rhbz#1491045
+
+* Mon Sep 11 2017 Dennis Gilmore <dennis@ausil.us> - 2.02-17
+- bump for Obsoletes again
+
+* Wed Sep 06 2017 Peter Jones <pjones@redhat.com> - 2.02-16
+- Fix Obsoletes on grub2-pc
+
+* Wed Aug 30 2017 Petr Šabata <contyk@redhat.com> - 2.02-15
+- Limit the pattern matching in do_alt_efi_install to files to
+  unbreak module builds
+
+* Fri Aug 25 2017 Peter Jones <pjones@redhat.com> - 2.02-14
+- Revert the /usr/lib/.build-id/ change:
+  https://fedoraproject.org/wiki/Changes/ParallelInstallableDebuginfo
+  says (without any particularly convincing reasoning):
+    The main build-id file should not be in the debuginfo file, but in the
+    main package (this was always a problem since the package and debuginfo
+    package installed might not match). If we want to make usr/lib/debug/ a
+    network resource then we will need to move the symlink to another
+    location (maybe /usr/lib/.build-id).
+  So do it that way.  Of course it doesn't matter, because exclude gets
+  ignored due to implementation details.
+
+* Fri Aug 25 2017 Peter Jones <pjones@redhat.com> - 2.02-13
+- Add some unconditional Provides:
+  grub2-efi on grub2-efi-${arch}
+  grub2-efi-cdboot on grub2-efi-${arch}-cdboot
+  grub2 on all grub2-${arch} pacakges
+- Something is somehow adding /usr/lib/.build-id/... to all the -tools
+  subpackages, so exclude all that.
+
+* Thu Aug 24 2017 Peter Jones <pjones@redhat.com> - 2.02-12
+- Fix arm kernel command line allocation
+  Resolves: rhbz#1484609
+- Get rid of the temporary extra efi packages hack.
+
+* Wed Aug 23 2017 Peter Jones <pjones@redhat.com> - 2.02-11
+- Put grub2-mkimage in -tools, not -tools-extra.
+- Fix i686 building
+- Fix ppc HFS+ usage due to /boot/efi's presence.
+
+* Fri Aug 18 2017 Peter Jones <pjones@redhat.com> - 2.02-10
+- Add the .img files into grub2-pc-modules (and all legacy variants)
+
+* Wed Aug 16 2017 Peter Jones <pjones@redhat.com> - 2.02-9
+- Re-work for ia32-efi.
+
+* Wed Aug 16 2017 pjones <pjones@redhat.com> - 2.02-8
+- Rebased to newer upstream for fedora-27
+
+* Tue Aug 15 2017 Peter Jones <pjones@redhat.com> - 2.02-7
+- Rebuild again with new fixed rpm. (bug #1480407)
+
+* Fri Aug 11 2017 Kevin Fenzi <kevin@scrye.com> - 2.02-6
+- Rebuild again with new fixed rpm. (bug #1480407)
+
+* Thu Aug 10 2017 Kevin Fenzi <kevin@scrye.com> - 2.02-5
+- Rebuild for rpm soname bump again.
+
+* Thu Aug 10 2017 Igor Gnatenko <ignatenko@redhat.com> - 2.02-4
+- Rebuilt for RPM soname bump
+
+* Thu Aug 03 2017 Peter Jones <pjones@redhat.com> - 2.02-3
+- Rebuild so it gets SB signed correctly.
+  Related: rhbz#1335533
+- Enable lsefi
+
+* Mon Jul 24 2017 Michael Cronenworth <mike@cchtml.com> - 2.02-2
+- Fix symlink to work on both EFI and BIOS machines
+  Resolves: rhbz#1335533
+
+* Mon Jul 10 2017 Peter Jones <pjones@redhat.com> - 2.02-1
+- Rebased to newer upstream for fedora-27
+
+* Wed Feb 01 2017 Stephen Gallagher <sgallagh@redhat.com> - 2.02-0.39
+- Add missing %%license macro
+- Fix deps that should have moved to -tools but didn't.
+
+* Thu Dec 08 2016 Peter Jones <pjones@redhat.com> - 2.02-0.38
+- Fix regexp in power compile flags, and synchronize release number with
+  other branches.
+
+* Fri Dec 02 2016 pjones <pjones@redhat.com> - 1:2.02-0.37
+- Rebased to newer upstream for fedora-26
+
+* Thu Dec 01 2016 Peter Jones <pjones@redhat.com> - 2.02-0.36
+- Update version to .36 because I already built an f25 one named 0.35
+
+* Thu Dec 01 2016 pjones <pjones@redhat.com> - 1:2.02-0.35
+- Rebased to newer upstream for fedora-26
+
+* Thu Dec 01 2016 Peter Jones <pjones@redhat.com> - 2.02-0.34
+- Fix power6 makefile bits for newer autoconf defaults.
+- efi/chainloader: fix wrong sanity check in relocate_coff() (Laszlo Ersek)
+  Resolves: rhbz#1347291
+
+* Thu Aug 25 2016 Peter Jones <pjones@redhat.com> - 2.02-0.34
+- Update to be newer than f24's branch.
+- Add grub2-get-kernel-settings
+  Related: rhbz#1226325
+
+* Thu Apr 07 2016 pjones <pjones@redhat.com> - 1:2.02-0.30
+- Revert 27e66193, which was replaced by upstream's 49426e9fd
+  Resolves: rhbz#1251600
+
+* Thu Apr 07 2016 Peter Jones <pjones@redhat.com> - 2.02-0.29
+- Fix ppc64 build failure and rebase to newer f24 code.
+
+* Tue Apr 05 2016 pjones <pjones@redhat.com> - 1:2.02-0.27
+- Pull TPM updates from mjg59.
+  Resolves: rhbz#1318067
+
+* Tue Mar 08 2016 pjones <pjones@redhat.com> - 1:2.02-0.27
+- Fix aarch64 build problem.
+
+* Fri Mar 04 2016 Peter Jones <pjones@redhat.com> - 2.02-0.26
+- Rebased to newer upstream (grub-2.02-beta3) for fedora-24
+
+* Thu Dec 10 2015 Peter Jones <pjones@redhat.com> - 2.02-0.25
+- Fix security issue when reading username and password
+  Related: CVE-2015-8370
+- Do a better job of handling GRUB2_PASSWORD
+  Related: rhbz#1284370
+
+* Fri Nov 20 2015 Peter Jones <pjones@redhat.com> - 2.02-0.24
+- Rebuild without multiboot* modules in the EFI image.
+  Related: rhbz#1264103
+
+* Sat Sep 05 2015 Kalev Lember <klember@redhat.com> - 2.02-0.23
+- Rebuilt for librpm soname bump
+
+* Wed Aug 05 2015 Peter Jones <pjones@redhat.com> - 2.02-0.21
+- Back out one of the debuginfo generation patches; it doesn't work right on
+  aarch64 yet.
+  Resolves: rhbz#1250197
+
+* Mon Aug 03 2015 Peter Jones <pjones@redhat.com> - 2.02-0.20
+- The previous fix was completely not right, so fix it a different way.
+  Resolves: rhbz#1249668
+
+* Fri Jul 31 2015 Peter Jones <pjones@redhat.com> - 2.02-0.19
+- Fix grub2-mkconfig's sort to put kernels in the right order.
+  Related: rhbz#1124074
+
+* Thu Jul 30 2015 Peter Jones <pjones@redhat.com> - 2.02-0.18
+- Fix a build failure on aarch64
+
+* Wed Jul 22 2015 Peter Jones <pjones@redhat.com> - 2.02-0.17
+- Don't build hardened (fixes FTBFS) (pbrobinson)
+- Reconcile with the current upstream
+- Fixes for gcc 5
+
+* Tue Apr 28 2015 Peter Jones <pjones@redhat.com> - 2.02-0.16
+- Make grub2-mkconfig produce the kernel titles we actually want.
+  Resolves: rhbz#1215839
+
+* Sat Feb 21 2015 Till Maas <opensource@till.name>
+- Rebuilt for Fedora 23 Change
+  https://fedoraproject.org/wiki/Changes/Harden_all_packages_with_position-independent_code
+
+* Mon Jan 05 2015 Peter Jones <pjones@redhat.com> - 2.02-0.15
+- Bump release to rebuild with Ralf Corsépius's fixes.
+
+* Sun Jan 04 2015 Ralf Corsépius <corsepiu@fedoraproject.org> - 2.02-0.14
+- Move grub2.info/grub2-dev.info install-info scriptlets into *-tools package.
+- Use sub-shell in %%__debug_install_post (RHBZ#1168732).
+- Cleanup grub2-starfield-theme packaging.
+
+* Thu Dec 04 2014 Peter Jones <pjones@redhat.com> - 2.02-0.13
+- Update minilzo to 2.08 for CVE-2014-4607
+  Resolves: rhbz#1131793
+
+* Thu Nov 13 2014 Peter Jones <pjones@redhat.com> - 2.02-0.12
+- Make backtrace and usb conditional on !arm
+- Make sure gcdaa64.efi is packaged.
+  Resolves: rhbz#1163481
+
+* Fri Nov 07 2014 Peter Jones <pjones@redhat.com> - 2.02-0.11
+- fix a copy-paste error in patch 0154.
+  Resolves: rhbz#964828
+
+* Mon Oct 27 2014 Peter Jones <pjones@redhat.com> - 2.02-0.10
+- Try to emit linux16/initrd16 and linuxefi/initrdefi when appropriate
+  in 30_os-prober.
+  Resolves: rhbz#1108296
+- If $fw_path doesn't work to find the config file, try $prefix as well
+  Resolves: rhbz#1148652
+
+* Mon Sep 29 2014 Peter Jones <pjones@redhat.com> - 2.02-0.9
+- Clean up the build a bit to make it faster
+- Make grubenv work right on UEFI machines
+  Related: rhbz#1119943
+- Sort debug and rescue kernels later than normal ones
+  Related: rhbz#1065360
+- Allow "fallback" to include entries by title as well as number.
+  Related: rhbz#1026084
+- Fix a segfault on aarch64.
+- Load arm with SB enabled if available.
+- Add some serial port options to GRUB_MODULES.
+
+* Tue Aug 19 2014 Peter Jones <pjones@redhat.com> - 2.02-0.8
+- Add ppc64le support.
+  Resolves: rhbz#1125540
+
+* Thu Jul 24 2014 Peter Jones <pjones@redhat.com> - 2.02-0.7
+- Enabled syslinuxcfg module.
+
+* Wed Jul 02 2014 Peter Jones <pjones@redhat.com> - 2.02-0.6
+- Re-merge RHEL 7 changes and ARM works in progress.
+
+* Mon Jun 30 2014 Peter Jones <pjones@redhat.com> - 2.02-0.5
+- Avoid munging raw spaces when we're escaping command line arguments.
+  Resolves: rhbz#923374
+
+* Tue Jun 24 2014 Peter Jones <pjones@redhat.com> - 2.02-0.4
+- Update to latest upstream.
+
+* Thu Mar 13 2014 Peter Jones <pjones@redhat.com> - 2.02-0.3
+- Merge in RHEL 7 changes and ARM works in progress.
+
+* Mon Jan 06 2014 Peter Jones <pjones@redhat.com> - 2.02-0.2
+- Update to grub-2.02~beta2
+
+* Sat Aug 10 2013 Peter Jones <pjones@redhat.com> - 2.00-25
+- Last build failed because of a hardware error on the builder.
+
+* Mon Aug 05 2013 Peter Jones <pjones@redhat.com> - 2.00-24
+- Fix compiler flags to deal with -fstack-protector-strong
+
+* Sat Aug 03 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1:2.00-24
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
+
+* Tue Jul 02 2013 Dennis Gilmore <dennis@ausil.us> - 2.00-23
+- add epoch to obsoletes
+
+* Fri Jun 21 2013 Peter Jones <pjones@redhat.com> - 2.00-22
+- Fix linewrapping in edit menu.
+  Resolves: rhbz #976643
+
+* Thu Jun 20 2013 Peter Jones <pjones@redhat.com> - 2.00-21
+- Fix obsoletes to pull in -starfield-theme subpackage when it should.
+
+* Fri Jun 14 2013 Peter Jones <pjones@redhat.com> - 2.00-20
+- Put the theme entirely ento the subpackage where it belongs (#974667)
+
+* Wed Jun 12 2013 Peter Jones <pjones@redhat.com> - 2.00-19
+- Rebase to upstream snapshot.
+- Fix PPC build error (#967862)
+- Fix crash on net_bootp command (#960624)
+- Reset colors on ppc when appropriate (#908519)
+- Left align "Loading..." messages (#908492)
+- Fix probing of SAS disks on PPC (#953954)
+- Add support for UEFI OSes returned by os-prober
+- Disable "video" mode on PPC for now (#973205)
+- Make grub fit better into the boot sequence, visually (#966719)
+
+* Fri May 10 2013 Matthias Clasen <mclasen@redhat.com> - 2.00-18
+- Move the starfield theme to a subpackage (#962004)
+- Don't allow SSE or MMX on UEFI builds (#949761)
+
+* Wed Apr 24 2013 Peter Jones <pjones@redhat.com> - 2.00-17.pj0
+- Rebase to upstream snapshot.
+
+* Thu Apr 04 2013 Peter Jones <pjones@redhat.com> - 2.00-17
+- Fix booting from drives with 4k sectors on UEFI.
+- Move bash completion to new location (#922997)
+- Include lvm support for /boot (#906203)
+
+* Thu Feb 14 2013 Peter Jones <pjones@redhat.com> - 2.00-16
+- Allow the user to disable submenu generation
+- (partially) support BLS-style configuration stanzas.
+
+* Tue Feb 12 2013 Peter Jones <pjones@redhat.com> - 2.00-15.pj0
+- Add various config file related changes.
+
+* Thu Dec 20 2012 Dennis Gilmore <dennis@ausil.us> - 2.00-15
+- bump nvr
+
+* Mon Dec 17 2012 Karsten Hopp <karsten@redhat.com> 2.00-14
+- add bootpath device to the device list (pfsmorigo, #886685)
+
+* Tue Nov 27 2012 Peter Jones <pjones@redhat.com> - 2.00-13
+- Add vlan tag support (pfsmorigo, #871563)
+- Follow symlinks during PReP installation in grub2-install (pfsmorigo, #874234)
+- Improve search paths for config files on network boot (pfsmorigo, #873406)
+
+* Tue Oct 23 2012 Peter Jones <pjones@redhat.com> - 2.00-12
+- Don't load modules when grub transitions to "normal" mode on UEFI.
+
+* Mon Oct 22 2012 Peter Jones <pjones@redhat.com> - 2.00-11
+- Rebuild with newer pesign so we'll get signed with the final signing keys.
+
+* Thu Oct 18 2012 Peter Jones <pjones@redhat.com> - 2.00-10
+- Various PPC fixes.
+- Fix crash fetching from http (gustavold, #860834)
+- Issue separate dns queries for ipv4 and ipv6 (gustavold, #860829)
+- Support IBM CAS reboot (pfsmorigo, #859223)
+- Include all modules in the core image on ppc (pfsmorigo, #866559)
+
+* Mon Oct 01 2012 Peter Jones <pjones@redhat.com> - 1:2.00-9
+- Work around bug with using "\x20" in linux command line.
+  Related: rhbz#855849
+
+* Thu Sep 20 2012 Peter Jones <pjones@redhat.com> - 2.00-8
+- Don't error on insmod on UEFI/SB, but also don't do any insmodding.
+- Increase device path size for ieee1275
+  Resolves: rhbz#857936
+- Make network booting work on ieee1275 machines.
+  Resolves: rhbz#857936
+
+* Wed Sep 05 2012 Matthew Garrett <mjg@redhat.com> - 2.00-7
+- Add Apple partition map support for EFI
+
+* Thu Aug 23 2012 David Cantrell <dcantrell@redhat.com> - 2.00-6
+- Only require pesign on EFI architectures (#851215)
+
+* Tue Aug 14 2012 Peter Jones <pjones@redhat.com> - 2.00-5
+- Work around AHCI firmware bug in efidisk driver.
+- Move to newer pesign macros
+- Don't allow insmod if we're in secure-boot mode.
+
+* Wed Aug 08 2012 Peter Jones <pjones@redhat.com>
+- Split module lists for UEFI boot vs UEFI cd images.
+- Add raid modules for UEFI image (related: #750794)
+- Include a prelink whitelist for binaries that need execstack (#839813)
+- Include fix efi memory map fix from upstream (#839363)
+
+* Wed Aug 08 2012 Peter Jones <pjones@redhat.com> - 2.00-4
+- Correct grub-mkimage invocation to use efidir RPM macro (jwb)
+- Sign with test keys on UEFI systems.
+- PPC - Handle device paths with commas correctly.
+  Related: rhbz#828740
+
+* Wed Jul 25 2012 Peter Jones <pjones@redhat.com> - 2.00-3
+- Add some more code to support Secure Boot, and temporarily disable
+  some other bits that don't work well enough yet.
+  Resolves: rhbz#836695
+
+* Wed Jul 11 2012 Matthew Garrett <mjg@redhat.com> - 2.00-2
+- Set a prefix for the image - needed for installer work
+- Provide the font in the EFI directory for the same reason
+
+* Thu Jun 28 2012 Peter Jones <pjones@redhat.com> - 2.00-1
+- Rebase to grub-2.00 release.
+
+* Mon Jun 18 2012 Peter Jones <pjones@redhat.com> - 2.0-0.37.beta6
+- Fix double-free in grub-probe.
+
+* Wed Jun 06 2012 Peter Jones <pjones@redhat.com> - 2.0-0.36.beta6
+- Build with patch19 applied.
+
+* Wed Jun 06 2012 Peter Jones <pjones@redhat.com> - 2.0-0.35.beta6
+- More ppc fixes.
+
+* Wed Jun 06 2012 Peter Jones <pjones@redhat.com> - 2.0-0.34.beta6
+- Add IBM PPC fixes.
+
+* Mon Jun 04 2012 Peter Jones <pjones@redhat.com> - 2.0-0.33.beta6
+- Update to beta6.
+- Various fixes from mads.
+
+* Fri May 25 2012 Peter Jones <pjones@redhat.com> - 2.0-0.32.beta5
+- Revert builddep change for crt1.o; it breaks ppc build.
+
+* Fri May 25 2012 Peter Jones <pjones@redhat.com> - 2.0-0.31.beta5
+- Add fwsetup command (pjones)
+- More ppc fixes (IBM)
+
+* Tue May 22 2012 Peter Jones <pjones@redhat.com> - 2.0-0.30.beta5
+- Fix the /other/ grub2-tools require to include epoch.
+
+* Mon May 21 2012 Peter Jones <pjones@redhat.com> - 2.0-0.29.beta5
+- Get rid of efi_uga and efi_gop, favoring all_video instead.
+
+* Mon May 21 2012 Peter Jones <pjones@redhat.com> - 2.0-0.28.beta5
+- Name grub.efi something that's arch-appropriate (kiilerix, pjones)
+- use EFI/$SOMETHING_DISTRO_BASED/ not always EFI/redhat/grub2-efi/ .
+- move common stuff to -tools (kiilerix)
+- spec file cleanups (kiilerix)
+
+* Mon May 14 2012 Peter Jones <pjones@redhat.com> - 2.0-0.27.beta5
+- Fix module trampolining on ppc (benh)
+
+* Thu May 10 2012 Peter Jones <pjones@redhat.com> - 2.0-0.27.beta5
+- Fix license of theme (mizmo)
+  Resolves: rhbz#820713
+- Fix some PPC bootloader detection IBM problem
+  Resolves: rhbz#820722
+
+* Thu May 10 2012 Peter Jones <pjones@redhat.com> - 2.0-0.26.beta5
+- Update to beta5.
+- Update how efi building works (kiilerix)
+- Fix theme support to bring in fonts correctly (kiilerix, pjones)
+
+* Wed May 09 2012 Peter Jones <pjones@redhat.com> - 2.0-0.25.beta4
+- Include theme support (mizmo)
+- Include locale support (kiilerix)
+- Include html docs (kiilerix)
+
+* Thu Apr 26 2012 Peter Jones <pjones@redhat.com> - 2.0-0.24
+- Various fixes from Mads Kiilerich
+
+* Thu Apr 19 2012 Peter Jones <pjones@redhat.com> - 2.0-0.23
+- Update to 2.00~beta4
+- Make fonts work so we can do graphics reasonably
+
+* Thu Mar 29 2012 David Aquilina <dwa@redhat.com> - 2.0-0.22
+- Fix ieee1275 platform define for ppc
+
+* Thu Mar 29 2012 Peter Jones <pjones@redhat.com> - 2.0-0.21
+- Remove ppc excludearch lines (dwa)
+- Update ppc terminfo patch (hamzy)
+
+* Wed Mar 28 2012 Peter Jones <pjones@redhat.com> - 2.0-0.20
+- Fix ppc64 vs ppc exclude according to what dwa tells me they need
+- Fix version number to better match policy.
+
+* Tue Mar 27 2012 Dan Horák <dan[at]danny.cz> - 1.99-19.2
+- Add support for serial terminal consoles on PPC by Mark Hamzy
+
+* Sun Mar 25 2012 Dan Horák <dan[at]danny.cz> - 1.99-19.1
+- Use Fix-tests-of-zeroed-partition patch by Mark Hamzy
+
+* Thu Mar 15 2012 Peter Jones <pjones@redhat.com> - 1.99-19
+- Use --with-grubdir= on configure to make it behave like -17 did.
+
+* Wed Mar 14 2012 Peter Jones <pjones@redhat.com> - 1.99-18
+- Rebase from 1.99 to 2.00~beta2
+
+* Wed Mar 07 2012 Peter Jones <pjones@redhat.com> - 1.99-17
+- Update for newer autotools and gcc 4.7.0
+  Related: rhbz#782144
+- Add /etc/sysconfig/grub link to /etc/default/grub
+  Resolves: rhbz#800152
+- ExcludeArch s390*, which is not supported by this package.
+  Resolves: rhbz#758333
+
+* Fri Feb 17 2012 Orion Poplawski <orion@cora.nwra.com> - 1:1.99-16
+- Build with -Os (bug 782144)
+
+* Fri Jan 13 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1:1.99-15
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild
+
+* Wed Dec 14 2011 Matthew Garrett <mjg@redhat.com> - 1.99-14
+- fix up various grub2-efi issues
+
+* Thu Dec 08 2011 Adam Williamson <awilliam@redhat.com> - 1.99-13
+- fix hardwired call to grub-probe in 30_os-prober (rhbz#737203)
+
+* Mon Nov 07 2011 Peter Jones <pjones@redhat.com> - 1.99-12
+- Lots of .spec fixes from Mads Kiilerich:
+  Remove comment about update-grub - it isn't run in any scriptlets
+  patch info pages so they can be installed and removed correctly when renamed
+  fix references to grub/grub2 renames in info pages (#743964)
+  update README.Fedora (#734090)
+  fix comments for the hack for upgrading from grub2 < 1.99-4
+  fix sed syntax error preventing use of $RPM_OPT_FLAGS (#704820)
+  make /etc/grub2*.cfg %%config(noreplace)
+  make grub.cfg %%ghost - an empty file is of no use anyway
+  create /etc/default/grub more like anaconda would create it (#678453)
+  don't create rescue entries by default - grubby will not maintain them anyway
+  set GRUB_SAVEDEFAULT=true so saved defaults works (rbhz#732058)
+  grub2-efi should have its own bash completion
+  don't set gfxpayload in efi mode - backport upstream r3402
+- Handle dmraid better. Resolves: rhbz#742226
+
+* Wed Oct 26 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1:1.99-11
+- Rebuilt for glibc bug#747377
+
+* Wed Oct 19 2011 Adam Williamson <awilliam@redhat.com> - 1.99-10
+- /etc/default/grub is explicitly intended for user customization, so
+  mark it as config(noreplace)
+
+* Tue Oct 11 2011 Peter Jones <pjones@redhat.com> - 1.99-9
+- grub has an epoch, so we need that expressed in the obsolete as well.
+  Today isn't my day.
+
+* Tue Oct 11 2011 Peter Jones <pjones@redhat.com> - 1.99-8
+- Fix my bad obsoletes syntax.
+
+* Thu Oct 06 2011 Peter Jones <pjones@redhat.com> - 1.99-7
+- Obsolete grub
+  Resolves: rhbz#743381
+
+* Wed Sep 14 2011 Peter Jones <pjones@redhat.com> - 1.99-6
+- Use mv not cp to try to avoid moving disk blocks around for -5 fix
+  Related: rhbz#735259
+- handle initramfs on xen better (patch from Marko Ristola)
+  Resolves: rhbz#728775
+
+* Sat Sep 03 2011 Kalev Lember <kalevlember@gmail.com> - 1.99-5
+- Fix upgrades from grub2 < 1.99-4 (#735259)
+
+* Fri Sep 02 2011 Peter Jones <pjones@redhat.com> - 1.99-4
+- Don't do sysadminny things in %%preun or %%post ever. (#735259)
+- Actually include the changelog in this build (sorry about -3)
+
+* Thu Sep 01 2011 Peter Jones <pjones@redhat.com> - 1.99-2
+- Require os-prober (#678456) (patch from Elad Alfassa)
+- Require which (#734959) (patch from Elad Alfassa)
+
+* Thu Sep 01 2011 Peter Jones <pjones@redhat.com> - 1.99-1
+- Update to grub-1.99 final.
+- Fix crt1.o require on x86-64 (fix from Mads Kiilerich)
+- Various CFLAGS fixes (from Mads Kiilerich)
+  - -fexceptions and -m64
+- Temporarily ignore translations (from Mads Kiilerich)
+
+* Thu Jul 21 2011 Peter Jones <pjones@redhat.com> - 1.99-0.3
+- Use /sbin not /usr/sbin .
+
+* Thu Jun 23 2011 Peter Lemenkov <lemenkov@gmail.com> - 1:1.99-0.2
+- Fixes for ppc and ppc64
+
+* Wed Feb 09 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1:1.98-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild