From 6e0329c02947263a7b6274015db8a45522b554c1 Mon Sep 17 00:00:00 2001
From: Peridot Bot <rockyautomation@rockylinux.org>
Date: Tue, 11 Apr 2023 15:26:32 +0000
Subject: [PATCH] import grub2-2.06-46.el9_1.5

---
 .gitignore                                    |    2 +-
 .grub2.metadata                               |    2 +-
 ...ecureboot-support-on-efi-chainloader.patch |   53 +-
 SOURCES/0010-re-write-.gitignore.patch        |   22 +-
 ...ient-architecture-CAS-reboot-support.patch |   22 +-
 ...nsole-display-attr-when-clear-screen.patch |    4 +-
 ...video-support-for-IBM-power-machines.patch |    9 +-
 ...018-Migrate-PPC-from-Yaboot-to-Grub2.patch |   11 +-
 .../0019-Add-fw_path-variable-revised.patch   |    7 +-
 ...0023-Enable-pager-by-default.-985860.patch |   23 +
 ...n-t-say-GNU-Linux-in-generated-menus.patch |   85 +
 ...rame-to-list-of-relocations-stripped.patch |   22 +
 ...password-to-boot-entries-generated-b.patch |   28 +
 ...ix-when-fallback-searching-for-grub-.patch |   41 +
 ...-before-grub.cfg-on-tftp-config-file.patch |  127 +
 ...CLASS-in-10_linux-from-etc-os-releas.patch |   29 +
 ...t-ordering-for-.debug-and-rescue-ker.patch |   30 +
 ...1-Try-prefix-if-fw_path-doesn-t-work.patch |  222 +
 ...fig-construct-titles-that-look-like-.patch |   69 +
 ...ly-grub2-password-config-tool-985962.patch |  245 +
 .../0034-tcp-add-window-scaling-support.patch |   87 +
 ...net-and-bootp-add-support-for-dhcpv6.patch |  651 ++
 ...rnel-settings-and-use-it-in-10_linux.patch |  271 +
 ...1374141-fix-incorrect-mask-for-ppc64.patch |   48 +
 .../0038-Make-grub_fatal-also-backtrace.patch |  172 +
 ...fo-pages-say-grub2-where-appropriate.patch | 1008 ++
 ...-chainloader-entries-don-t-try-any-x.patch |  124 +
 ...-Add-ability-to-boot-from-subvolumes.patch |  703 ++
 ...port-btrfs_subvol-and-btrfs_subvolid.patch |   26 +
 .../0043-grub2-btrfs-03-follow_default.patch  |  198 +
 .../0044-grub2-btrfs-04-grub2-install.patch   |  176 +
 .../0045-grub2-btrfs-05-grub2-mkconfig.patch  |  129 +
 .../0046-grub2-btrfs-06-subvol-mount.patch    |  539 +
 ...subvol-name-scheme-to-support-old-sn.patch |   58 +
 ...-correctly-with-btrfs-snapshots-bsc-.patch |  274 +
 ...ocate_pool-and-grub_efi_free_pool-wr.patch |   72 +
 ...fi_.-memory-helpers-where-reasonable.patch |  106 +
 ...1-Add-PRIxGRUB_EFI_STATUS-and-use-it.patch |   48 +
 .../0052-don-t-use-int-for-efi-status.patch   |   22 +
 ...INIT-declare-its-function-prototypes.patch |   29 +
 ...-efi-as-HFS-on-ppc-machines-in-grub-.patch |   41 +
 ...d-xen-or-multiboot-2-modules-as-need.patch |   47 +
 ...-calibration-not-take-51-seconds-to-.patch |  211 +
 ...057-align-struct-efi_variable-better.patch |   33 +
 ...058-Add-BLS-support-to-grub-mkconfig.patch |  382 +
 ...-backtrace-on-grub_abort-for-grub-em.patch |   26 +
 SOURCES/0060-Add-grub2-switch-to-blscfg.patch |  395 +
 SOURCES/0061-make-better-backtraces.patch     |  910 ++
 ...w-our-startup-message-if-debug-is-se.patch |   23 +
 ...-some-minor-include-path-weirdnesses.patch |  137 +
 ...it-possible-to-enabled-build-id-sha1.patch |   61 +
 ...f-grub_dprintf-without-the-file-line.patch |   56 +
 ...dprintf-that-tells-us-load-addresses.patch |  178 +
 SOURCES/0067-Fixup-for-newer-compiler.patch   |   36 +
 ...-export-the-start-and-_start-symbols.patch |   42 +
 SOURCES/0069-Fixup-for-newer-compiler.patch   |   22 +
 ...pport-for-non-Ethernet-network-cards.patch |  766 ++
 ...racketed-ipv6-addrs-and-port-numbers.patch |  270 +
 .../0072-bootp-New-net_bootp6-command.patch   | 1368 +++
 .../0073-efinet-UEFI-IPv6-PXE-support.patch   |  126 +
 ...074-grub.texi-Add-net_bootp6-doument.patch |   48 +
 ...essing-DHCPACK-packet-from-HTTP-Boot.patch |  108 +
 ...etting-network-from-UEFI-device-path.patch |  405 +
 ...etting-DNS-server-from-UEFI-protocol.patch |  336 +
 ...78-Support-UEFI-networking-protocols.patch | 5053 ++++++++++
 .../0079-AUDIT-0-http-boot-tracker-bug.patch  |   62 +
 ...-incr-command-to-increment-integer-v.patch |   93 +
 SOURCES/0081-Add-auto-hide-menu-support.patch |  183 +
 .../0082-Add-grub-set-bootflag-utility.patch  |  290 +
 ...b-boot-indeterminate.service-example.patch |   33 +
 SOURCES/0084-gentpl-add-disable-support.patch |   46 +
 .../0085-gentpl-add-pc-firmware-type.patch    |   22 +
 ...e-the-firmware-acceleration-for-http.patch |   25 +
 ...ot_url-reflect-the-protocol-hostname.patch |   50 +
 ...n-tell-configure-which-cflags-utils-.patch |  149 +
 ...make-it-possible-to-run-checkers-on-.patch |   58 +
 ...90-Rework-how-the-fdt-command-builds.patch |  123 +
 ...able-non-wordsize-allocations-on-arm.patch |   41 +
 ...nd-prefix-when-HTTP-path-is-relative.patch |  152 +
 .../0093-Make-grub_error-more-verbose.patch   |   61 +
 ...eset-an-alias-for-the-reboot-command.patch |   40 +
 SOURCES/0095-Add-a-version-command.patch      |  134 +
 ...dprintf-and-nerf-dprintf-in-script.c.patch |   74 +
 ...-Better-memory-allocation-and-error-.patch |  279 +
 ...tter-locations-for-kernel-and-initrd.patch |  211 +
 ...p-all-the-places-Wsign-compare-error.patch |  199 +
 ...gn-compare-Wno-conversion-Wno-error-.patch |   59 +
 ...ce-buffers-for-reading-to-addresses-.patch |  101 +
 ...-arrange-grub_cmd_linux-a-little-bit.patch |  133 +
 ...e-our-own-allocator-for-kernel-stuff.patch |  258 +
 ...itrd-params-cmdline-allocations-abov.patch |  171 +
 .../0105-Fix-getroot.c-s-trampolines.patch    |   47 +
 ...not-allow-stack-trampolines-anywhere.patch |   38 +
 SOURCES/0107-Reimplement-boot_counter.patch   |  196 +
 ...ntry-selection-based-on-ID-and-title.patch |  233 +
 ...try-users-option-argument-to-be-opti.patch |   46 +
 ...export-env-and-efi-load-env-commands.patch |  346 +
 ...le-to-subtract-conditions-from-debug.patch |   45 +
 ...bles-from-the-initial-context-when-c.patch |   44 +
 ...-boot-success-reset-from-menu-auto-h.patch |  168 +
 ...t-boot-commands-will-only-return-on-.patch |   97 +
 ...g-Update-comment-about-running-as-ro.patch |   27 +
 ...g-Write-new-env-to-tmpfile-and-then-.patch |  152 +
 ...indeterminate-getting-set-on-boot_su.patch |   81 +
 .../0118-Add-start-symbol-for-RISC-V.patch    |   28 +
 ...conf-Force-autogen.sh-to-use-python3.patch |   33 +
 ...fw-http-_path-variables-to-make-them.patch |   50 +
 ...-literal-IPv6-addresses-in-square-br.patch |  114 +
 ...o-specify-a-port-number-in-addresses.patch |   48 +
 ...mprove-check-to-detect-literal-IPv6-.patch |   48 +
 ...debug-message-if-parsing-the-address.patch |   68 +
 ...so-accept-F8-as-a-user-interrupt-key.patch |   30 +
 ...se-address-before-jumping-to-the-PE-.patch |   62 +
 ...ate-TPM-measurement-errors-to-the-ve.patch |   62 +
 ...maximum-bounce-buffer-size-to-16-MiB.patch |   40 +
 ...fix-when-the-HTTP-path-is-relative-a.patch |   47 +
 ...turn-in-efi-export-env-and-efi-load-.patch |   27 +
 ...p-fix-some-allocation-error-checking.patch |   37 +
 ...p-fix-some-allocation-error-checking.patch |   39 +
 ...g.c-fix-some-potential-allocation-ov.patch |  127 +
 ...integer-overflows-in-grub_cmd_initrd.patch |   49 +
 ...rnel-validation-without-shim-protoco.patch |  130 +
 ...ar-pointers-in-grub-core-net-bootp.c.patch |   37 +
 ...ointers-in-grub-core-net-efi-ip4_con.patch |   38 +
 ...ointers-in-grub-core-net-efi-ip6_con.patch |   28 +
 ...-pointers-in-grub-core-net-efi-net.c.patch |   37 +
 ...-pointers-in-grub-core-net-efi-pxe.c.patch |   46 +
 ...gration-scripts-to-make-systemctl-re.patch |  190 +
 ...ion.sh-Also-set-old-menu_show_once-g.patch |   32 +
 ...set-1-when-keyboard-is-in-Translate-.patch |  121 +
 ...ll-disable-support-for-EFI-platforms.patch |  101 +
 ...imestamps-configure-flag-to-prepend-.patch |  112 +
 ...ements-to-grub_disk_open-and-grub_di.patch |   47 +
 ...on-grub_debug_is_enabled-void-return.patch |   51 +
 ...ear-screen-when-debugging-is-enabled.patch |   31 +
 ...Fix-error-handling-in-grub_file_open.patch |   34 +
 ..._-instrumentation-new-file-debug-tag.patch |   71 +
 ...-Avoiding-many-unecessary-open-close.patch |  136 +
 ...-implements-fibre-channel-discovery-.patch |   90 +
 ...erpc-enables-device-mapper-discovery.patch |  106 +
 ...fallback_set-var-to-force-the-set-ma.patch |  245 +
 ...igning-grub-with-an-appended-signatu.patch |  309 +
 ...rub-Document-signing-grub-under-UEFI.patch |   61 +
 ...nt-signing-grub-with-an-appended-sig.patch |   67 +
 ...e-grub_dl_set_persistent-for-the-emu.patch |   44 +
 SOURCES/0159-pgp-factor-out-rsa_pad.patch     |  191 +
 ...rage-for-grub_crypto_pk_-to-crypto.c.patch |   71 +
 ...p-tweaks-in-preparation-for-libtasn1.patch |   64 +
 ...0162-libtasn1-import-libtasn1-4.16.0.patch | 8934 +++++++++++++++++
 ...asn1-disable-code-not-needed-in-grub.patch |  307 +
 ...tasn1-changes-for-grub-compatibility.patch |  202 +
 ...65-libtasn1-compile-into-asn1-module.patch |   70 +
 ...6-test_asn1-test-module-for-libtasn1.patch | 1457 +++
 ...-support-embedding-x509-certificates.patch |  253 +
 ...res-import-GNUTLS-s-ASN.1-descriptio.patch |  639 ++
 ...res-parse-PKCS-7-signedData-and-X.50.patch | 1528 +++
 ...res-support-verifying-appended-signa.patch |  718 ++
 ...pended-signatures-verification-tests.patch |  897 ++
 ...72-appended-signatures-documentation.patch |  341 +
 ...er-lockdown-based-on-ibm-secure-boot.patch |  109 +
 ...275-drop-HEAP_MAX_ADDR-HEAP_MIN_SIZE.patch |   75 +
 ...-Also-handle-the-Extended-Key-Usage-.patch |  315 +
 ...eee1275-ofdisk-retry-on-open-failure.patch |  103 +
 ...ainloading-EFI-apps-from-loop-mounts.patch |  138 +
 .../0178-efinet-Add-DHCP-proxy-support.patch  |   53 +
 ...gnore-checksum-seed-incompat-feature.patch |   54 +
 ...-cmdline-when-generating-legacy-menu.patch |   36 +
 .../0181-Suppress-gettext-error-message.patch |   33 +
 ...d-Always-use-boot-grub2-user.cfg-as-.patch |   42 +
 ...for-EFI-at-runtime-instead-of-config.patch |   63 +
 ...or-if-boot-to-firmware-setup-is-not-.patch |   92 +
 ...x-EFI-loader-kernel-image-allocation.patch |  218 +
 ...over-the-device-to-read-the-config-f.patch |  123 +
 ...etting-of-prefix-for-signed-binary-c.patch |   88 +
 ...adable-filesystem-with-v4-superblock.patch |  118 +
 ...module-name-on-license-check-failure.patch |   48 +
 ...pc-ieee1275-load-grub-at-4MB-not-2MB.patch |  106 +
 ...-mkconfig-restore-umask-for-grub.cfg.patch |   40 +
 ...btrfs-Use-full-btrfs-bootloader-area.patch |  160 +
 ...-Fedora-location-of-DejaVu-SANS-font.patch |   30 +
 ...t-show-Booting-s-msg-when-auto-booti.patch |   90 +
 ...-Welcome-to-GRUB-message-in-EFI-buil.patch |   41 +
 ...ot-set-colorstate-until-the-first-te.patch |   51 +
 ...ot-set-cursor-until-the-first-text-o.patch |   71 +
 ...se-visual-indentation-in-config.h.in.patch |   93 +
 ...sure-config-util.h-precedes-config.h.patch |  275 +
 .../0200-Drop-gnulib-fix-base64.patch.patch   |  140 +
 SOURCES/0201-Drop-gnulib-no-abort.patch.patch |   97 +
 ...version-and-drop-most-gnulib-patches.patch |  832 ++
 ...Fix-bug-stopping-iteration-when-no-f.patch |   33 +
 ...w-efidisk-only-option-on-EFI-systems.patch |  168 +
 SOURCES/0205-efi-new-connectefi-command.patch |  395 +
 ...-i386-efi-linux.c-do-not-validate-ke.patch |   73 +
 ...-arm64-linux.c-do-not-validate-kerne.patch |   58 +
 ...-efi-chainloader.c-do-not-validate-c.patch |   80 +
 ...-efi-linux.c-drop-now-unused-grub_li.patch |   83 +
 ...etection-support-device-names-with-c.patch |   71 +
 .../0211-make-ofdisk_retries-optional.patch   |   43 +
 ...loader-grub_load_and_start_image-doe.patch |   69 +
 ...hainloader-simplify-the-loader-state.patch |  330 +
 ...ot-Add-API-to-pass-context-to-loader.patch |  158 +
 ...i-chainloader-Use-grub_loader_set_ex.patch |  147 +
 ...linux-Avoid-a-use-after-free-in-the-.patch |   41 +
 ...386-efi-linux-Use-grub_loader_set_ex.patch |  296 +
 ...linux-Fix-a-memory-leak-in-the-initr.patch |   75 +
 ...ct-non-kernel-files-in-the-shim_lock.patch |  101 +
 ...-leak-device_name-on-error-in-grub_f.patch |   39 +
 ...g-Abort-sooner-if-a-read-operation-f.patch |  198 +
 ...g-Refuse-to-handle-multiple-image-he.patch |   28 +
 ...g-Drop-greyscale-support-to-fix-heap.patch |  170 +
 ...g-Avoid-heap-OOB-R-W-inserting-huff-.patch |   40 +
 ...-png-Sanity-check-some-huffman-codes.patch |   40 +
 ...eg-Abort-sooner-if-a-read-operation-.patch |  255 +
 ...eg-Do-not-reallocate-a-given-huff-ta.patch |   29 +
 ...eg-Refuse-to-handle-multiple-start-o.patch |   44 +
 ...eg-Block-int-underflow-wild-pointer-.patch |   53 +
 ...ix-array-out-of-bounds-formatting-un.patch |   34 +
 ...ff-Block-overly-large-netbuff-allocs.patch |   46 +
 ...2-net-ip-Do-IP-fragment-maths-safely.patch |   44 +
 ...le-free-addresses-on-corrupt-DNS-res.patch |   56 +
 ...ad-past-the-end-of-the-string-we-re-.patch |   71 +
 ...-a-UAF-and-double-free-from-a-failed.patch |  112 +
 .../0236-net-tftp-Avoid-a-trivial-UAF.patch   |   35 +
 ...tear-down-socket-if-it-s-already-bee.patch |   42 +
 ...Fix-OOB-write-for-split-http-headers.patch |   46 +
 ...or-out-on-headers-with-LF-without-CR.patch |   48 +
 ...ead-past-the-end-of-nat-journal-entr.patch |   72 +
 ...-not-read-past-the-end-of-nat-bitmap.patch |  132 +
 ...ot-copy-file-names-that-are-too-long.patch |   38 +
 ...eral-fuzz-issues-with-invalid-dir-it.patch |   79 +
 ...e-ASAN-and-SEGV-issues-found-with-fu.patch |  134 +
 ...x-more-fuzz-issues-related-to-chunks.patch |   75 +
 ...grub_min-and-grub_max-more-resilient.patch |   83 +
 ...FS-switch-to-using-grub_min-grub_max.patch |   92 +
 ...oot_time-also-call-grub_dprintf-boot.patch |   46 +
 ...dules-make-.module_license-read-only.patch |   30 +
 ...p-.llvm_addrsig-sections-and-similar.patch |   39 +
 ...locate-space-for-non-allocable-secti.patch |   35 +
 ...eader-struct-and-fix-some-bad-naming.patch |   81 +
 ...nel-in-EFI_RUNTIME_SERVICES_CODE-ins.patch |   85 +
 ...ule-sections-at-page-aligned-address.patch |  378 +
 ...-nx-add-memory-attribute-get-set-API.patch |  317 +
 ...-page-permissions-for-loaded-modules.patch |  263 +
 ...7-nx-set-attrs-in-our-kernel-loaders.patch |  565 ++
 ...x-compatible-flag-in-EFI-grub-images.patch |   34 +
 ...-document-the-behavior-of-multiple-v.patch |   25 +
 ...probe-dprint-errors-from-filesystems.patch |   43 +
 ...1-fs-fat-don-t-error-when-mtime-is-0.patch |   64 +
 ...file-show-which-file-filters-get-run.patch |   45 +
 ...ed-array-positions-for-our-allocatio.patch |   81 +
 ...tion-policy-for-kernel-vs-initrd-mem.patch |  127 +
 ...-initrd-within-the-bounds-expressed-.patch |   57 +
 ...ER_-CODE-DATA-for-kernel-and-initrd-.patch |   61 +
 ...e-etc-kernel-cmdline-during-mkconfig.patch |   27 +
 ...sh-don-t-dup-rhgb-quiet-check-mtimes.patch |   35 +
 .../0269-squish-give-up-on-rhgb-quiet.patch   |   25 +
 ...write-etc-kernel-cmdline-if-writable.patch |   57 +
 ...scfg-Don-t-root-device-in-emu-builds.patch |   27 +
 ...ux-Remove-magic-number-header-field-.patch |   43 +
 .../0273-Correct-BSS-zeroing-on-aarch64.patch |   95 +
 ...ate-i-cache-before-starting-the-kern.patch |   39 +
 ...ncorrect-array-size-in-kernel-alloca.patch |   36 +
 ...i-tpm-Refine-the-status-of-log-event.patch |   42 +
 ...-Use-grub_strcpy-instead-of-grub_mem.patch |   37 +
 ...-EFI_CC_MEASUREMENT_PROTOCOL-support.patch |  258 +
 ...hs-exceeds-font-max_glyph_width-or-f.patch |   31 +
 ...erflow-in-grub_font_get_glyph_intern.patch |  110 +
 ...-integer-overflows-in-grub_font_cons.patch |   79 +
 ...0282-font-Remove-grub_font_dup_glyph.patch |   40 +
 ...nteger-overflow-in-ensure_comb_space.patch |   46 +
 ...nt-Fix-integer-overflow-in-BMP-index.patch |   63 +
 ...-underflow-in-binary-search-of-char-.patch |   83 +
 ...b-Enforce-verification-of-font-files.patch |   52 +
 .../0287-fbutil-Fix-integer-overflow.patch    |   83 +
 ...ix-an-integer-underflow-in-blit_comb.patch |   89 +
 ..._font_blit_glyph-and-grub_font_blit_.patch |   73 +
 ...l_font-to-glyphs-in-ascii_font_glyph.patch |   34 +
 ...ix-an-integer-overflow-in-grub_unico.patch |   53 +
 ...ening-fonts-from-the-bundled-memdisk.patch |   78 +
 .../0293-mm-Clarify-grub_real_malloc.patch    |  183 +
 ...loc-Make-small-allocs-comment-match-.patch |   32 +
 SOURCES/0295-mm-Document-grub_free.patch      |  120 +
 ...0296-mm-Document-grub_mm_init_region.patch |   73 +
 ...-internal-memory-management-structur.patch |   78 +
 ...e-preserve-header-vs-region-alignmen.patch |   56 +
 ...-region-merge-with-region-after-as-w.patch |  203 +
 ...-Debug-support-for-region-operations.patch |   71 +
 ...p-unused-unloading-of-modules-on-OOM.patch |   80 +
 ...ally-requesting-additional-memory-re.patch |  130 +
 ...ys-request-a-fixed-number-of-pages-o.patch |  103 +
 ...tract-function-to-add-memory-regions.patch |   85 +
 ...ss-up-errors-from-add_memory_regions.patch |   87 +
 ...-Implement-runtime-addition-of-pages.patch |   75 +
 ...-default-memory-allocation-to-32-MiB.patch |   31 +
 ...e-disk-caches-last-when-out-of-memor.patch |   57 +
 ...09-ppc64le-signed-boot-media-changes.patch |  125 +
 ...veral-implicit-function-declarations.patch |   54 +
 ...rt-for-grub-emu-to-kexec-Linux-menu-.patch |  431 +
 ...n-Hack-Ware-remove-GRUB_IEEE1275_FLA.patch |  111 +
 ...-memory-with-ibm-client-architecture.patch |  308 +
 ...ee1275-drop-len-1-quirk-in-heap_init.patch |   36 +
 ...1275-support-runtime-memory-claiming.patch |  435 +
 ...5-implement-vec5-for-cas-negotiation.patch |   74 +
 ...ort-for-trusted-boot-using-a-vTPM-2..patch |  246 +
 .../0318-powerpc-Drop-Open-Hack-Ware.patch    |   69 +
 ...disk-Modify-sector-by-sysfs-as-disk-.patch |   70 +
 ...gion-size-to-take-management-overhea.patch |  145 +
 ...e-some-space-when-adding-new-regions.patch |   53 +
 ...complex-heap-growth-math-in-hot-path.patch |   72 +
 SOURCES/grub.macros                           |   10 +-
 SOURCES/grub.patches                          |  584 +-
 SPECS/grub2.spec                              |   30 +-
 312 files changed, 56253 insertions(+), 351 deletions(-)
 create mode 100644 SOURCES/0023-Enable-pager-by-default.-985860.patch
 create mode 100644 SOURCES/0024-Don-t-say-GNU-Linux-in-generated-menus.patch
 create mode 100644 SOURCES/0025-Add-.eh_frame-to-list-of-relocations-stripped.patch
 create mode 100644 SOURCES/0026-Don-t-require-a-password-to-boot-entries-generated-b.patch
 create mode 100644 SOURCES/0027-use-fw_path-prefix-when-fallback-searching-for-grub-.patch
 create mode 100644 SOURCES/0028-Try-mac-guid-etc-before-grub.cfg-on-tftp-config-file.patch
 create mode 100644 SOURCES/0029-Generate-OS-and-CLASS-in-10_linux-from-etc-os-releas.patch
 create mode 100644 SOURCES/0030-Minimize-the-sort-ordering-for-.debug-and-rescue-ker.patch
 create mode 100644 SOURCES/0031-Try-prefix-if-fw_path-doesn-t-work.patch
 create mode 100644 SOURCES/0032-Make-grub2-mkconfig-construct-titles-that-look-like-.patch
 create mode 100644 SOURCES/0033-Add-friendly-grub2-password-config-tool-985962.patch
 create mode 100644 SOURCES/0034-tcp-add-window-scaling-support.patch
 create mode 100644 SOURCES/0035-efinet-and-bootp-add-support-for-dhcpv6.patch
 create mode 100644 SOURCES/0036-Add-grub-get-kernel-settings-and-use-it-in-10_linux.patch
 create mode 100644 SOURCES/0037-bz1374141-fix-incorrect-mask-for-ppc64.patch
 create mode 100644 SOURCES/0038-Make-grub_fatal-also-backtrace.patch
 create mode 100644 SOURCES/0039-Make-our-info-pages-say-grub2-where-appropriate.patch
 create mode 100644 SOURCES/0040-macos-just-build-chainloader-entries-don-t-try-any-x.patch
 create mode 100644 SOURCES/0041-grub2-btrfs-Add-ability-to-boot-from-subvolumes.patch
 create mode 100644 SOURCES/0042-export-btrfs_subvol-and-btrfs_subvolid.patch
 create mode 100644 SOURCES/0043-grub2-btrfs-03-follow_default.patch
 create mode 100644 SOURCES/0044-grub2-btrfs-04-grub2-install.patch
 create mode 100644 SOURCES/0045-grub2-btrfs-05-grub2-mkconfig.patch
 create mode 100644 SOURCES/0046-grub2-btrfs-06-subvol-mount.patch
 create mode 100644 SOURCES/0047-Fallback-to-old-subvol-name-scheme-to-support-old-sn.patch
 create mode 100644 SOURCES/0048-Grub-not-working-correctly-with-btrfs-snapshots-bsc-.patch
 create mode 100644 SOURCES/0049-Add-grub_efi_allocate_pool-and-grub_efi_free_pool-wr.patch
 create mode 100644 SOURCES/0050-Use-grub_efi_.-memory-helpers-where-reasonable.patch
 create mode 100644 SOURCES/0051-Add-PRIxGRUB_EFI_STATUS-and-use-it.patch
 create mode 100644 SOURCES/0052-don-t-use-int-for-efi-status.patch
 create mode 100644 SOURCES/0053-make-GRUB_MOD_INIT-declare-its-function-prototypes.patch
 create mode 100644 SOURCES/0054-Don-t-guess-boot-efi-as-HFS-on-ppc-machines-in-grub-.patch
 create mode 100644 SOURCES/0055-20_linux_xen-load-xen-or-multiboot-2-modules-as-need.patch
 create mode 100644 SOURCES/0056-Make-pmtimer-tsc-calibration-not-take-51-seconds-to-.patch
 create mode 100644 SOURCES/0057-align-struct-efi_variable-better.patch
 create mode 100644 SOURCES/0058-Add-BLS-support-to-grub-mkconfig.patch
 create mode 100644 SOURCES/0059-Don-t-attempt-to-backtrace-on-grub_abort-for-grub-em.patch
 create mode 100644 SOURCES/0060-Add-grub2-switch-to-blscfg.patch
 create mode 100644 SOURCES/0061-make-better-backtraces.patch
 create mode 100644 SOURCES/0062-normal-don-t-draw-our-startup-message-if-debug-is-se.patch
 create mode 100644 SOURCES/0063-Work-around-some-minor-include-path-weirdnesses.patch
 create mode 100644 SOURCES/0064-Make-it-possible-to-enabled-build-id-sha1.patch
 create mode 100644 SOURCES/0065-Add-grub_qdprintf-grub_dprintf-without-the-file-line.patch
 create mode 100644 SOURCES/0066-Make-a-gdb-dprintf-that-tells-us-load-addresses.patch
 create mode 100644 SOURCES/0067-Fixup-for-newer-compiler.patch
 create mode 100644 SOURCES/0068-Don-t-attempt-to-export-the-start-and-_start-symbols.patch
 create mode 100644 SOURCES/0069-Fixup-for-newer-compiler.patch
 create mode 100644 SOURCES/0070-Add-support-for-non-Ethernet-network-cards.patch
 create mode 100644 SOURCES/0071-net-read-bracketed-ipv6-addrs-and-port-numbers.patch
 create mode 100644 SOURCES/0072-bootp-New-net_bootp6-command.patch
 create mode 100644 SOURCES/0073-efinet-UEFI-IPv6-PXE-support.patch
 create mode 100644 SOURCES/0074-grub.texi-Add-net_bootp6-doument.patch
 create mode 100644 SOURCES/0075-bootp-Add-processing-DHCPACK-packet-from-HTTP-Boot.patch
 create mode 100644 SOURCES/0076-efinet-Setting-network-from-UEFI-device-path.patch
 create mode 100644 SOURCES/0077-efinet-Setting-DNS-server-from-UEFI-protocol.patch
 create mode 100644 SOURCES/0078-Support-UEFI-networking-protocols.patch
 create mode 100644 SOURCES/0079-AUDIT-0-http-boot-tracker-bug.patch
 create mode 100644 SOURCES/0080-grub-editenv-Add-incr-command-to-increment-integer-v.patch
 create mode 100644 SOURCES/0081-Add-auto-hide-menu-support.patch
 create mode 100644 SOURCES/0082-Add-grub-set-bootflag-utility.patch
 create mode 100644 SOURCES/0083-docs-Add-grub-boot-indeterminate.service-example.patch
 create mode 100644 SOURCES/0084-gentpl-add-disable-support.patch
 create mode 100644 SOURCES/0085-gentpl-add-pc-firmware-type.patch
 create mode 100644 SOURCES/0086-efinet-also-use-the-firmware-acceleration-for-http.patch
 create mode 100644 SOURCES/0087-efi-http-Make-root_url-reflect-the-protocol-hostname.patch
 create mode 100644 SOURCES/0088-Make-it-so-we-can-tell-configure-which-cflags-utils-.patch
 create mode 100644 SOURCES/0089-module-verifier-make-it-possible-to-run-checkers-on-.patch
 create mode 100644 SOURCES/0090-Rework-how-the-fdt-command-builds.patch
 create mode 100644 SOURCES/0091-Disable-non-wordsize-allocations-on-arm.patch
 create mode 100644 SOURCES/0092-Prepend-prefix-when-HTTP-path-is-relative.patch
 create mode 100644 SOURCES/0093-Make-grub_error-more-verbose.patch
 create mode 100644 SOURCES/0094-Make-reset-an-alias-for-the-reboot-command.patch
 create mode 100644 SOURCES/0095-Add-a-version-command.patch
 create mode 100644 SOURCES/0096-Add-more-dprintf-and-nerf-dprintf-in-script.c.patch
 create mode 100644 SOURCES/0097-arm-arm64-loader-Better-memory-allocation-and-error-.patch
 create mode 100644 SOURCES/0098-Try-to-pick-better-locations-for-kernel-and-initrd.patch
 create mode 100644 SOURCES/0099-Attempt-to-fix-up-all-the-places-Wsign-compare-error.patch
 create mode 100644 SOURCES/0100-Don-t-use-Wno-sign-compare-Wno-conversion-Wno-error-.patch
 create mode 100644 SOURCES/0101-x86-efi-Use-bounce-buffers-for-reading-to-addresses-.patch
 create mode 100644 SOURCES/0102-x86-efi-Re-arrange-grub_cmd_linux-a-little-bit.patch
 create mode 100644 SOURCES/0103-x86-efi-Make-our-own-allocator-for-kernel-stuff.patch
 create mode 100644 SOURCES/0104-x86-efi-Allow-initrd-params-cmdline-allocations-abov.patch
 create mode 100644 SOURCES/0105-Fix-getroot.c-s-trampolines.patch
 create mode 100644 SOURCES/0106-Do-not-allow-stack-trampolines-anywhere.patch
 create mode 100644 SOURCES/0107-Reimplement-boot_counter.patch
 create mode 100644 SOURCES/0108-Fix-menu-entry-selection-based-on-ID-and-title.patch
 create mode 100644 SOURCES/0109-Make-the-menu-entry-users-option-argument-to-be-opti.patch
 create mode 100644 SOURCES/0110-Add-efi-export-env-and-efi-load-env-commands.patch
 create mode 100644 SOURCES/0111-Make-it-possible-to-subtract-conditions-from-debug.patch
 create mode 100644 SOURCES/0112-Export-all-variables-from-the-initial-context-when-c.patch
 create mode 100644 SOURCES/0113-grub.d-Split-out-boot-success-reset-from-menu-auto-h.patch
 create mode 100644 SOURCES/0114-Don-t-assume-that-boot-commands-will-only-return-on-.patch
 create mode 100644 SOURCES/0115-grub-set-bootflag-Update-comment-about-running-as-ro.patch
 create mode 100644 SOURCES/0116-grub-set-bootflag-Write-new-env-to-tmpfile-and-then-.patch
 create mode 100644 SOURCES/0117-grub.d-Fix-boot_indeterminate-getting-set-on-boot_su.patch
 create mode 100644 SOURCES/0118-Add-start-symbol-for-RISC-V.patch
 create mode 100644 SOURCES/0119-bootstrap.conf-Force-autogen.sh-to-use-python3.patch
 create mode 100644 SOURCES/0120-efi-http-Export-fw-http-_path-variables-to-make-them.patch
 create mode 100644 SOURCES/0121-efi-http-Enclose-literal-IPv6-addresses-in-square-br.patch
 create mode 100644 SOURCES/0122-efi-net-Allow-to-specify-a-port-number-in-addresses.patch
 create mode 100644 SOURCES/0123-efi-ip4_config-Improve-check-to-detect-literal-IPv6-.patch
 create mode 100644 SOURCES/0124-efi-net-Print-a-debug-message-if-parsing-the-address.patch
 create mode 100644 SOURCES/0125-kern-term-Also-accept-F8-as-a-user-interrupt-key.patch
 create mode 100644 SOURCES/0126-efi-Set-image-base-address-before-jumping-to-the-PE-.patch
 create mode 100644 SOURCES/0127-tpm-Don-t-propagate-TPM-measurement-errors-to-the-ve.patch
 create mode 100644 SOURCES/0128-x86-efi-Reduce-maximum-bounce-buffer-size-to-16-MiB.patch
 create mode 100644 SOURCES/0129-http-Prepend-prefix-when-the-HTTP-path-is-relative-a.patch
 create mode 100644 SOURCES/0130-Fix-a-missing-return-in-efi-export-env-and-efi-load-.patch
 create mode 100644 SOURCES/0131-efi-dhcp-fix-some-allocation-error-checking.patch
 create mode 100644 SOURCES/0132-efi-http-fix-some-allocation-error-checking.patch
 create mode 100644 SOURCES/0133-efi-ip-46-_config.c-fix-some-potential-allocation-ov.patch
 create mode 100644 SOURCES/0134-efilinux-Fix-integer-overflows-in-grub_cmd_initrd.patch
 create mode 100644 SOURCES/0135-linuxefi-fail-kernel-validation-without-shim-protoco.patch
 create mode 100644 SOURCES/0136-Fix-const-char-pointers-in-grub-core-net-bootp.c.patch
 create mode 100644 SOURCES/0137-Fix-const-char-pointers-in-grub-core-net-efi-ip4_con.patch
 create mode 100644 SOURCES/0138-Fix-const-char-pointers-in-grub-core-net-efi-ip6_con.patch
 create mode 100644 SOURCES/0139-Fix-const-char-pointers-in-grub-core-net-efi-net.c.patch
 create mode 100644 SOURCES/0140-Fix-const-char-pointers-in-grub-core-net-efi-pxe.c.patch
 create mode 100644 SOURCES/0141-Add-systemd-integration-scripts-to-make-systemctl-re.patch
 create mode 100644 SOURCES/0142-systemd-integration.sh-Also-set-old-menu_show_once-g.patch
 create mode 100644 SOURCES/0143-at_keyboard-use-set-1-when-keyboard-is-in-Translate-.patch
 create mode 100644 SOURCES/0144-grub-install-disable-support-for-EFI-platforms.patch
 create mode 100644 SOURCES/0145-New-with-debug-timestamps-configure-flag-to-prepend-.patch
 create mode 100644 SOURCES/0146-Added-debug-statements-to-grub_disk_open-and-grub_di.patch
 create mode 100644 SOURCES/0147-Introduce-function-grub_debug_is_enabled-void-return.patch
 create mode 100644 SOURCES/0148-Don-t-clear-screen-when-debugging-is-enabled.patch
 create mode 100644 SOURCES/0149-kern-file-Fix-error-handling-in-grub_file_open.patch
 create mode 100644 SOURCES/0150-grub_file_-instrumentation-new-file-debug-tag.patch
 create mode 100644 SOURCES/0151-ieee1275-Avoiding-many-unecessary-open-close.patch
 create mode 100644 SOURCES/0152-ieee1275-powerpc-implements-fibre-channel-discovery-.patch
 create mode 100644 SOURCES/0153-ieee1275-powerpc-enables-device-mapper-discovery.patch
 create mode 100644 SOURCES/0154-Add-at_keyboard_fallback_set-var-to-force-the-set-ma.patch
 create mode 100644 SOURCES/0155-Add-suport-for-signing-grub-with-an-appended-signatu.patch
 create mode 100644 SOURCES/0156-docs-grub-Document-signing-grub-under-UEFI.patch
 create mode 100644 SOURCES/0157-docs-grub-Document-signing-grub-with-an-appended-sig.patch
 create mode 100644 SOURCES/0158-dl-provide-a-fake-grub_dl_set_persistent-for-the-emu.patch
 create mode 100644 SOURCES/0159-pgp-factor-out-rsa_pad.patch
 create mode 100644 SOURCES/0160-crypto-move-storage-for-grub_crypto_pk_-to-crypto.c.patch
 create mode 100644 SOURCES/0161-posix_wrap-tweaks-in-preparation-for-libtasn1.patch
 create mode 100644 SOURCES/0162-libtasn1-import-libtasn1-4.16.0.patch
 create mode 100644 SOURCES/0163-libtasn1-disable-code-not-needed-in-grub.patch
 create mode 100644 SOURCES/0164-libtasn1-changes-for-grub-compatibility.patch
 create mode 100644 SOURCES/0165-libtasn1-compile-into-asn1-module.patch
 create mode 100644 SOURCES/0166-test_asn1-test-module-for-libtasn1.patch
 create mode 100644 SOURCES/0167-grub-install-support-embedding-x509-certificates.patch
 create mode 100644 SOURCES/0168-appended-signatures-import-GNUTLS-s-ASN.1-descriptio.patch
 create mode 100644 SOURCES/0169-appended-signatures-parse-PKCS-7-signedData-and-X.50.patch
 create mode 100644 SOURCES/0170-appended-signatures-support-verifying-appended-signa.patch
 create mode 100644 SOURCES/0171-appended-signatures-verification-tests.patch
 create mode 100644 SOURCES/0172-appended-signatures-documentation.patch
 create mode 100644 SOURCES/0173-ieee1275-enter-lockdown-based-on-ibm-secure-boot.patch
 create mode 100644 SOURCES/0174-ieee1275-drop-HEAP_MAX_ADDR-HEAP_MIN_SIZE.patch
 create mode 100644 SOURCES/0175-appendedsig-x509-Also-handle-the-Extended-Key-Usage-.patch
 create mode 100644 SOURCES/0176-ieee1275-ofdisk-retry-on-open-failure.patch
 create mode 100644 SOURCES/0177-Allow-chainloading-EFI-apps-from-loop-mounts.patch
 create mode 100644 SOURCES/0178-efinet-Add-DHCP-proxy-support.patch
 create mode 100644 SOURCES/0179-fs-ext2-Ignore-checksum-seed-incompat-feature.patch
 create mode 100644 SOURCES/0180-Don-t-update-the-cmdline-when-generating-legacy-menu.patch
 create mode 100644 SOURCES/0181-Suppress-gettext-error-message.patch
 create mode 100644 SOURCES/0182-grub-set-password-Always-use-boot-grub2-user.cfg-as-.patch
 create mode 100644 SOURCES/0183-templates-Check-for-EFI-at-runtime-instead-of-config.patch
 create mode 100644 SOURCES/0184-efi-Print-an-error-if-boot-to-firmware-setup-is-not-.patch
 create mode 100644 SOURCES/0185-arm64-Fix-EFI-loader-kernel-image-allocation.patch
 create mode 100644 SOURCES/0186-normal-main-Discover-the-device-to-read-the-config-f.patch
 create mode 100644 SOURCES/0187-powerpc-adjust-setting-of-prefix-for-signed-binary-c.patch
 create mode 100644 SOURCES/0188-fs-xfs-Fix-unreadable-filesystem-with-v4-superblock.patch
 create mode 100644 SOURCES/0189-Print-module-name-on-license-check-failure.patch
 create mode 100644 SOURCES/0190-powerpc-ieee1275-load-grub-at-4MB-not-2MB.patch
 create mode 100644 SOURCES/0191-grub-mkconfig-restore-umask-for-grub.cfg.patch
 create mode 100644 SOURCES/0192-fs-btrfs-Use-full-btrfs-bootloader-area.patch
 create mode 100644 SOURCES/0193-Add-Fedora-location-of-DejaVu-SANS-font.patch
 create mode 100644 SOURCES/0194-normal-menu-Don-t-show-Booting-s-msg-when-auto-booti.patch
 create mode 100644 SOURCES/0195-EFI-suppress-the-Welcome-to-GRUB-message-in-EFI-buil.patch
 create mode 100644 SOURCES/0196-EFI-console-Do-not-set-colorstate-until-the-first-te.patch
 create mode 100644 SOURCES/0197-EFI-console-Do-not-set-cursor-until-the-first-text-o.patch
 create mode 100644 SOURCES/0198-Use-visual-indentation-in-config.h.in.patch
 create mode 100644 SOURCES/0199-Where-present-ensure-config-util.h-precedes-config.h.patch
 create mode 100644 SOURCES/0200-Drop-gnulib-fix-base64.patch.patch
 create mode 100644 SOURCES/0201-Drop-gnulib-no-abort.patch.patch
 create mode 100644 SOURCES/0202-Update-gnulib-version-and-drop-most-gnulib-patches.patch
 create mode 100644 SOURCES/0203-commands-search-Fix-bug-stopping-iteration-when-no-f.patch
 create mode 100644 SOURCES/0204-search-new-efidisk-only-option-on-EFI-systems.patch
 create mode 100644 SOURCES/0205-efi-new-connectefi-command.patch
 create mode 100644 SOURCES/0206-grub-core-loader-i386-efi-linux.c-do-not-validate-ke.patch
 create mode 100644 SOURCES/0207-grub-core-loader-arm64-linux.c-do-not-validate-kerne.patch
 create mode 100644 SOURCES/0208-grub-core-loader-efi-chainloader.c-do-not-validate-c.patch
 create mode 100644 SOURCES/0209-grub-core-loader-efi-linux.c-drop-now-unused-grub_li.patch
 create mode 100644 SOURCES/0210-powerpc-prefix-detection-support-device-names-with-c.patch
 create mode 100644 SOURCES/0211-make-ofdisk_retries-optional.patch
 create mode 100644 SOURCES/0212-loader-efi-chainloader-grub_load_and_start_image-doe.patch
 create mode 100644 SOURCES/0213-loader-efi-chainloader-simplify-the-loader-state.patch
 create mode 100644 SOURCES/0214-commands-boot-Add-API-to-pass-context-to-loader.patch
 create mode 100644 SOURCES/0215-loader-efi-chainloader-Use-grub_loader_set_ex.patch
 create mode 100644 SOURCES/0216-loader-i386-efi-linux-Avoid-a-use-after-free-in-the-.patch
 create mode 100644 SOURCES/0217-loader-i386-efi-linux-Use-grub_loader_set_ex.patch
 create mode 100644 SOURCES/0218-loader-i386-efi-linux-Fix-a-memory-leak-in-the-initr.patch
 create mode 100644 SOURCES/0219-kern-efi-sb-Reject-non-kernel-files-in-the-shim_lock.patch
 create mode 100644 SOURCES/0220-kern-file-Do-not-leak-device_name-on-error-in-grub_f.patch
 create mode 100644 SOURCES/0221-video-readers-png-Abort-sooner-if-a-read-operation-f.patch
 create mode 100644 SOURCES/0222-video-readers-png-Refuse-to-handle-multiple-image-he.patch
 create mode 100644 SOURCES/0223-video-readers-png-Drop-greyscale-support-to-fix-heap.patch
 create mode 100644 SOURCES/0224-video-readers-png-Avoid-heap-OOB-R-W-inserting-huff-.patch
 create mode 100644 SOURCES/0225-video-readers-png-Sanity-check-some-huffman-codes.patch
 create mode 100644 SOURCES/0226-video-readers-jpeg-Abort-sooner-if-a-read-operation-.patch
 create mode 100644 SOURCES/0227-video-readers-jpeg-Do-not-reallocate-a-given-huff-ta.patch
 create mode 100644 SOURCES/0228-video-readers-jpeg-Refuse-to-handle-multiple-start-o.patch
 create mode 100644 SOURCES/0229-video-readers-jpeg-Block-int-underflow-wild-pointer-.patch
 create mode 100644 SOURCES/0230-normal-charset-Fix-array-out-of-bounds-formatting-un.patch
 create mode 100644 SOURCES/0231-net-netbuff-Block-overly-large-netbuff-allocs.patch
 create mode 100644 SOURCES/0232-net-ip-Do-IP-fragment-maths-safely.patch
 create mode 100644 SOURCES/0233-net-dns-Fix-double-free-addresses-on-corrupt-DNS-res.patch
 create mode 100644 SOURCES/0234-net-dns-Don-t-read-past-the-end-of-the-string-we-re-.patch
 create mode 100644 SOURCES/0235-net-tftp-Prevent-a-UAF-and-double-free-from-a-failed.patch
 create mode 100644 SOURCES/0236-net-tftp-Avoid-a-trivial-UAF.patch
 create mode 100644 SOURCES/0237-net-http-Do-not-tear-down-socket-if-it-s-already-bee.patch
 create mode 100644 SOURCES/0238-net-http-Fix-OOB-write-for-split-http-headers.patch
 create mode 100644 SOURCES/0239-net-http-Error-out-on-headers-with-LF-without-CR.patch
 create mode 100644 SOURCES/0240-fs-f2fs-Do-not-read-past-the-end-of-nat-journal-entr.patch
 create mode 100644 SOURCES/0241-fs-f2fs-Do-not-read-past-the-end-of-nat-bitmap.patch
 create mode 100644 SOURCES/0242-fs-f2fs-Do-not-copy-file-names-that-are-too-long.patch
 create mode 100644 SOURCES/0243-fs-btrfs-Fix-several-fuzz-issues-with-invalid-dir-it.patch
 create mode 100644 SOURCES/0244-fs-btrfs-Fix-more-ASAN-and-SEGV-issues-found-with-fu.patch
 create mode 100644 SOURCES/0245-fs-btrfs-Fix-more-fuzz-issues-related-to-chunks.patch
 create mode 100644 SOURCES/0246-misc-Make-grub_min-and-grub_max-more-resilient.patch
 create mode 100644 SOURCES/0247-ReiserFS-switch-to-using-grub_min-grub_max.patch
 create mode 100644 SOURCES/0248-misc-make-grub_boot_time-also-call-grub_dprintf-boot.patch
 create mode 100644 SOURCES/0249-modules-make-.module_license-read-only.patch
 create mode 100644 SOURCES/0250-modules-strip-.llvm_addrsig-sections-and-similar.patch
 create mode 100644 SOURCES/0251-modules-Don-t-allocate-space-for-non-allocable-secti.patch
 create mode 100644 SOURCES/0252-pe-add-the-DOS-header-struct-and-fix-some-bad-naming.patch
 create mode 100644 SOURCES/0253-EFI-allocate-kernel-in-EFI_RUNTIME_SERVICES_CODE-ins.patch
 create mode 100644 SOURCES/0254-modules-load-module-sections-at-page-aligned-address.patch
 create mode 100644 SOURCES/0255-nx-add-memory-attribute-get-set-API.patch
 create mode 100644 SOURCES/0256-nx-set-page-permissions-for-loaded-modules.patch
 create mode 100644 SOURCES/0257-nx-set-attrs-in-our-kernel-loaders.patch
 create mode 100644 SOURCES/0258-nx-set-the-nx-compatible-flag-in-EFI-grub-images.patch
 create mode 100644 SOURCES/0259-grub-probe-document-the-behavior-of-multiple-v.patch
 create mode 100644 SOURCES/0260-grub_fs_probe-dprint-errors-from-filesystems.patch
 create mode 100644 SOURCES/0261-fs-fat-don-t-error-when-mtime-is-0.patch
 create mode 100644 SOURCES/0262-Make-debug-file-show-which-file-filters-get-run.patch
 create mode 100644 SOURCES/0263-efi-use-enumerated-array-positions-for-our-allocatio.patch
 create mode 100644 SOURCES/0264-efi-split-allocation-policy-for-kernel-vs-initrd-mem.patch
 create mode 100644 SOURCES/0265-efi-allocate-the-initrd-within-the-bounds-expressed-.patch
 create mode 100644 SOURCES/0266-efi-use-EFI_LOADER_-CODE-DATA-for-kernel-and-initrd-.patch
 create mode 100644 SOURCES/0267-BLS-create-etc-kernel-cmdline-during-mkconfig.patch
 create mode 100644 SOURCES/0268-squish-don-t-dup-rhgb-quiet-check-mtimes.patch
 create mode 100644 SOURCES/0269-squish-give-up-on-rhgb-quiet.patch
 create mode 100644 SOURCES/0270-squish-BLS-only-write-etc-kernel-cmdline-if-writable.patch
 create mode 100644 SOURCES/0271-blscfg-Don-t-root-device-in-emu-builds.patch
 create mode 100644 SOURCES/0272-loader-arm64-linux-Remove-magic-number-header-field-.patch
 create mode 100644 SOURCES/0273-Correct-BSS-zeroing-on-aarch64.patch
 create mode 100644 SOURCES/0274-linuxefi-Invalidate-i-cache-before-starting-the-kern.patch
 create mode 100644 SOURCES/0275-x86-efi-Fix-an-incorrect-array-size-in-kernel-alloca.patch
 create mode 100644 SOURCES/0276-commands-efi-tpm-Refine-the-status-of-log-event.patch
 create mode 100644 SOURCES/0277-commands-efi-tpm-Use-grub_strcpy-instead-of-grub_mem.patch
 create mode 100644 SOURCES/0278-efi-tpm-Add-EFI_CC_MEASUREMENT_PROTOCOL-support.patch
 create mode 100644 SOURCES/0279-font-Reject-glyphs-exceeds-font-max_glyph_width-or-f.patch
 create mode 100644 SOURCES/0280-font-Fix-size-overflow-in-grub_font_get_glyph_intern.patch
 create mode 100644 SOURCES/0281-font-Fix-several-integer-overflows-in-grub_font_cons.patch
 create mode 100644 SOURCES/0282-font-Remove-grub_font_dup_glyph.patch
 create mode 100644 SOURCES/0283-font-Fix-integer-overflow-in-ensure_comb_space.patch
 create mode 100644 SOURCES/0284-font-Fix-integer-overflow-in-BMP-index.patch
 create mode 100644 SOURCES/0285-font-Fix-integer-underflow-in-binary-search-of-char-.patch
 create mode 100644 SOURCES/0286-kern-efi-sb-Enforce-verification-of-font-files.patch
 create mode 100644 SOURCES/0287-fbutil-Fix-integer-overflow.patch
 create mode 100644 SOURCES/0288-font-Fix-an-integer-underflow-in-blit_comb.patch
 create mode 100644 SOURCES/0289-font-Harden-grub_font_blit_glyph-and-grub_font_blit_.patch
 create mode 100644 SOURCES/0290-font-Assign-null_font-to-glyphs-in-ascii_font_glyph.patch
 create mode 100644 SOURCES/0291-normal-charset-Fix-an-integer-overflow-in-grub_unico.patch
 create mode 100644 SOURCES/0292-font-Try-opening-fonts-from-the-bundled-memdisk.patch
 create mode 100644 SOURCES/0293-mm-Clarify-grub_real_malloc.patch
 create mode 100644 SOURCES/0294-mm-grub_real_malloc-Make-small-allocs-comment-match-.patch
 create mode 100644 SOURCES/0295-mm-Document-grub_free.patch
 create mode 100644 SOURCES/0296-mm-Document-grub_mm_init_region.patch
 create mode 100644 SOURCES/0297-mm-Document-GRUB-internal-memory-management-structur.patch
 create mode 100644 SOURCES/0298-mm-Assert-that-we-preserve-header-vs-region-alignmen.patch
 create mode 100644 SOURCES/0299-mm-When-adding-a-region-merge-with-region-after-as-w.patch
 create mode 100644 SOURCES/0300-mm-Debug-support-for-region-operations.patch
 create mode 100644 SOURCES/0301-mm-Drop-unused-unloading-of-modules-on-OOM.patch
 create mode 100644 SOURCES/0302-mm-Allow-dynamically-requesting-additional-memory-re.patch
 create mode 100644 SOURCES/0303-kern-efi-mm-Always-request-a-fixed-number-of-pages-o.patch
 create mode 100644 SOURCES/0304-kern-efi-mm-Extract-function-to-add-memory-regions.patch
 create mode 100644 SOURCES/0305-kern-efi-mm-Pass-up-errors-from-add_memory_regions.patch
 create mode 100644 SOURCES/0306-kern-efi-mm-Implement-runtime-addition-of-pages.patch
 create mode 100644 SOURCES/0307-efi-Increase-default-memory-allocation-to-32-MiB.patch
 create mode 100644 SOURCES/0308-mm-Try-invalidate-disk-caches-last-when-out-of-memor.patch
 create mode 100644 SOURCES/0309-ppc64le-signed-boot-media-changes.patch
 create mode 100644 SOURCES/0310-core-Fix-several-implicit-function-declarations.patch
 create mode 100644 SOURCES/0311-loader-Add-support-for-grub-emu-to-kexec-Linux-menu-.patch
 create mode 100644 SOURCES/0312-powerpc-Drop-Open-Hack-Ware-remove-GRUB_IEEE1275_FLA.patch
 create mode 100644 SOURCES/0313-ieee1275-request-memory-with-ibm-client-architecture.patch
 create mode 100644 SOURCES/0314-ieee1275-drop-len-1-quirk-in-heap_init.patch
 create mode 100644 SOURCES/0315-ieee1275-support-runtime-memory-claiming.patch
 create mode 100644 SOURCES/0316-ieee1275-implement-vec5-for-cas-negotiation.patch
 create mode 100644 SOURCES/0317-ibmvtpm-Add-support-for-trusted-boot-using-a-vTPM-2..patch
 create mode 100644 SOURCES/0318-powerpc-Drop-Open-Hack-Ware.patch
 create mode 100644 SOURCES/0319-osdep-linux-hostdisk-Modify-sector-by-sysfs-as-disk-.patch
 create mode 100644 SOURCES/0320-mm-Adjust-new-region-size-to-take-management-overhea.patch
 create mode 100644 SOURCES/0321-mm-Preallocate-some-space-when-adding-new-regions.patch
 create mode 100644 SOURCES/0322-mm-Avoid-complex-heap-growth-math-in-hot-path.patch

diff --git a/.gitignore b/.gitignore
index 4acae17..dca6c5b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-SOURCES/gnulib-fixes.tar.gz
+SOURCES/gnulib-9f48fb992a3d7e96610c4ce8be969cff2d61a01b.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
index 88b5cc8..3b87954 100644
--- a/.grub2.metadata
+++ b/.grub2.metadata
@@ -1,4 +1,4 @@
-1a07692dfaa916675a92b1383885141f490f98d2 SOURCES/gnulib-fixes.tar.gz
+d08376d97163f99ce0d61fce160d6f7667c5c944 SOURCES/gnulib-9f48fb992a3d7e96610c4ce8be969cff2d61a01b.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/0007-Add-secureboot-support-on-efi-chainloader.patch b/SOURCES/0007-Add-secureboot-support-on-efi-chainloader.patch
index bfb5a9b..34ead63 100644
--- a/SOURCES/0007-Add-secureboot-support-on-efi-chainloader.patch
+++ b/SOURCES/0007-Add-secureboot-support-on-efi-chainloader.patch
@@ -164,18 +164,33 @@ following branches:
 
 Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1347291
 Signed-off-by: Laszlo Ersek <lersek@redhat.com>
+
+Also:
+
+commit cc06f149fbd2d8c1da1e83173d21629ba97e0d92
+Author: Raymund Will <rw@suse.com>
+
+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/kern/efi/efi.c           |  14 +-
  grub-core/loader/arm64/linux.c     |   4 +-
- grub-core/loader/efi/chainloader.c | 816 +++++++++++++++++++++++++++++++++----
+ grub-core/loader/efi/chainloader.c | 820 +++++++++++++++++++++++++++++++++----
  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(-)
+ 7 files changed, 844 insertions(+), 90 deletions(-)
 
 diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
-index 35b8f670602..4a2259aa1c7 100644
+index 35b8f67060..4a2259aa1c 100644
 --- a/grub-core/kern/efi/efi.c
 +++ b/grub-core/kern/efi/efi.c
 @@ -296,14 +296,20 @@ grub_efi_secure_boot (void)
@@ -204,7 +219,7 @@ index 35b8f670602..4a2259aa1c7 100644
    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
+index a312c66868..04994d5c67 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)),
@@ -226,7 +241,7 @@ index a312c668685..04994d5c67d 100644
        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
+index 2bd80f4db3..e6a8d4ad0e 100644
 --- a/grub-core/loader/efi/chainloader.c
 +++ b/grub-core/loader/efi/chainloader.c
 @@ -32,6 +32,8 @@
@@ -261,7 +276,7 @@ index 2bd80f4db3d..b54cf6986fc 100644
  
    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)
+@@ -213,20 +221,694 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename)
    return file_path;
  }
  
@@ -376,6 +391,10 @@ index 2bd80f4db3d..b54cf6986fc 100644
 +  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
@@ -955,7 +974,7 @@ index 2bd80f4db3d..b54cf6986fc 100644
  
    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)),
+@@ -238,15 +920,45 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
    address = 0;
    image_handle = 0;
    file_path = 0;
@@ -1003,7 +1022,7 @@ index 2bd80f4db3d..b54cf6986fc 100644
    if (! dev)
      goto fail;
  
-@@ -283,17 +991,14 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+@@ -283,17 +995,14 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
    if (! file_path)
      goto fail;
  
@@ -1024,7 +1043,7 @@ index 2bd80f4db3d..b54cf6986fc 100644
  
    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)),
+@@ -307,7 +1016,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
      }
  
    boot_image = (void *) ((grub_addr_t) address);
@@ -1033,7 +1052,7 @@ index 2bd80f4db3d..b54cf6986fc 100644
      {
        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)),
+@@ -317,7 +1026,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
      }
  
  #if defined (__i386__) || defined (__x86_64__)
@@ -1042,7 +1061,7 @@ index 2bd80f4db3d..b54cf6986fc 100644
      {
        struct grub_macho_fat_header *head = boot_image;
        if (head->magic
-@@ -326,6 +1031,14 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+@@ -326,6 +1035,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);
@@ -1057,7 +1076,7 @@ index 2bd80f4db3d..b54cf6986fc 100644
  	  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)),
+@@ -340,79 +1057,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)
@@ -1154,7 +1173,7 @@ index 2bd80f4db3d..b54cf6986fc 100644
    if (dev)
      grub_device_close (dev);
  
-@@ -424,6 +1097,9 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+@@ -424,6 +1101,9 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
    if (address)
      efi_call_2 (b->free_pages, address, pages);
  
@@ -1165,7 +1184,7 @@ index 2bd80f4db3d..b54cf6986fc 100644
  
    return grub_errno;
 diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c
-index c24202a5dd1..c8ecce6dfd0 100644
+index c24202a5dd..c8ecce6dfd 100644
 --- a/grub-core/loader/efi/linux.c
 +++ b/grub-core/loader/efi/linux.c
 @@ -33,21 +33,34 @@ struct grub_efi_shim_lock
@@ -1210,7 +1229,7 @@ index c24202a5dd1..c8ecce6dfd0 100644
  
  #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
+index bb2616a809..6b24cbb948 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)),
@@ -1278,7 +1297,7 @@ index bb2616a8092..6b24cbb9483 100644
      grub_file_close (file);
  
 diff --git a/include/grub/efi/linux.h b/include/grub/efi/linux.h
-index d9ede36773b..0033d9305a9 100644
+index d9ede36773..0033d9305a 100644
 --- a/include/grub/efi/linux.h
 +++ b/include/grub/efi/linux.h
 @@ -22,7 +22,7 @@
@@ -1291,7 +1310,7 @@ index d9ede36773b..0033d9305a9 100644
  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
+index 0ed8781f03..a43adf2746 100644
 --- a/include/grub/efi/pe32.h
 +++ b/include/grub/efi/pe32.h
 @@ -223,7 +223,11 @@ struct grub_pe64_optional_header
diff --git a/SOURCES/0010-re-write-.gitignore.patch b/SOURCES/0010-re-write-.gitignore.patch
index 74cb0da..b6fa0be 100644
--- a/SOURCES/0010-re-write-.gitignore.patch
+++ b/SOURCES/0010-re-write-.gitignore.patch
@@ -4,14 +4,14 @@ Date: Mon, 8 Jul 2019 12:55:29 +0200
 Subject: [PATCH] re-write .gitignore
 
 ---
- .gitignore                        | 152 ++++++++++++++++++++++++++++++++++++++
+ .gitignore                        | 150 ++++++++++++++++++++++++++++++++++++++
  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(+)
+ 7 files changed, 181 insertions(+)
  create mode 100644 docs/.gitignore
  create mode 100644 grub-core/.gitignore
  create mode 100644 grub-core/lib/.gitignore
@@ -20,10 +20,10 @@ Subject: [PATCH] re-write .gitignore
  create mode 100644 util/bash-completion.d/.gitignore
 
 diff --git a/.gitignore b/.gitignore
-index f6a1bd05175..594d0134d33 100644
+index f6a1bd0517..208d1d2325 100644
 --- a/.gitignore
 +++ b/.gitignore
-@@ -275,3 +275,155 @@ widthspec.bin
+@@ -275,3 +275,153 @@ widthspec.bin
  /xfs_test
  /xzcompress_test
  /zfs_test
@@ -152,8 +152,6 @@ index f6a1bd05175..594d0134d33 100644
 +/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
@@ -181,7 +179,7 @@ index f6a1bd05175..594d0134d33 100644
 +/widthspec.h
 diff --git a/docs/.gitignore b/docs/.gitignore
 new file mode 100644
-index 00000000000..e1d849ef95b
+index 0000000000..e1d849ef95
 --- /dev/null
 +++ b/docs/.gitignore
 @@ -0,0 +1,5 @@
@@ -192,7 +190,7 @@ index 00000000000..e1d849ef95b
 +/version*.texi
 diff --git a/grub-core/.gitignore b/grub-core/.gitignore
 new file mode 100644
-index 00000000000..2acce281159
+index 0000000000..2acce28115
 --- /dev/null
 +++ b/grub-core/.gitignore
 @@ -0,0 +1,16 @@
@@ -214,14 +212,14 @@ index 00000000000..2acce281159
 +/trigtables.c
 diff --git a/grub-core/lib/.gitignore b/grub-core/lib/.gitignore
 new file mode 100644
-index 00000000000..68154591404
+index 0000000000..6815459140
 --- /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
+index 0000000000..8fbf564624
 --- /dev/null
 +++ b/include/grub/gcrypt/.gitignore
 @@ -0,0 +1,2 @@
@@ -229,7 +227,7 @@ index 00000000000..8fbf5646246
 +gcrypt.h
 diff --git a/po/.gitignore b/po/.gitignore
 new file mode 100644
-index 00000000000..f507e7741e3
+index 0000000000..f507e7741e
 --- /dev/null
 +++ b/po/.gitignore
 @@ -0,0 +1,5 @@
@@ -240,7 +238,7 @@ index 00000000000..f507e7741e3
 +/stamp-po
 diff --git a/util/bash-completion.d/.gitignore b/util/bash-completion.d/.gitignore
 new file mode 100644
-index 00000000000..6813a527ad3
+index 0000000000..6813a527ad
 --- /dev/null
 +++ b/util/bash-completion.d/.gitignore
 @@ -0,0 +1,2 @@
diff --git a/SOURCES/0011-IBM-client-architecture-CAS-reboot-support.patch b/SOURCES/0011-IBM-client-architecture-CAS-reboot-support.patch
index eaaf9a4..316b9eb 100644
--- a/SOURCES/0011-IBM-client-architecture-CAS-reboot-support.patch
+++ b/SOURCES/0011-IBM-client-architecture-CAS-reboot-support.patch
@@ -7,16 +7,20 @@ 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:
+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
+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
+
+Signed-off-by: Paulo Flabiano Smorigo <pfsmorigo@br.ibm.com>
+[rharwood@redhat.com: commit message rewrap]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
 ---
  grub-core/kern/ieee1275/openfw.c | 63 ++++++++++++++++++++++++++++++++++++++++
  grub-core/normal/main.c          | 19 ++++++++++++
@@ -25,7 +29,7 @@ parameters
  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
+index 4d493ab766..3a6689abb1 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)
@@ -96,7 +100,7 @@ index 4d493ab7661..3a6689abb11 100644
 +  return 0;
 +}
 diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
-index c4ebe9e22ad..70614de1565 100644
+index c4ebe9e22a..70614de156 100644
 --- a/grub-core/normal/main.c
 +++ b/grub-core/normal/main.c
 @@ -34,6 +34,9 @@
@@ -133,7 +137,7 @@ index c4ebe9e22ad..70614de1565 100644
        grub_errno = GRUB_ERR_NONE;
      }
 diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c
-index 25158407dd8..ad80399246a 100644
+index 25158407dd..ad80399246 100644
 --- a/grub-core/script/execute.c
 +++ b/grub-core/script/execute.c
 @@ -28,6 +28,9 @@
@@ -158,7 +162,7 @@ index 25158407dd8..ad80399246a 100644
      {
        char *line;
 diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h
-index 73e2f464475..0a599607f31 100644
+index 73e2f46447..0a599607f3 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
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
index c8bb9d3..121af84 100644
--- 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
@@ -10,12 +10,14 @@ This should fix this bugzilla:
 https://bugzilla.redhat.com/show_bug.cgi?id=908519
 
 Signed-off-by: Peter Jones <pjones@redhat.com>
+Signed-off-by: Paulo Flabiano Smorigo <pfsmorigo@br.ibm.com>
+Signed-off-by: Robbie Harwood <rharwood@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
+index 85ecf06b4d..05c88dcf49 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,
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
index 430f600..917943f 100644
--- a/SOURCES/0013-Disable-GRUB-video-support-for-IBM-power-machines.patch
+++ b/SOURCES/0013-Disable-GRUB-video-support-for-IBM-power-machines.patch
@@ -5,6 +5,9 @@ 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
+
+Signed-off-by: Paulo Flabiano Smorigo <pfsmorigo@br.ibm.com>
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
 ---
  grub-core/kern/ieee1275/cmain.c  | 5 ++++-
  grub-core/video/ieee1275.c       | 9 ++++++---
@@ -12,7 +15,7 @@ https://bugzilla.redhat.com/show_bug.cgi?id=973205
  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
+index 20cbbd761e..04df9d2c66 100644
 --- a/grub-core/kern/ieee1275/cmain.c
 +++ b/grub-core/kern/ieee1275/cmain.c
 @@ -90,7 +90,10 @@ grub_ieee1275_find_options (void)
@@ -28,7 +31,7 @@ index 20cbbd761ec..04df9d2c667 100644
    /* 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
+index 17a3dbbb57..b8e4b3feb3 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 =
@@ -48,7 +51,7 @@ index 17a3dbbb575..b8e4b3feb32 100644
  
  GRUB_MOD_FINI(ieee1275_fb)
 diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h
-index 0a599607f31..b5a1d49bbc3 100644
+index 0a599607f3..b5a1d49bbc 100644
 --- a/include/grub/ieee1275/ieee1275.h
 +++ b/include/grub/ieee1275/ieee1275.h
 @@ -148,6 +148,8 @@ enum grub_ieee1275_flag
diff --git a/SOURCES/0018-Migrate-PPC-from-Yaboot-to-Grub2.patch b/SOURCES/0018-Migrate-PPC-from-Yaboot-to-Grub2.patch
index ee8c7ab..1e310e9 100644
--- a/SOURCES/0018-Migrate-PPC-from-Yaboot-to-Grub2.patch
+++ b/SOURCES/0018-Migrate-PPC-from-Yaboot-to-Grub2.patch
@@ -3,8 +3,11 @@ 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.
+Add configuration support for serial terminal consoles.  This will set
+the maximum screen size so that text is not overwritten.
+
+Signed-off-by: Mark Hamzy <hamzy@us.ibm.com>
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
 ---
  Makefile.util.def              |   7 +++
  util/grub.d/20_ppc_terminfo.in | 114 +++++++++++++++++++++++++++++++++++++++++
@@ -12,7 +15,7 @@ maximum screen size so that text is not overwritten.
  create mode 100644 util/grub.d/20_ppc_terminfo.in
 
 diff --git a/Makefile.util.def b/Makefile.util.def
-index f8b356cc1fa..2c9b283a230 100644
+index f8b356cc1f..2c9b283a23 100644
 --- a/Makefile.util.def
 +++ b/Makefile.util.def
 @@ -508,6 +508,13 @@ script = {
@@ -31,7 +34,7 @@ index f8b356cc1fa..2c9b283a230 100644
    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
+index 0000000000..10d6658682
 --- /dev/null
 +++ b/util/grub.d/20_ppc_terminfo.in
 @@ -0,0 +1,114 @@
diff --git a/SOURCES/0019-Add-fw_path-variable-revised.patch b/SOURCES/0019-Add-fw_path-variable-revised.patch
index df9ef83..804938b 100644
--- a/SOURCES/0019-Add-fw_path-variable-revised.patch
+++ b/SOURCES/0019-Add-fw_path-variable-revised.patch
@@ -8,13 +8,16 @@ 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
+
+Signed-off-by: Paulo Flabiano Smorigo <pfsmorigo@br.ibm.com>
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
 ---
  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
+index 73967e2f5b..d1de9fa687 100644
 --- a/grub-core/kern/main.c
 +++ b/grub-core/kern/main.c
 @@ -128,16 +128,15 @@ grub_set_prefix_and_root (void)
@@ -41,7 +44,7 @@ index 73967e2f5b0..d1de9fa6873 100644
      }
  
 diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
-index 70614de1565..62571e6dfcc 100644
+index 70614de156..62571e6dfc 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)),
diff --git a/SOURCES/0023-Enable-pager-by-default.-985860.patch b/SOURCES/0023-Enable-pager-by-default.-985860.patch
new file mode 100644
index 0000000..63cf787
--- /dev/null
+++ b/SOURCES/0023-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 93a90233ea..858b526c92 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/0024-Don-t-say-GNU-Linux-in-generated-menus.patch b/SOURCES/0024-Don-t-say-GNU-Linux-in-generated-menus.patch
new file mode 100644
index 0000000..5b96784
--- /dev/null
+++ b/SOURCES/0024-Don-t-say-GNU-Linux-in-generated-menus.patch
@@ -0,0 +1,85 @@
+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.
+
+[rharwood: say it even less]
+---
+ grub-core/normal/main.c         | 2 +-
+ tests/util/grub-shell-tester.in | 2 +-
+ tests/util/grub-shell.in        | 2 +-
+ util/grub.d/10_linux.in         | 4 ++--
+ util/grub.d/20_linux_xen.in     | 4 ++--
+ 5 files changed, 7 insertions(+), 7 deletions(-)
+
+diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
+index 7ca2e5400b..98372217ad 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -218,7 +218,7 @@ grub_normal_init_page (struct grub_term_output *term,
+  
+   grub_term_cls (term);
+ 
+-  msg_formatted = grub_xasprintf (_("GNU GRUB  version %s"), PACKAGE_VERSION);
++  msg_formatted = grub_xasprintf (_("GRUB version %s"), PACKAGE_VERSION);
+   if (!msg_formatted)
+     return;
+  
+diff --git a/tests/util/grub-shell-tester.in b/tests/util/grub-shell-tester.in
+index 8a87109b15..9a4319d4f4 100644
+--- a/tests/util/grub-shell-tester.in
++++ b/tests/util/grub-shell-tester.in
+@@ -56,7 +56,7 @@ for option in "$@"; do
+ 	usage
+ 	exit 0 ;;
+     -v | --version)
+-	echo "$0 (GNU GRUB ${PACKAGE_VERSION})"
++	echo "$0 (GRUB ${PACKAGE_VERSION})"
+ 	exit 0 ;;
+     --modules=*)
+ 	ms=`echo "$option" | sed -e 's/--modules=//'`
+diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in
+index 93e9f51484..ec1182bf93 100644
+--- a/tests/util/grub-shell.in
++++ b/tests/util/grub-shell.in
+@@ -209,7 +209,7 @@ for option in "$@"; do
+ 	usage
+ 	exit 0 ;;
+     -v | --version)
+-	echo "$0 (GNU GRUB ${PACKAGE_VERSION})"
++	echo "$0 (GRUB ${PACKAGE_VERSION})"
+ 	exit 0 ;;
+     --trim)
+ 	trim=1
+diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
+index dc75a1c30b..4a499c53a6 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 3b1f470492..ada20775a1 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/0025-Add-.eh_frame-to-list-of-relocations-stripped.patch b/SOURCES/0025-Add-.eh_frame-to-list-of-relocations-stripped.patch
new file mode 100644
index 0000000..0532a04
--- /dev/null
+++ b/SOURCES/0025-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 2a1a886f6d..191b1a70c6 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/0026-Don-t-require-a-password-to-boot-entries-generated-b.patch b/SOURCES/0026-Don-t-require-a-password-to-boot-entries-generated-b.patch
new file mode 100644
index 0000000..89887c1
--- /dev/null
+++ b/SOURCES/0026-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 4a499c53a6..cf8d118698 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/0027-use-fw_path-prefix-when-fallback-searching-for-grub-.patch b/SOURCES/0027-use-fw_path-prefix-when-fallback-searching-for-grub-.patch
new file mode 100644
index 0000000..1da91e1
--- /dev/null
+++ b/SOURCES/0027-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 98372217ad..bf24e65713 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -347,7 +347,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);
+@@ -370,7 +370,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/0028-Try-mac-guid-etc-before-grub.cfg-on-tftp-config-file.patch b/SOURCES/0028-Try-mac-guid-etc-before-grub.cfg-on-tftp-config-file.patch
new file mode 100644
index 0000000..0d77281
--- /dev/null
+++ b/SOURCES/0028-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 bf24e65713..0a99768f75 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -345,61 +345,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/0029-Generate-OS-and-CLASS-in-10_linux-from-etc-os-releas.patch b/SOURCES/0029-Generate-OS-and-CLASS-in-10_linux-from-etc-os-releas.patch
new file mode 100644
index 0000000..f22e6f9
--- /dev/null
+++ b/SOURCES/0029-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 cf8d118698..5f6d3c8d52 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/0030-Minimize-the-sort-ordering-for-.debug-and-rescue-ker.patch b/SOURCES/0030-Minimize-the-sort-ordering-for-.debug-and-rescue-ker.patch
new file mode 100644
index 0000000..f4d83da
--- /dev/null
+++ b/SOURCES/0030-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 301d1ac229..0f6505bf3b 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/0031-Try-prefix-if-fw_path-doesn-t-work.patch b/SOURCES/0031-Try-prefix-if-fw_path-doesn-t-work.patch
new file mode 100644
index 0000000..a1c0d9e
--- /dev/null
+++ b/SOURCES/0031-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 e71d158416..0cd2a62723 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 4d3eb5c1a5..0ef148f4ad 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 0a99768f75..55558cc0b9 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -335,81 +335,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/0032-Make-grub2-mkconfig-construct-titles-that-look-like-.patch b/SOURCES/0032-Make-grub2-mkconfig-construct-titles-that-look-like-.patch
new file mode 100644
index 0000000..db101c0
--- /dev/null
+++ b/SOURCES/0032-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 5f6d3c8d52..786dbabb4a 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/0033-Add-friendly-grub2-password-config-tool-985962.patch b/SOURCES/0033-Add-friendly-grub2-password-config-tool-985962.patch
new file mode 100644
index 0000000..5dabeeb
--- /dev/null
+++ b/SOURCES/0033-Add-friendly-grub2-password-config-tool-985962.patch
@@ -0,0 +1,245 @@
+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]
+Signed-off-by: Andy Lutomirski <luto@kernel.org>
+[rharwood: migrate man page to h2m, context]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ configure.ac                   |   1 +
+ Makefile.util.def              |  13 +++++
+ docs/man/grub-set-password.h2m |   2 +
+ util/grub-mkconfig.in          |   2 +
+ util/grub-set-password.in      | 128 +++++++++++++++++++++++++++++++++++++++++
+ util/grub.d/01_users.in        |  11 ++++
+ 6 files changed, 157 insertions(+)
+ create mode 100644 docs/man/grub-set-password.h2m
+ 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 8331f95b64..7f59ad788f 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-script-check])
+ grub_TRANSFORM([grub-set-default])
+ grub_TRANSFORM([grub-sparc64-setup])
+diff --git a/Makefile.util.def b/Makefile.util.def
+index 2c9b283a23..4ee22c5daa 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;
+@@ -724,6 +730,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/docs/man/grub-set-password.h2m b/docs/man/grub-set-password.h2m
+new file mode 100644
+index 0000000000..10ee82f4d5
+--- /dev/null
++++ b/docs/man/grub-set-password.h2m
+@@ -0,0 +1,2 @@
++[NAME]
++grub-set-password \- generate the user.cfg file containing the hashed grub bootloader password
+diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in
+index 8ea2315ebc..ba14cf6261 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.in b/util/grub-set-password.in
+new file mode 100644
+index 0000000000..5ebf50576d
+--- /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://bugs.rockylinux.org.
++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 0000000000..db2f44bfb7
+--- /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/0034-tcp-add-window-scaling-support.patch b/SOURCES/0034-tcp-add-window-scaling-support.patch
new file mode 100644
index 0000000..61b5a67
--- /dev/null
+++ b/SOURCES/0034-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 e8ad34b84d..7d4b822626 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/0035-efinet-and-bootp-add-support-for-dhcpv6.patch b/SOURCES/0035-efinet-and-bootp-add-support-for-dhcpv6.patch
new file mode 100644
index 0000000..efeeee0
--- /dev/null
+++ b/SOURCES/0035-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 6fb5627025..e28fb6a09f 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 5388f952ba..173fb63153 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 0ef148f4ad..22f2689aae 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 7f44b30f52..4ab2f5c735 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 f1a52210c0..117469450d 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 7ae4b6bd80..8a05ec4fe7 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/0036-Add-grub-get-kernel-settings-and-use-it-in-10_linux.patch b/SOURCES/0036-Add-grub-get-kernel-settings-and-use-it-in-10_linux.patch
new file mode 100644
index 0000000..95490cd
--- /dev/null
+++ b/SOURCES/0036-Add-grub-get-kernel-settings-and-use-it-in-10_linux.patch
@@ -0,0 +1,271 @@
+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
+[rharwood: migrate man page to h2m]
+---
+ configure.ac                                   |  1 +
+ Makefile.util.def                              |  7 ++
+ docs/man/grub-get-kernel-settings.h2m          |  2 +
+ util/bash-completion.d/grub-completion.bash.in | 22 +++++++
+ util/grub-get-kernel-settings.in               | 88 ++++++++++++++++++++++++++
+ util/grub-mkconfig.in                          |  3 +
+ util/grub.d/10_linux.in                        | 23 +++++--
+ 7 files changed, 141 insertions(+), 5 deletions(-)
+ create mode 100644 docs/man/grub-get-kernel-settings.h2m
+ create mode 100644 util/grub-get-kernel-settings.in
+
+diff --git a/configure.ac b/configure.ac
+index 7f59ad788f..0d0e6782a1 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])
+diff --git a/Makefile.util.def b/Makefile.util.def
+index 4ee22c5daa..18a9242776 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -716,6 +716,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/docs/man/grub-get-kernel-settings.h2m b/docs/man/grub-get-kernel-settings.h2m
+new file mode 100644
+index 0000000000..b8051f01f3
+--- /dev/null
++++ b/docs/man/grub-get-kernel-settings.h2m
+@@ -0,0 +1,2 @@
++[NAME]
++grub-get-kernel-settings \- Evaluate the system's kernel installation settings for use while making a grub configuration file
+diff --git a/util/bash-completion.d/grub-completion.bash.in b/util/bash-completion.d/grub-completion.bash.in
+index 44bf135b9f..5c4acd496d 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.in b/util/grub-get-kernel-settings.in
+new file mode 100644
+index 0000000000..7e87dfccc0
+--- /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 ba14cf6261..005f093809 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 786dbabb4a..292e333324 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/"
+@@ -306,11 +310,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
+@@ -319,10 +327,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/0037-bz1374141-fix-incorrect-mask-for-ppc64.patch b/SOURCES/0037-bz1374141-fix-incorrect-mask-for-ppc64.patch
new file mode 100644
index 0000000..15b2111
--- /dev/null
+++ b/SOURCES/0037-bz1374141-fix-incorrect-mask-for-ppc64.patch
@@ -0,0 +1,48 @@
+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)
+
+Signed-off-by: Masahiro Matsuya <mmatsuya@redhat.com>
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ 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 ac4e62a95c..3860b6f78d 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/0038-Make-grub_fatal-also-backtrace.patch b/SOURCES/0038-Make-grub_fatal-also-backtrace.patch
new file mode 100644
index 0000000..f876575
--- /dev/null
+++ b/SOURCES/0038-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 c15e91943b..058c88ac3a 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 63b586d09c..a3e215155b 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 0000000000..1079b5380e
+--- /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 825a8800e2..c0ad6ab8be 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 c3e03c7275..c67273db3a 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/0039-Make-our-info-pages-say-grub2-where-appropriate.patch b/SOURCES/0039-Make-our-info-pages-say-grub2-where-appropriate.patch
new file mode 100644
index 0000000..afba285
--- /dev/null
+++ b/SOURCES/0039-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 6c629a23e2..19f708ee66 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 69f08d289f..0615d0ed97 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/0040-macos-just-build-chainloader-entries-don-t-try-any-x.patch b/SOURCES/0040-macos-just-build-chainloader-entries-don-t-try-any-x.patch
new file mode 100644
index 0000000..8d3139d
--- /dev/null
+++ b/SOURCES/0040-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 1b91c102f3..4b27bd2015 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/0041-grub2-btrfs-Add-ability-to-boot-from-subvolumes.patch b/SOURCES/0041-grub2-btrfs-Add-ability-to-boot-from-subvolumes.patch
new file mode 100644
index 0000000..57d2391
--- /dev/null
+++ b/SOURCES/0041-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 63203034df..f1fff7385b 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 9d93fb6c18..234ad97677 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/0042-export-btrfs_subvol-and-btrfs_subvolid.patch b/SOURCES/0042-export-btrfs_subvol-and-btrfs_subvolid.patch
new file mode 100644
index 0000000..719866e
--- /dev/null
+++ b/SOURCES/0042-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 f1fff7385b..ad1b56b716 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/0043-grub2-btrfs-03-follow_default.patch b/SOURCES/0043-grub2-btrfs-03-follow_default.patch
new file mode 100644
index 0000000..621f029
--- /dev/null
+++ b/SOURCES/0043-grub2-btrfs-03-follow_default.patch
@@ -0,0 +1,198 @@
+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
+
+Signed-off-by: Michael Chang <mchang@suse.com>
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ 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 ad1b56b716..113c1f746c 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/0044-grub2-btrfs-04-grub2-install.patch b/SOURCES/0044-grub2-btrfs-04-grub2-install.patch
new file mode 100644
index 0000000..6b2bc78
--- /dev/null
+++ b/SOURCES/0044-grub2-btrfs-04-grub2-install.patch
@@ -0,0 +1,176 @@
+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
+
+Signed-off-by: Michael Chang <mchang@suse.com>
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ 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 001b818fe5..caf9b1ccd3 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 7d6325138c..46a881530c 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 ebcdd8f5e2..f044a880a7 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 0fbe7f78c6..0f66f36d23 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 47a241a391..5db7a9a7d9 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 875d5896ce..c9a7e5f4ad 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/0045-grub2-btrfs-05-grub2-mkconfig.patch b/SOURCES/0045-grub2-btrfs-05-grub2-mkconfig.patch
new file mode 100644
index 0000000..ca3f86b
--- /dev/null
+++ b/SOURCES/0045-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 005f093809..535c0f0249 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 0f6505bf3b..5e96f6cc5d 100644
+--- a/util/grub-mkconfig_lib.in
++++ b/util/grub-mkconfig_lib.in
+@@ -49,7 +49,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 858b526c92..de727e6ee6 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 292e333324..7bb3a211a7 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 ada20775a1..e9e73b815f 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/0046-grub2-btrfs-06-subvol-mount.patch b/SOURCES/0046-grub2-btrfs-06-subvol-mount.patch
new file mode 100644
index 0000000..8cdf247
--- /dev/null
+++ b/SOURCES/0046-grub2-btrfs-06-subvol-mount.patch
@@ -0,0 +1,539 @@
+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
+
+Signed-off-by: Michael Chang <mchang@suse.com>
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ 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 113c1f746c..d323746ecf 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 caf9b1ccd3..28790307e0 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 0f66f36d23..84ed6e88ec 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 73fa2d34ab..9c642ae3fe 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/0047-Fallback-to-old-subvol-name-scheme-to-support-old-sn.patch b/SOURCES/0047-Fallback-to-old-subvol-name-scheme-to-support-old-sn.patch
new file mode 100644
index 0000000..a5db09a
--- /dev/null
+++ b/SOURCES/0047-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 d323746ecf..673ded0352 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/0048-Grub-not-working-correctly-with-btrfs-snapshots-bsc-.patch b/SOURCES/0048-Grub-not-working-correctly-with-btrfs-snapshots-bsc-.patch
new file mode 100644
index 0000000..99c106d
--- /dev/null
+++ b/SOURCES/0048-Grub-not-working-correctly-with-btrfs-snapshots-bsc-.patch
@@ -0,0 +1,274 @@
+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)
+
+Signed-off-by: Michael Chang <mchang@suse.com>
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ 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 673ded0352..2b21cbaa67 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/0049-Add-grub_efi_allocate_pool-and-grub_efi_free_pool-wr.patch b/SOURCES/0049-Add-grub_efi_allocate_pool-and-grub_efi_free_pool-wr.patch
new file mode 100644
index 0000000..d07dd27
--- /dev/null
+++ b/SOURCES/0049-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 585fa6662b..03f9a9d011 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/0050-Use-grub_efi_.-memory-helpers-where-reasonable.patch b/SOURCES/0050-Use-grub_efi_.-memory-helpers-where-reasonable.patch
new file mode 100644
index 0000000..2a3d27b
--- /dev/null
+++ b/SOURCES/0050-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 07c4937898..89ac84cc66 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 ();
+ 
+@@ -527,10 +527,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;
+@@ -541,8 +540,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)
+     {
+@@ -582,8 +579,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)
+     {
+@@ -815,14 +812,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;
+ }
+@@ -830,10 +827,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;
+@@ -1100,7 +1094,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/0051-Add-PRIxGRUB_EFI_STATUS-and-use-it.patch b/SOURCES/0051-Add-PRIxGRUB_EFI_STATUS-and-use-it.patch
new file mode 100644
index 0000000..f649c8c
--- /dev/null
+++ b/SOURCES/0051-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 89ac84cc66..ac8dfd40c6 100644
+--- a/grub-core/loader/efi/chainloader.c
++++ b/grub-core/loader/efi/chainloader.c
+@@ -810,7 +810,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 117469450d..9962880147 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/0052-don-t-use-int-for-efi-status.patch b/SOURCES/0052-don-t-use-int-for-efi-status.patch
new file mode 100644
index 0000000..4d48e37
--- /dev/null
+++ b/SOURCES/0052-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 05d8237a9b..ae9885edb8 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/0053-make-GRUB_MOD_INIT-declare-its-function-prototypes.patch b/SOURCES/0053-make-GRUB_MOD_INIT-declare-its-function-prototypes.patch
new file mode 100644
index 0000000..fb71ea5
--- /dev/null
+++ b/SOURCES/0053-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 b3753c9ca2..91933b85f2 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/0054-Don-t-guess-boot-efi-as-HFS-on-ppc-machines-in-grub-.patch b/SOURCES/0054-Don-t-guess-boot-efi-as-HFS-on-ppc-machines-in-grub-.patch
new file mode 100644
index 0000000..ae8a8c6
--- /dev/null
+++ b/SOURCES/0054-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 84ed6e88ec..a2bec7446c 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/0055-20_linux_xen-load-xen-or-multiboot-2-modules-as-need.patch b/SOURCES/0055-20_linux_xen-load-xen-or-multiboot-2-modules-as-need.patch
new file mode 100644
index 0000000..cd58ff4
--- /dev/null
+++ b/SOURCES/0055-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 e9e73b815f..c23b064be6 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/0056-Make-pmtimer-tsc-calibration-not-take-51-seconds-to-.patch b/SOURCES/0056-Make-pmtimer-tsc-calibration-not-take-51-seconds-to-.patch
new file mode 100644
index 0000000..beef0f3
--- /dev/null
+++ b/SOURCES/0056-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 c9c3616997..ca15c3aacd 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/0057-align-struct-efi_variable-better.patch b/SOURCES/0057-align-struct-efi_variable-better.patch
new file mode 100644
index 0000000..361cb13
--- /dev/null
+++ b/SOURCES/0057-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 36d2dedf47..9d93ba88ba 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 0a3ff15913..ba446d9904 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/0058-Add-BLS-support-to-grub-mkconfig.patch b/SOURCES/0058-Add-BLS-support-to-grub-mkconfig.patch
new file mode 100644
index 0000000..3e5b230
--- /dev/null
+++ b/SOURCES/0058-Add-BLS-support-to-grub-mkconfig.patch
@@ -0,0 +1,382 @@
+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>
+[javierm: remove outdated URL for BLS document]
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+[iwienand@redhat.com: skip machine ID check when updating entries]
+Signed-off-by: Ian Wienand <iwienand@redhat.com>
+[rharwood: use sort(1), commit message composits, drop man pages]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ util/grub-mkconfig.in     |   9 +-
+ util/grub-mkconfig_lib.in |  22 ++++-
+ util/grub.d/10_linux.in   | 218 +++++++++++++++++++++++++++++++++++++++++++++-
+ 3 files changed, 243 insertions(+), 6 deletions(-)
+
+diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in
+index 535c0f0249..f55339a3f6 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 5e96f6cc5d..301d8a8a1e 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
+@@ -122,8 +125,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='
+ '
+@@ -158,18 +172,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 7bb3a211a7..2851952659 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -82,6 +82,218 @@ 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://systemd.io/BOOT_LOADER_SPECIFICATION/.
++
++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}" ]; then
++        return
++    fi
++
++    local IFS=$'\n'
++
++    files=($(for bls in ${blsdir}/*.conf; do
++        if ! [[ -e "${bls}" ]] ; then
++            continue
++        fi
++        bls="${bls%.conf}"
++        bls="${bls##*/}"
++        echo "${bls}"
++    done | sort -Vr 2>/dev/null)) || :
++
++    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}" ] && ! command -v new-kernel-pkg >/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 +333,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
+@@ -231,6 +444,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`
+@@ -269,7 +483,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/0059-Don-t-attempt-to-backtrace-on-grub_abort-for-grub-em.patch b/SOURCES/0059-Don-t-attempt-to-backtrace-on-grub_abort-for-grub-em.patch
new file mode 100644
index 0000000..1ac900c
--- /dev/null
+++ b/SOURCES/0059-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 a3e215155b..c60601b699 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/0060-Add-grub2-switch-to-blscfg.patch b/SOURCES/0060-Add-grub2-switch-to-blscfg.patch
new file mode 100644
index 0000000..e316116
--- /dev/null
+++ b/SOURCES/0060-Add-grub2-switch-to-blscfg.patch
@@ -0,0 +1,395 @@
+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>
+[rharwood: skip on ostree installations, migrate man to h2m]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ Makefile.util.def                  |   7 +
+ docs/man/grub-switch-to-blscfg.h2m |   2 +
+ util/grub-set-password.in          |   2 +-
+ util/grub-switch-to-blscfg.in      | 317 +++++++++++++++++++++++++++++++++++++
+ util/grub.d/10_linux.in            |   2 +-
+ 5 files changed, 328 insertions(+), 2 deletions(-)
+ create mode 100644 docs/man/grub-switch-to-blscfg.h2m
+ create mode 100644 util/grub-switch-to-blscfg.in
+
+diff --git a/Makefile.util.def b/Makefile.util.def
+index 18a9242776..88f55e35c4 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -1348,6 +1348,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/docs/man/grub-switch-to-blscfg.h2m b/docs/man/grub-switch-to-blscfg.h2m
+new file mode 100644
+index 0000000000..fa341426a5
+--- /dev/null
++++ b/docs/man/grub-switch-to-blscfg.h2m
+@@ -0,0 +1,2 @@
++[NAME]
++grub-switch-to-blscfg \- switch to using BLS config files
+diff --git a/util/grub-set-password.in b/util/grub-set-password.in
+index 5ebf50576d..c0b5ebbfdc 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.in b/util/grub-switch-to-blscfg.in
+new file mode 100644
+index 0000000000..a851424beb
+--- /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/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
+index 2851952659..e490e1a43a 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -138,7 +138,7 @@ blsdir="/boot/loader/entries"
+ 
+ get_sorted_bls()
+ {
+-    if ! [ -d "${blsdir}" ]; then
++    if ! [ -d "${blsdir}" ] || [ -f /run/ostree-booted ] || [ -d /ostree/repo ]; then
+         return
+     fi
+ 
diff --git a/SOURCES/0061-make-better-backtraces.patch b/SOURCES/0061-make-better-backtraces.patch
new file mode 100644
index 0000000..7fdcaf7
--- /dev/null
+++ b/SOURCES/0061-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 88f55e35c4..bda9fd1211 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 058c88ac3a..52ec0fafcd 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;
+@@ -2399,15 +2402,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 c0ad6ab8be..8b5ec3913b 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 b64acd70fe..99281472d3 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 0000000000..019c6fdfef
+--- /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 0000000000..4a82e865cc
+--- /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 7afb9e6f72..88d2077709 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 0000000000..2413f9a57d
+--- /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 27bc68b8a5..b51d0abfa6 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 0cd2a62723..937c1bc44c 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 c60601b699..a432a6be54 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 c070afc621..d8c8377578 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 1079b5380e..0000000000
+--- 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 c67273db3a..0000000000
+--- 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 395519762f..275cf85e2d 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 91933b85f2..2f76e6b043 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 abbca5ea33..300a9766cd 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 9f8265315a..f3bc41f9d0 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 3946fe8e18..5679a1d00a 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 666a7ee3c9..41676bdb2b 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 0d89858d9b..939f182fc7 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 d75c6d7cc7..8f2a593e52 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 03b916f053..701bf63abc 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 ee88e44e97..bfd29a3bf0 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/0062-normal-don-t-draw-our-startup-message-if-debug-is-se.patch b/SOURCES/0062-normal-don-t-draw-our-startup-message-if-debug-is-se.patch
new file mode 100644
index 0000000..a916299
--- /dev/null
+++ b/SOURCES/0062-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 55558cc0b9..af9792c963 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -430,6 +430,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/0063-Work-around-some-minor-include-path-weirdnesses.patch b/SOURCES/0063-Work-around-some-minor-include-path-weirdnesses.patch
new file mode 100644
index 0000000..c7ae8d0
--- /dev/null
+++ b/SOURCES/0063-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 0000000000..1592f6f76b
+--- /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 0000000000..9568933938
+--- /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 0000000000..9231375cb0
+--- /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 0000000000..dba9d8678d
+--- /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/0064-Make-it-possible-to-enabled-build-id-sha1.patch b/SOURCES/0064-Make-it-possible-to-enabled-build-id-sha1.patch
new file mode 100644
index 0000000..99d8055
--- /dev/null
+++ b/SOURCES/0064-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 0d0e6782a1..302300711f 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1442,7 +1442,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 6e14bb553c..21238fcfd0 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/0065-Add-grub_qdprintf-grub_dprintf-without-the-file-line.patch b/SOURCES/0065-Add-grub_qdprintf-grub_dprintf-without-the-file-line.patch
new file mode 100644
index 0000000..7658e9b
--- /dev/null
+++ b/SOURCES/0065-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 a432a6be54..9a2fae6398 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 fd18e6320b..3adc4036e3 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/0066-Make-a-gdb-dprintf-that-tells-us-load-addresses.patch b/SOURCES/0066-Make-a-gdb-dprintf-that-tells-us-load-addresses.patch
new file mode 100644
index 0000000..ccf34a5
--- /dev/null
+++ b/SOURCES/0066-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 88d2077709..9557254035 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 ae9885edb8..d6a2fb5778 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 6d39bd3ad2..2d12e6188f 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 03f9a9d011..2e0691454b 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/0067-Fixup-for-newer-compiler.patch b/SOURCES/0067-Fixup-for-newer-compiler.patch
new file mode 100644
index 0000000..ed23271
--- /dev/null
+++ b/SOURCES/0067-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 2b21cbaa67..4cc86e9b79 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 7a93f43291..8212697bf6 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/0068-Don-t-attempt-to-export-the-start-and-_start-symbols.patch b/SOURCES/0068-Don-t-attempt-to-export-the-start-and-_start-symbols.patch
new file mode 100644
index 0000000..9b78c00
--- /dev/null
+++ b/SOURCES/0068-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 300a9766cd..55849777ea 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/0069-Fixup-for-newer-compiler.patch b/SOURCES/0069-Fixup-for-newer-compiler.patch
new file mode 100644
index 0000000..167dca1
--- /dev/null
+++ b/SOURCES/0069-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 191b1a70c6..5f0ef96985 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/0070-Add-support-for-non-Ethernet-network-cards.patch b/SOURCES/0070-Add-support-for-non-Ethernet-network-cards.patch
new file mode 100644
index 0000000..fb44628
--- /dev/null
+++ b/SOURCES/0070-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 54306e3b16..67b409a8ac 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 e28fb6a09f..08b6b2b5d6 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 173fb63153..a673bea807 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 b194920861..5b6c5e16a6 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 3f4152d036..9f8fb4b6d2 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 3860b6f78d..bcb3f9ea02 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 056052e40d..22ebcbf211 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 4d7ceed6f9..9aae83a5eb 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 2cbd95dce2..56a3ec5c8e 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 ea5edf8f1f..a5896f6dc2 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 22f2689aae..a46f82362e 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 8a05ec4fe7..af0404db7e 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/0071-net-read-bracketed-ipv6-addrs-and-port-numbers.patch b/SOURCES/0071-net-read-bracketed-ipv6-addrs-and-port-numbers.patch
new file mode 100644
index 0000000..307e33c
--- /dev/null
+++ b/SOURCES/0071-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 b616cf40b1..12a2632ea5 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 a46f82362e..0ce5e675ed 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 4ab2f5c735..d54b13f09f 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 af0404db7e..d55d505a03 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/0072-bootp-New-net_bootp6-command.patch b/SOURCES/0072-bootp-New-net_bootp6-command.patch
new file mode 100644
index 0000000..bef3acd
--- /dev/null
+++ b/SOURCES/0072-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 08b6b2b5d6..fe93b80f1c 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 a673bea807..8e25680db0 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 a5896f6dc2..ce6bdc75c6 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 9962880147..7614b58dca 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 d55d505a03..543251f727 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/0073-efinet-UEFI-IPv6-PXE-support.patch b/SOURCES/0073-efinet-UEFI-IPv6-PXE-support.patch
new file mode 100644
index 0000000..988c178
--- /dev/null
+++ b/SOURCES/0073-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 8e25680db0..014e5bf980 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 7614b58dca..91ab528e4d 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/0074-grub.texi-Add-net_bootp6-doument.patch b/SOURCES/0074-grub.texi-Add-net_bootp6-doument.patch
new file mode 100644
index 0000000..b42e09b
--- /dev/null
+++ b/SOURCES/0074-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 0615d0ed97..04ed6ac1f0 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/0075-bootp-Add-processing-DHCPACK-packet-from-HTTP-Boot.patch b/SOURCES/0075-bootp-Add-processing-DHCPACK-packet-from-HTTP-Boot.patch
new file mode 100644
index 0000000..abff9eb
--- /dev/null
+++ b/SOURCES/0075-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 fe93b80f1c..8fb8918ae7 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 543251f727..42af7de250 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/0076-efinet-Setting-network-from-UEFI-device-path.patch b/SOURCES/0076-efinet-Setting-network-from-UEFI-device-path.patch
new file mode 100644
index 0000000..f4faf27
--- /dev/null
+++ b/SOURCES/0076-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 014e5bf980..8171ecaa5e 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 91ab528e4d..4a51667adb 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/0077-efinet-Setting-DNS-server-from-UEFI-protocol.patch b/SOURCES/0077-efinet-Setting-DNS-server-from-UEFI-protocol.patch
new file mode 100644
index 0000000..2d92ff0
--- /dev/null
+++ b/SOURCES/0077-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 8171ecaa5e..715a6168d7 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 4a51667adb..0b490195ad 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/0078-Support-UEFI-networking-protocols.patch b/SOURCES/0078-Support-UEFI-networking-protocols.patch
new file mode 100644
index 0000000..145becf
--- /dev/null
+++ b/SOURCES/0078-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 52ec0fafcd..12797336c9 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -2300,6 +2300,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;
+@@ -2313,6 +2319,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 a458c3aca7..1637731535 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 d6a2fb5778..2a446f5031 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 715a6168d7..e11d759f19 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 0000000000..dbef63d8c0
+--- /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 0000000000..ef371d885e
+--- /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 0000000000..3f61fd2fa5
+--- /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 0000000000..b711a5d945
+--- /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 0000000000..017c4d05bc
+--- /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 0000000000..86bce6535d
+--- /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 0000000000..531949cba5
+--- /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 0ce5e675ed..55aed92722 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 a2461cda1c..77958dd9dd 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 0b490195ad..f431f49973 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 0000000000..fdb88eb810
+--- /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 0000000000..c5e9a89f50
+--- /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 0000000000..de90d223e8
+--- /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/0079-AUDIT-0-http-boot-tracker-bug.patch b/SOURCES/0079-AUDIT-0-http-boot-tracker-bug.patch
new file mode 100644
index 0000000..b487271
--- /dev/null
+++ b/SOURCES/0079-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 86bce6535d..4bb308026c 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 12a2632ea5..b52b558d63 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/0080-grub-editenv-Add-incr-command-to-increment-integer-v.patch b/SOURCES/0080-grub-editenv-Add-incr-command-to-increment-integer-v.patch
new file mode 100644
index 0000000..6e2e4e2
--- /dev/null
+++ b/SOURCES/0080-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 db6f187cc6..948eec8a11 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/0081-Add-auto-hide-menu-support.patch b/SOURCES/0081-Add-auto-hide-menu-support.patch
new file mode 100644
index 0000000..a4fbe4a
--- /dev/null
+++ b/SOURCES/0081-Add-auto-hide-menu-support.patch
@@ -0,0 +1,183 @@
+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>
+---
+ 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 bda9fd1211..cb8e3c3270 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 0000000000..ad175870a5
+--- /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 4b27bd2015..3c9431cfcf 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/0082-Add-grub-set-bootflag-utility.patch b/SOURCES/0082-Add-grub-set-bootflag-utility.patch
new file mode 100644
index 0000000..495bb13
--- /dev/null
+++ b/SOURCES/0082-Add-grub-set-bootflag-utility.patch
@@ -0,0 +1,290 @@
+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>
+[makhomed: grub-boot-success.timer: Only run if not in a container]
+Signed-off-by: Gena Makhomed <makhomed@gmail.com>
+[rharwood: migrate to h2m]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ Makefile.util.def              |   7 ++
+ util/grub-set-bootflag.c       | 172 +++++++++++++++++++++++++++++++++++++++++
+ conf/Makefile.extra-dist       |   3 +
+ docs/grub-boot-success.service |   6 ++
+ docs/grub-boot-success.timer   |   7 ++
+ docs/man/grub-set-bootflag.h2m |   2 +
+ 6 files changed, 197 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 docs/man/grub-set-bootflag.h2m
+
+diff --git a/Makefile.util.def b/Makefile.util.def
+index cb8e3c3270..d066652e9b 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -1429,3 +1429,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 0000000000..d506f7e75b
+--- /dev/null
++++ b/util/grub-set-bootflag.c
+@@ -0,0 +1,172 @@
++/* 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>
++
++#include "progname.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(FILE *out)
++{
++  int i;
++
++  fprintf (out, "Usage: 'grub-set-bootflag <bootflag>', where <bootflag> is one of:\n");
++  for (i = 0; bootflags[i]; i++)
++    fprintf (out, "  %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 (stderr);
++      return 1;
++    }
++  else if (!strcmp (argv[1], "--help"))
++    {
++      usage (stdout);
++      return 0;
++    }
++  else if (!strcmp (argv[1], "--version"))
++    {
++      printf ("grub-set-bootflag (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
++      return 0;
++    }
++
++  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 (stderr);
++      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 8f1485d52a..ad235de7fc 100644
+--- a/conf/Makefile.extra-dist
++++ b/conf/Makefile.extra-dist
+@@ -15,6 +15,9 @@ EXTRA_DIST += docs/man
+ 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 0000000000..80e79584c9
+--- /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 0000000000..406f172005
+--- /dev/null
++++ b/docs/grub-boot-success.timer
+@@ -0,0 +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/docs/man/grub-set-bootflag.h2m b/docs/man/grub-set-bootflag.h2m
+new file mode 100644
+index 0000000000..94ec0b92ed
+--- /dev/null
++++ b/docs/man/grub-set-bootflag.h2m
+@@ -0,0 +1,2 @@
++[NAME]
++grub-set-bootflag \- set a bootflag in the GRUB environment block
diff --git a/SOURCES/0083-docs-Add-grub-boot-indeterminate.service-example.patch b/SOURCES/0083-docs-Add-grub-boot-indeterminate.service-example.patch
new file mode 100644
index 0000000..96eff36
--- /dev/null
+++ b/SOURCES/0083-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 0000000000..6c8dcb186b
+--- /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/0084-gentpl-add-disable-support.patch b/SOURCES/0084-gentpl-add-disable-support.patch
new file mode 100644
index 0000000..3b305e6
--- /dev/null
+++ b/SOURCES/0084-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 c86550d4f9..f3c5f84f85 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/0085-gentpl-add-pc-firmware-type.patch b/SOURCES/0085-gentpl-add-pc-firmware-type.patch
new file mode 100644
index 0000000..0bfd2ea
--- /dev/null
+++ b/SOURCES/0085-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 f3c5f84f85..f09b336869 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/0086-efinet-also-use-the-firmware-acceleration-for-http.patch b/SOURCES/0086-efinet-also-use-the-firmware-acceleration-for-http.patch
new file mode 100644
index 0000000..a3a9400
--- /dev/null
+++ b/SOURCES/0086-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 4bb308026c..6603cd83ed 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/0087-efi-http-Make-root_url-reflect-the-protocol-hostname.patch b/SOURCES/0087-efi-http-Make-root_url-reflect-the-protocol-hostname.patch
new file mode 100644
index 0000000..90d9777
--- /dev/null
+++ b/SOURCES/0087-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 3f61fd2fa5..243acbaa35 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/0088-Make-it-so-we-can-tell-configure-which-cflags-utils-.patch b/SOURCES/0088-Make-it-so-we-can-tell-configure-which-cflags-utils-.patch
new file mode 100644
index 0000000..6d2974f
--- /dev/null
+++ b/SOURCES/0088-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 302300711f..008f6c273b 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -849,11 +849,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
+@@ -1954,6 +1966,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 5f0ef96985..2ff9b39357 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 f09b336869..0e62e14666 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/0089-module-verifier-make-it-possible-to-run-checkers-on-.patch b/SOURCES/0089-module-verifier-make-it-possible-to-run-checkers-on-.patch
new file mode 100644
index 0000000..78c15d6
--- /dev/null
+++ b/SOURCES/0089-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 257229f8f0..ba7d41aafe 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 4db6b4bedd..fc23ef800b 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 ceb24309ae..a98e2f9b1a 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/0090-Rework-how-the-fdt-command-builds.patch b/SOURCES/0090-Rework-how-the-fdt-command-builds.patch
new file mode 100644
index 0000000..8f41272
--- /dev/null
+++ b/SOURCES/0090-Rework-how-the-fdt-command-builds.patch
@@ -0,0 +1,123 @@
+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>
+[javierm: Fix build with platform emu, aarch64, and risc-v]
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+Signed-off-by: Robbie Harwood <rharwood@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          | 6 ++++++
+ grub-core/Makefile.am       | 1 +
+ 5 files changed, 11 insertions(+), 5 deletions(-)
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 12797336c9..4e7d90da76 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;
+@@ -1837,7 +1837,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 0d371c5633..37e04bd69e 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 c86f283d75..c572415d38 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 e609c7e411..3514aa4a5b 100644
+--- a/include/grub/fdt.h
++++ b/include/grub/fdt.h
+@@ -19,6 +19,9 @@
+ #ifndef GRUB_FDT_HEADER
+ #define GRUB_FDT_HEADER	1
+ 
++#if !defined(GRUB_MACHINE_EMU) && \
++    (defined(__arm__) || defined(__aarch64__) || defined(__riscv))
++
+ #include <grub/types.h>
+ #include <grub/symbol.h>
+ 
+@@ -144,4 +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(GRUB_MACHINE_EMU) && \
++          (defined(__arm__) || defined(__aarch64__) || defined(__riscv)) */
++
+ #endif	/* ! GRUB_FDT_HEADER */
+diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
+index bfd29a3bf0..c2e8a82bce 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/0091-Disable-non-wordsize-allocations-on-arm.patch b/SOURCES/0091-Disable-non-wordsize-allocations-on-arm.patch
new file mode 100644
index 0000000..ea09b65
--- /dev/null
+++ b/SOURCES/0091-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 008f6c273b..54462e0892 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1260,6 +1260,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/0092-Prepend-prefix-when-HTTP-path-is-relative.patch b/SOURCES/0092-Prepend-prefix-when-HTTP-path-is-relative.patch
new file mode 100644
index 0000000..30d8075
--- /dev/null
+++ b/SOURCES/0092-Prepend-prefix-when-HTTP-path-is-relative.patch
@@ -0,0 +1,152 @@
+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>
+Signed-off-by: Stephen Benjamin <stephen@redhat.com>
+Signed-off-by: Robbie Harwood <rharwood@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 d1de9fa687..1c540fc8c2 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 243acbaa35..de351b2cd0 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/0093-Make-grub_error-more-verbose.patch b/SOURCES/0093-Make-grub_error-more-verbose.patch
new file mode 100644
index 0000000..306a3d3
--- /dev/null
+++ b/SOURCES/0093-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 53c734de70..aebfe0cf83 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 b08d5d0de4..c0f90ef07c 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/0094-Make-reset-an-alias-for-the-reboot-command.patch b/SOURCES/0094-Make-reset-an-alias-for-the-reboot-command.patch
new file mode 100644
index 0000000..22475d6
--- /dev/null
+++ b/SOURCES/0094-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 46d364c99a..f5cc228363 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/0095-Add-a-version-command.patch b/SOURCES/0095-Add-a-version-command.patch
new file mode 100644
index 0000000..18c355c
--- /dev/null
+++ b/SOURCES/0095-Add-a-version-command.patch
@@ -0,0 +1,134 @@
+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>
+[rharwood: don't say GNU, commit message cleanup]
+Signed-off-by: Robbie Harwood <rharwood@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 54462e0892..7b4e1854d3 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -284,6 +284,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 4e7d90da76..4f203533f5 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 0000000000..de0acb07ba
+--- /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 (_("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 9e8f9911b1..c7e316f0f1 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/0096-Add-more-dprintf-and-nerf-dprintf-in-script.c.patch b/SOURCES/0096-Add-more-dprintf-and-nerf-dprintf-in-script.c.patch
new file mode 100644
index 0000000..72c8d30
--- /dev/null
+++ b/SOURCES/0096-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 0320115662..7cdffe3ebd 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 f077b5f553..fe8ba6e6c9 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 73b8ecc0c0..f58b58c89d 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 ec4d4337c6..844e8343ca 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/0097-arm-arm64-loader-Better-memory-allocation-and-error-.patch b/SOURCES/0097-arm-arm64-loader-Better-memory-allocation-and-error-.patch
new file mode 100644
index 0000000..57d937c
--- /dev/null
+++ b/SOURCES/0097-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 f6aef0ef64..85ad4b4494 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 04994d5c67..70a0075ec5 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/0098-Try-to-pick-better-locations-for-kernel-and-initrd.patch b/SOURCES/0098-Try-to-pick-better-locations-for-kernel-and-initrd.patch
new file mode 100644
index 0000000..4f42ea5
--- /dev/null
+++ b/SOURCES/0098-Try-to-pick-better-locations-for-kernel-and-initrd.patch
@@ -0,0 +1,211 @@
+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>
+[david.abdurachmanov: fix macro for riscv64]
+Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
+Signed-off-by: Robbie Harwood <rharwood@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/riscv64/efi/memory.h |  1 +
+ include/grub/x86_64/efi/memory.h  |  4 +++-
+ 8 files changed, 29 insertions(+), 12 deletions(-)
+
+diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
+index 85ad4b4494..e84961d078 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 3017d0f3e5..33e981e76e 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 2c64918e3f..a4c2ec8350 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 c6cb324171..acb61dca44 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 2c64918e3f..a4c2ec8350 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 2c64918e3f..a4c2ec8350 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/riscv64/efi/memory.h b/include/grub/riscv64/efi/memory.h
+index c6cb324171..acb61dca44 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/include/grub/x86_64/efi/memory.h b/include/grub/x86_64/efi/memory.h
+index 46e9145a30..e81cfb3221 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/0099-Attempt-to-fix-up-all-the-places-Wsign-compare-error.patch b/SOURCES/0099-Attempt-to-fix-up-all-the-places-Wsign-compare-error.patch
new file mode 100644
index 0000000..77db523
--- /dev/null
+++ b/SOURCES/0099-Attempt-to-fix-up-all-the-places-Wsign-compare-error.patch
@@ -0,0 +1,199 @@
+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/setup.c                      |  2 +-
+ 10 files changed, 17 insertions(+), 16 deletions(-)
+
+diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c
+index 0ff13bcaf8..d278c2921f 100644
+--- a/grub-core/kern/emu/misc.c
++++ b/grub-core/kern/emu/misc.c
+@@ -185,7 +185,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 467305b46a..79037c093f 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 c77d6085cc..42a315031f 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 28790307e0..9f730b3518 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 da62f924e3..7bc99ac1c1 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 8386564200..bfcef852d8 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 a39f869394..358d604210 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 0fe45a6103..3e09240b99 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 c08e46bbb4..c6fac732b4 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/setup.c b/util/setup.c
+index da5f2c07f5..8b22bb8cca 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/SOURCES/0100-Don-t-use-Wno-sign-compare-Wno-conversion-Wno-error-.patch b/SOURCES/0100-Don-t-use-Wno-sign-compare-Wno-conversion-Wno-error-.patch
new file mode 100644
index 0000000..03b4bbf
--- /dev/null
+++ b/SOURCES/0100-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 7b4e1854d3..490353713a 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1452,11 +1452,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"
+ 
+@@ -1990,6 +1990,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 2ff9b39357..35e14ff017 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/0101-x86-efi-Use-bounce-buffers-for-reading-to-addresses-.patch b/SOURCES/0101-x86-efi-Use-bounce-buffers-for-reading-to-addresses-.patch
new file mode 100644
index 0000000..5a9c6f2
--- /dev/null
+++ b/SOURCES/0101-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 33e981e76e..2f0336809e 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/0102-x86-efi-Re-arrange-grub_cmd_linux-a-little-bit.patch b/SOURCES/0102-x86-efi-Re-arrange-grub_cmd_linux-a-little-bit.patch
new file mode 100644
index 0000000..9b10d68
--- /dev/null
+++ b/SOURCES/0102-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 2f0336809e..5f48fa5561 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/0103-x86-efi-Make-our-own-allocator-for-kernel-stuff.patch b/SOURCES/0103-x86-efi-Make-our-own-allocator-for-kernel-stuff.patch
new file mode 100644
index 0000000..700c98b
--- /dev/null
+++ b/SOURCES/0103-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 5f48fa5561..3e4f7ef39f 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/0104-x86-efi-Allow-initrd-params-cmdline-allocations-abov.patch b/SOURCES/0104-x86-efi-Allow-initrd-params-cmdline-allocations-abov.patch
new file mode 100644
index 0000000..6ac11a1
--- /dev/null
+++ b/SOURCES/0104-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 3e4f7ef39f..6bc18d5aef 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 25ef52c04e..fac22476cc 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/0105-Fix-getroot.c-s-trampolines.patch b/SOURCES/0105-Fix-getroot.c-s-trampolines.patch
new file mode 100644
index 0000000..e744b77
--- /dev/null
+++ b/SOURCES/0105-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 9f730b3518..f0c503f43d 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/0106-Do-not-allow-stack-trampolines-anywhere.patch b/SOURCES/0106-Do-not-allow-stack-trampolines-anywhere.patch
new file mode 100644
index 0000000..658c7a4
--- /dev/null
+++ b/SOURCES/0106-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 490353713a..a02d40a05b 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1998,6 +1998,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 35e14ff017..0647c53b91 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/0107-Reimplement-boot_counter.patch b/SOURCES/0107-Reimplement-boot_counter.patch
new file mode 100644
index 0000000..d018de4
--- /dev/null
+++ b/SOURCES/0107-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 d066652e9b..e10fe766d1 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 4f203533f5..ea4d59f51b 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 0000000000..79cf137656
+--- /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 0000000000..be0e770ea8
+--- /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/0108-Fix-menu-entry-selection-based-on-ID-and-title.patch b/SOURCES/0108-Fix-menu-entry-selection-based-on-ID-and-title.patch
new file mode 100644
index 0000000..af74901
--- /dev/null
+++ b/SOURCES/0108-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 d7a222e681..4a02aadb01 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/0109-Make-the-menu-entry-users-option-argument-to-be-opti.patch b/SOURCES/0109-Make-the-menu-entry-users-option-argument-to-be-opti.patch
new file mode 100644
index 0000000..68779eb
--- /dev/null
+++ b/SOURCES/0109-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 b194123eb6..b175a1b43b 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/0110-Add-efi-export-env-and-efi-load-env-commands.patch b/SOURCES/0110-Add-efi-export-env-and-efi-load-env-commands.patch
new file mode 100644
index 0000000..33d2724
--- /dev/null
+++ b/SOURCES/0110-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 ea4d59f51b..dc9fea6f44 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 0000000000..cbd13e03e8
+--- /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 2a446f5031..14bc10eb56 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 2d12e6188f..0574d8d621 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 2e4e78b132..874506da16 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 d506f7e75b..a6ccc11383 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 2e0691454b..8dfc89a33b 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 c3e6559217..ab969af246 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/0111-Make-it-possible-to-subtract-conditions-from-debug.patch b/SOURCES/0111-Make-it-possible-to-subtract-conditions-from-debug.patch
new file mode 100644
index 0000000..15305f8
--- /dev/null
+++ b/SOURCES/0111-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 9a2fae6398..578bf51a5f 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/0112-Export-all-variables-from-the-initial-context-when-c.patch b/SOURCES/0112-Export-all-variables-from-the-initial-context-when-c.patch
new file mode 100644
index 0000000..214fa4f
--- /dev/null
+++ b/SOURCES/0112-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 ee53d4a68e..87edd254c4 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 4a02aadb01..fe2e77a43e 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/0113-grub.d-Split-out-boot-success-reset-from-menu-auto-h.patch b/SOURCES/0113-grub.d-Split-out-boot-success-reset-from-menu-auto-h.patch
new file mode 100644
index 0000000..43e8d16
--- /dev/null
+++ b/SOURCES/0113-grub.d-Split-out-boot-success-reset-from-menu-auto-h.patch
@@ -0,0 +1,168 @@
+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
+
+Signed-off-by: Christian Glombek <lorbus@fedoraproject.org>
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ 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 e10fe766d1..b4ce5383b7 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 be0e770ea8..2e2c3ff7d3 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 0000000000..6c88d933dd
+--- /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 ad175870a5..6a7c0fa0d4 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/0114-Don-t-assume-that-boot-commands-will-only-return-on-.patch b/SOURCES/0114-Don-t-assume-that-boot-commands-will-only-return-on-.patch
new file mode 100644
index 0000000..df36517
--- /dev/null
+++ b/SOURCES/0114-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 fe2e77a43e..ec0c92bade 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/0115-grub-set-bootflag-Update-comment-about-running-as-ro.patch b/SOURCES/0115-grub-set-bootflag-Update-comment-about-running-as-ro.patch
new file mode 100644
index 0000000..9ffb3ae
--- /dev/null
+++ b/SOURCES/0115-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 a6ccc11383..3eb04beb5e 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/0116-grub-set-bootflag-Write-new-env-to-tmpfile-and-then-.patch b/SOURCES/0116-grub-set-bootflag-Write-new-env-to-tmpfile-and-then-.patch
new file mode 100644
index 0000000..11722d1
--- /dev/null
+++ b/SOURCES/0116-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 3eb04beb5e..3b4c25ca2a 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>
+ 
+@@ -56,8 +58,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)
+@@ -89,7 +93,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");
+@@ -144,30 +173,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/0117-grub.d-Fix-boot_indeterminate-getting-set-on-boot_su.patch b/SOURCES/0117-grub.d-Fix-boot_indeterminate-getting-set-on-boot_su.patch
new file mode 100644
index 0000000..948ff73
--- /dev/null
+++ b/SOURCES/0117-grub.d-Fix-boot_indeterminate-getting-set-on-boot_su.patch
@@ -0,0 +1,81 @@
+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
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+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>
+[jpokorny: 01_menu_auto_hide.in: fix a then/than typo]
+Signed-off-by: Jan Pokorný <jpokorny@fedoraproject.org>
+Signed-off-by: Robbie Harwood <rharwood@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 6c88d933dd..e73f4137b3 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 than 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/0118-Add-start-symbol-for-RISC-V.patch b/SOURCES/0118-Add-start-symbol-for-RISC-V.patch
new file mode 100644
index 0000000..2746fa2
--- /dev/null
+++ b/SOURCES/0118-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 f2a7b2b1ed..781773136e 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/0119-bootstrap.conf-Force-autogen.sh-to-use-python3.patch b/SOURCES/0119-bootstrap.conf-Force-autogen.sh-to-use-python3.patch
new file mode 100644
index 0000000..8bfc964
--- /dev/null
+++ b/SOURCES/0119-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 6b043fc354..52d4af44be 100644
+--- a/bootstrap.conf
++++ b/bootstrap.conf
+@@ -92,7 +92,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/0120-efi-http-Export-fw-http-_path-variables-to-make-them.patch b/SOURCES/0120-efi-http-Export-fw-http-_path-variables-to-make-them.patch
new file mode 100644
index 0000000..f745782
--- /dev/null
+++ b/SOURCES/0120-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 1c540fc8c2..b573be6650 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 de351b2cd0..755b7a6d05 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/0121-efi-http-Enclose-literal-IPv6-addresses-in-square-br.patch b/SOURCES/0121-efi-http-Enclose-literal-IPv6-addresses-in-square-br.patch
new file mode 100644
index 0000000..03d85c2
--- /dev/null
+++ b/SOURCES/0121-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 755b7a6d05..fc8cb25ae0 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/0122-efi-net-Allow-to-specify-a-port-number-in-addresses.patch b/SOURCES/0122-efi-net-Allow-to-specify-a-port-number-in-addresses.patch
new file mode 100644
index 0000000..8fe26cd
--- /dev/null
+++ b/SOURCES/0122-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 6603cd83ed..84573937b1 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/0123-efi-ip4_config-Improve-check-to-detect-literal-IPv6-.patch b/SOURCES/0123-efi-ip4_config-Improve-check-to-detect-literal-IPv6-.patch
new file mode 100644
index 0000000..2dc7001
--- /dev/null
+++ b/SOURCES/0123-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 b711a5d945..313c818b18 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/0124-efi-net-Print-a-debug-message-if-parsing-the-address.patch b/SOURCES/0124-efi-net-Print-a-debug-message-if-parsing-the-address.patch
new file mode 100644
index 0000000..da94e08
--- /dev/null
+++ b/SOURCES/0124-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 84573937b1..a3f0535d43 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/0125-kern-term-Also-accept-F8-as-a-user-interrupt-key.patch b/SOURCES/0125-kern-term-Also-accept-F8-as-a-user-interrupt-key.patch
new file mode 100644
index 0000000..8793b0d
--- /dev/null
+++ b/SOURCES/0125-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 14d5964983..4d61f4e979 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/0126-efi-Set-image-base-address-before-jumping-to-the-PE-.patch b/SOURCES/0126-efi-Set-image-base-address-before-jumping-to-the-PE-.patch
new file mode 100644
index 0000000..6aa5013
--- /dev/null
+++ b/SOURCES/0126-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 0622dfa48d..e8b9ecb17f 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/0127-tpm-Don-t-propagate-TPM-measurement-errors-to-the-ve.patch b/SOURCES/0127-tpm-Don-t-propagate-TPM-measurement-errors-to-the-ve.patch
new file mode 100644
index 0000000..d1c7b17
--- /dev/null
+++ b/SOURCES/0127-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 2052c36eab..e287d042e6 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/0128-x86-efi-Reduce-maximum-bounce-buffer-size-to-16-MiB.patch b/SOURCES/0128-x86-efi-Reduce-maximum-bounce-buffer-size-to-16-MiB.patch
new file mode 100644
index 0000000..7c41e87
--- /dev/null
+++ b/SOURCES/0128-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 6bc18d5aef..15d40d6e35 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/0129-http-Prepend-prefix-when-the-HTTP-path-is-relative-a.patch b/SOURCES/0129-http-Prepend-prefix-when-the-HTTP-path-is-relative-a.patch
new file mode 100644
index 0000000..7fef93e
--- /dev/null
+++ b/SOURCES/0129-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 b52b558d63..7f878b5615 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/0130-Fix-a-missing-return-in-efi-export-env-and-efi-load-.patch b/SOURCES/0130-Fix-a-missing-return-in-efi-export-env-and-efi-load-.patch
new file mode 100644
index 0000000..f3b10c1
--- /dev/null
+++ b/SOURCES/0130-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 cbd13e03e8..977edb6b06 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/0131-efi-dhcp-fix-some-allocation-error-checking.patch b/SOURCES/0131-efi-dhcp-fix-some-allocation-error-checking.patch
new file mode 100644
index 0000000..90e7a34
--- /dev/null
+++ b/SOURCES/0131-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 dbef63d8c0..e5c79b748b 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/0132-efi-http-fix-some-allocation-error-checking.patch b/SOURCES/0132-efi-http-fix-some-allocation-error-checking.patch
new file mode 100644
index 0000000..149ada8
--- /dev/null
+++ b/SOURCES/0132-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 fc8cb25ae0..26647a50fa 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/0133-efi-ip-46-_config.c-fix-some-potential-allocation-ov.patch b/SOURCES/0133-efi-ip-46-_config.c-fix-some-potential-allocation-ov.patch
new file mode 100644
index 0000000..6413eb6
--- /dev/null
+++ b/SOURCES/0133-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 313c818b18..9725e928f7 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 017c4d05bc..a46f6f9b68 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/0134-efilinux-Fix-integer-overflows-in-grub_cmd_initrd.patch b/SOURCES/0134-efilinux-Fix-integer-overflows-in-grub_cmd_initrd.patch
new file mode 100644
index 0000000..8e63d1d
--- /dev/null
+++ b/SOURCES/0134-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 15d40d6e35..f992ceeef2 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/0135-linuxefi-fail-kernel-validation-without-shim-protoco.patch b/SOURCES/0135-linuxefi-fail-kernel-validation-without-shim-protoco.patch
new file mode 100644
index 0000000..828fe16
--- /dev/null
+++ b/SOURCES/0135-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 70a0075ec5..47f8cf0d84 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 ac8dfd40c6..d41e8ea14a 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 e8b9ecb17f..9260731c10 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 f992ceeef2..3cf0f9b330 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/0136-Fix-const-char-pointers-in-grub-core-net-bootp.c.patch b/SOURCES/0136-Fix-const-char-pointers-in-grub-core-net-bootp.c.patch
new file mode 100644
index 0000000..f20f5c7
--- /dev/null
+++ b/SOURCES/0136-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 8fb8918ae7..7baf3540c8 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/0137-Fix-const-char-pointers-in-grub-core-net-efi-ip4_con.patch b/SOURCES/0137-Fix-const-char-pointers-in-grub-core-net-efi-ip4_con.patch
new file mode 100644
index 0000000..ea92110
--- /dev/null
+++ b/SOURCES/0137-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 9725e928f7..cb880fc3e8 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/0138-Fix-const-char-pointers-in-grub-core-net-efi-ip6_con.patch b/SOURCES/0138-Fix-const-char-pointers-in-grub-core-net-efi-ip6_con.patch
new file mode 100644
index 0000000..915e6d7
--- /dev/null
+++ b/SOURCES/0138-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 a46f6f9b68..1c5415d718 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/0139-Fix-const-char-pointers-in-grub-core-net-efi-net.c.patch b/SOURCES/0139-Fix-const-char-pointers-in-grub-core-net-efi-net.c.patch
new file mode 100644
index 0000000..fbba65a
--- /dev/null
+++ b/SOURCES/0139-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 a3f0535d43..78e5442fc5 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/0140-Fix-const-char-pointers-in-grub-core-net-efi-pxe.c.patch b/SOURCES/0140-Fix-const-char-pointers-in-grub-core-net-efi-pxe.c.patch
new file mode 100644
index 0000000..9b9acfe
--- /dev/null
+++ b/SOURCES/0140-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 531949cba5..73e2bb01c1 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/0141-Add-systemd-integration-scripts-to-make-systemctl-re.patch b/SOURCES/0141-Add-systemd-integration-scripts-to-make-systemctl-re.patch
new file mode 100644
index 0000000..8cf45c3
--- /dev/null
+++ b/SOURCES/0141-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 b4ce5383b7..6366442129 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 0647c53b91..9fe5863b2d 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 0000000000..1cd7f36142
+--- /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 0000000000..f2d4ac0073
+--- /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 0000000000..c81fb594ce
+--- /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 0000000000..dc1218597b
+--- /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/0142-systemd-integration.sh-Also-set-old-menu_show_once-g.patch b/SOURCES/0142-systemd-integration.sh-Also-set-old-menu_show_once-g.patch
new file mode 100644
index 0000000..a16ed68
--- /dev/null
+++ b/SOURCES/0142-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 dc1218597b..a4c071c5b0 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/0143-at_keyboard-use-set-1-when-keyboard-is-in-Translate-.patch b/SOURCES/0143-at_keyboard-use-set-1-when-keyboard-is-in-Translate-.patch
new file mode 100644
index 0000000..f891a69
--- /dev/null
+++ b/SOURCES/0143-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 597111077b..2601438260 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 bcb4d9ba78..9414dc1b99 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/0144-grub-install-disable-support-for-EFI-platforms.patch b/SOURCES/0144-grub-install-disable-support-for-EFI-platforms.patch
new file mode 100644
index 0000000..f6a7530
--- /dev/null
+++ b/SOURCES/0144-grub-install-disable-support-for-EFI-platforms.patch
@@ -0,0 +1,101 @@
+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>
+[rharwood: drop man page]
+---
+ util/grub-install.c | 37 ++++++++++++++++---------------------
+ docs/grub.texi      |  7 +++++++
+ 2 files changed, 23 insertions(+), 21 deletions(-)
+
+diff --git a/util/grub-install.c b/util/grub-install.c
+index a2bec7446c..5babc7af55 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 04ed6ac1f0..4870faaa00 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/SOURCES/0145-New-with-debug-timestamps-configure-flag-to-prepend-.patch b/SOURCES/0145-New-with-debug-timestamps-configure-flag-to-prepend-.patch
new file mode 100644
index 0000000..ec5b30b
--- /dev/null
+++ b/SOURCES/0145-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 a02d40a05b..ab0d326f00 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1585,6 +1585,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)])])
+@@ -2136,6 +2147,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])
+ 
+@@ -2231,6 +2243,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 578bf51a5f..9f54b6b7d2 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 c7e316f0f1..c80e3e0aba 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/0146-Added-debug-statements-to-grub_disk_open-and-grub_di.patch b/SOURCES/0146-Added-debug-statements-to-grub_disk_open-and-grub_di.patch
new file mode 100644
index 0000000..d26027c
--- /dev/null
+++ b/SOURCES/0146-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 e1b0e073e0..05a28ab142 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/0147-Introduce-function-grub_debug_is_enabled-void-return.patch b/SOURCES/0147-Introduce-function-grub_debug_is_enabled-void-return.patch
new file mode 100644
index 0000000..9ce5d9d
--- /dev/null
+++ b/SOURCES/0147-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 9f54b6b7d2..a186ad3dd4 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 3adc4036e3..6c4aa85ac5 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/0148-Don-t-clear-screen-when-debugging-is-enabled.patch b/SOURCES/0148-Don-t-clear-screen-when-debugging-is-enabled.patch
new file mode 100644
index 0000000..c35d8a3
--- /dev/null
+++ b/SOURCES/0148-Don-t-clear-screen-when-debugging-is-enabled.patch
@@ -0,0 +1,31 @@
+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>
+[rharwood@redhat.com: rebase fuzz]
+Signed-off-by: Robbie Harwood <rharwood@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 af9792c963..7de9e4c36d 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -215,8 +215,9 @@ grub_normal_init_page (struct grub_term_output *term,
+   char *msg_formatted;
+   grub_uint32_t *unicode_msg;
+   grub_uint32_t *last_position;
+- 
+-  grub_term_cls (term);
++
++  if (! grub_debug_is_enabled ())
++    grub_term_cls (term);
+ 
+   msg_formatted = grub_xasprintf (_("GRUB version %s"), PACKAGE_VERSION);
+   if (!msg_formatted)
diff --git a/SOURCES/0149-kern-file-Fix-error-handling-in-grub_file_open.patch b/SOURCES/0149-kern-file-Fix-error-handling-in-grub_file_open.patch
new file mode 100644
index 0000000..e444325
--- /dev/null
+++ b/SOURCES/0149-kern-file-Fix-error-handling-in-grub_file_open.patch
@@ -0,0 +1,34 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Steve McIntyre <steve@einval.com>
+Date: Tue, 6 Dec 2022 01:45:11 +0000
+Subject: [PATCH] kern/file: Fix error handling in grub_file_open()
+
+grub_file_open() calls grub_file_get_device_name(), but doesn't check
+the return. Instead, it checks if grub_errno is set.
+
+However, nothing initialises grub_errno here when grub_file_open()
+starts. This means that trying to open one file that doesn't exist and
+then trying to open another file that does will (incorrectly) also
+fail to open that second file.
+
+Let's fix that.
+
+Signed-off-by: Steve McIntyre <steve@einval.com>
+---
+ grub-core/kern/file.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c
+index 58454458c4..5b58f45cfd 100644
+--- a/grub-core/kern/file.c
++++ b/grub-core/kern/file.c
+@@ -66,6 +66,9 @@ grub_file_open (const char *name, enum grub_file_type type)
+   const char *file_name;
+   grub_file_filter_id_t filter;
+ 
++  /* Reset grub_errno before we start */
++  grub_errno = GRUB_ERR_NONE;
++
+   device_name = grub_file_get_device_name (name);
+   if (grub_errno)
+     goto fail;
diff --git a/SOURCES/0150-grub_file_-instrumentation-new-file-debug-tag.patch b/SOURCES/0150-grub_file_-instrumentation-new-file-debug-tag.patch
new file mode 100644
index 0000000..6327899
--- /dev/null
+++ b/SOURCES/0150-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 5b58f45cfd..ec10e54fc0 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);
++
+   /* Reset grub_errno before we start */
+   grub_errno = GRUB_ERR_NONE;
+ 
+@@ -131,6 +133,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:
+@@ -141,6 +145,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;
+ }
+ 
+@@ -172,6 +178,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)
+@@ -192,11 +199,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/0151-ieee1275-Avoiding-many-unecessary-open-close.patch b/SOURCES/0151-ieee1275-Avoiding-many-unecessary-open-close.patch
new file mode 100644
index 0000000..f6c5244
--- /dev/null
+++ b/SOURCES/0151-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 03674cb477..ea7f78ac7d 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/0152-ieee1275-powerpc-implements-fibre-channel-discovery-.patch b/SOURCES/0152-ieee1275-powerpc-implements-fibre-channel-discovery-.patch
new file mode 100644
index 0000000..04c5c32
--- /dev/null
+++ b/SOURCES/0152-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 a6153d3595..0f5d54e9f2 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/0153-ieee1275-powerpc-enables-device-mapper-discovery.patch b/SOURCES/0153-ieee1275-powerpc-enables-device-mapper-discovery.patch
new file mode 100644
index 0000000..85dbde7
--- /dev/null
+++ b/SOURCES/0153-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 0f5d54e9f2..cc849d9c94 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/0154-Add-at_keyboard_fallback_set-var-to-force-the-set-ma.patch b/SOURCES/0154-Add-at_keyboard_fallback_set-var-to-force-the-set-ma.patch
new file mode 100644
index 0000000..f938fd1
--- /dev/null
+++ b/SOURCES/0154-Add-at_keyboard_fallback_set-var-to-force-the-set-ma.patch
@@ -0,0 +1,245 @@
+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
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+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
+
+Signed-off-by: Renaud Métrich <rmetrich@redhat.com>
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ 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 2601438260..dac0f946fe 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/0155-Add-suport-for-signing-grub-with-an-appended-signatu.patch b/SOURCES/0155-Add-suport-for-signing-grub-with-an-appended-signatu.patch
new file mode 100644
index 0000000..81660d4
--- /dev/null
+++ b/SOURCES/0155-Add-suport-for-signing-grub-with-an-appended-signatu.patch
@@ -0,0 +1,309 @@
+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>
+---
+ util/grub-install-common.c  | 18 ++++++++++++++----
+ util/grub-mkimage.c         | 15 +++++++++++++--
+ util/grub-mkimagexx.c       | 39 ++++++++++++++++++++++++++++++++++++++-
+ util/mkimage.c              | 13 +++++++------
+ include/grub/util/install.h |  8 ++++++--
+ include/grub/util/mkimage.h |  4 ++--
+ 6 files changed, 80 insertions(+), 17 deletions(-)
+
+diff --git a/util/grub-install-common.c b/util/grub-install-common.c
+index 4e212e690c..a74fee16e2 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;
+     }
+@@ -661,11 +669,13 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
+ 		  " --output '%s' "
+ 		  " --dtb '%s' "
+ 		  "--sbat '%s' "
+-		  "--format '%s' --compression '%s' %s %s %s\n",
++		  "--format '%s' --compression '%s' "
++		  "--appended-signature-size %zu %s %s %s\n",
+ 		  dir, prefix,
+ 		  outname, dtb ? : "", sbat ? : "", mkimage_target,
+-		  compnames[compression], note ? "--note" : "",
+-		  disable_shim_lock ? "--disable-shim-lock" : "", s);
++		  compnames[compression], appsig_size,
++		  disable_shim_lock ? "--disable-shim-lock" : "",
++		  note ? "--note" : "", s);
+   free (s);
+ 
+   tgt = grub_install_get_image_target (mkimage_target);
+@@ -675,7 +685,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 c0d5599370..8a53310548 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,9 @@ 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.appsig_size, arguments.comp,
++			       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 d78fa3e533..393119486d 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 a26cf76f72..bab1227601 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 7df3191f47..cf4531e02b 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 3819a67441..6f1da89b9b 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/0156-docs-grub-Document-signing-grub-under-UEFI.patch b/SOURCES/0156-docs-grub-Document-signing-grub-under-UEFI.patch
new file mode 100644
index 0000000..f2b5c17
--- /dev/null
+++ b/SOURCES/0156-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 4870faaa00..365d1d6931 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/0157-docs-grub-Document-signing-grub-with-an-appended-sig.patch b/SOURCES/0157-docs-grub-Document-signing-grub-with-an-appended-sig.patch
new file mode 100644
index 0000000..ee3d659
--- /dev/null
+++ b/SOURCES/0157-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 365d1d6931..afbde7c1f7 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/0158-dl-provide-a-fake-grub_dl_set_persistent-for-the-emu.patch b/SOURCES/0158-dl-provide-a-fake-grub_dl_set_persistent-for-the-emu.patch
new file mode 100644
index 0000000..b23ce49
--- /dev/null
+++ b/SOURCES/0158-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 2f76e6b043..20d870f2a4 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/0159-pgp-factor-out-rsa_pad.patch b/SOURCES/0159-pgp-factor-out-rsa_pad.patch
new file mode 100644
index 0000000..43fd882
--- /dev/null
+++ b/SOURCES/0159-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 dc9fea6f44..64cc758835 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -2511,6 +2511,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 5daa1e9d00..2408db4994 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 0000000000..dbacd563d0
+--- /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 0000000000..5c338c84a1
+--- /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/0160-crypto-move-storage-for-grub_crypto_pk_-to-crypto.c.patch b/SOURCES/0160-crypto-move-storage-for-grub_crypto_pk_-to-crypto.c.patch
new file mode 100644
index 0000000..541474e
--- /dev/null
+++ b/SOURCES/0160-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 2408db4994..355a43844a 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 ca334d5a40..c578128a59 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/0161-posix_wrap-tweaks-in-preparation-for-libtasn1.patch b/SOURCES/0161-posix_wrap-tweaks-in-preparation-for-libtasn1.patch
new file mode 100644
index 0000000..3176f1b
--- /dev/null
+++ b/SOURCES/0161-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 7217138ffd..591dbf3289 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 7a8d385e97..4634db09f2 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 854eb0122e..f63412c8da 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/0162-libtasn1-import-libtasn1-4.16.0.patch b/SOURCES/0162-libtasn1-import-libtasn1-4.16.0.patch
new file mode 100644
index 0000000..9587661
--- /dev/null
+++ b/SOURCES/0162-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 0000000000..245ea64cf0
+--- /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 0000000000..ff04eb778c
+--- /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 0000000000..997eb2725d
+--- /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 0000000000..cee74daf79
+--- /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 0000000000..e91a3a151c
+--- /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 0000000000..d5dbbf8765
+--- /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 0000000000..8189c56a4c
+--- /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 0000000000..440a33f4bb
+--- /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 0000000000..48229844ff
+--- /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 0000000000..ea1625786c
+--- /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 0000000000..598e684b35
+--- /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 0000000000..99e685da07
+--- /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 0000000000..6fd7a30dc3
+--- /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 0000000000..e8b3628db9
+--- /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 0000000000..50a8642296
+--- /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/0163-libtasn1-disable-code-not-needed-in-grub.patch b/SOURCES/0163-libtasn1-disable-code-not-needed-in-grub.patch
new file mode 100644
index 0000000..84dcbf0
--- /dev/null
+++ b/SOURCES/0163-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 245ea64cf0..52def59836 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 ff04eb778c..42f9a92b5d 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 997eb2725d..539008d8e9 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 cee74daf79..42785e8622 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 8189c56a4c..fcfde01a39 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 6fd7a30dc3..785eda2ae3 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/0164-libtasn1-changes-for-grub-compatibility.patch b/SOURCES/0164-libtasn1-changes-for-grub-compatibility.patch
new file mode 100644
index 0000000..7c756bd
--- /dev/null
+++ b/SOURCES/0164-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 42f9a92b5d..7856858b27 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 539008d8e9..ed761ff56b 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 e91a3a151c..e33875c2c7 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 d5dbbf8765..89c9be69dc 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 ea1625786c..4a568efee9 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 785eda2ae3..28dbf16c4e 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/0165-libtasn1-compile-into-asn1-module.patch b/SOURCES/0165-libtasn1-compile-into-asn1-module.patch
new file mode 100644
index 0000000..0fb433f
--- /dev/null
+++ b/SOURCES/0165-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 64cc758835..ea92468fa2 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -2577,3 +2577,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 0000000000..622ba942e3
+--- /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/0166-test_asn1-test-module-for-libtasn1.patch b/SOURCES/0166-test_asn1-test-module-for-libtasn1.patch
new file mode 100644
index 0000000..6cc13bd
--- /dev/null
+++ b/SOURCES/0166-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 6366442129..d04b3fe68a 100644
+--- a/Makefile.util.def
++++ b/Makefile.util.def
+@@ -1289,6 +1289,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 ea92468fa2..a32e6ada59 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -2592,3 +2592,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 0000000000..534e304521
+--- /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 0000000000..f48aea0ef8
+--- /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 0000000000..9f01006ddf
+--- /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 0000000000..dbe1474b20
+--- /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 0000000000..d367bbfb5a
+--- /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 0000000000..3a83b58c59
+--- /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 0000000000..d8a049e8df
+--- /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 0000000000..dc7268d4c6
+--- /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 0000000000..75fcd21f0d
+--- /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 0000000000..1e7d3d64f5
+--- /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 0000000000..e2561e5ec6
+--- /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 0000000000..555e56dd20
+--- /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 208d1d2325..1d005887e7 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 0000000000..8173c5c270
+--- /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/0167-grub-install-support-embedding-x509-certificates.patch b/SOURCES/0167-grub-install-support-embedding-x509-certificates.patch
new file mode 100644
index 0000000..c4c35f4
--- /dev/null
+++ b/SOURCES/0167-grub-install-support-embedding-x509-certificates.patch
@@ -0,0 +1,253 @@
+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  | 23 ++++++++++++++++++++++-
+ util/grub-mkimage.c         | 15 +++++++++++++--
+ util/mkimage.c              | 38 ++++++++++++++++++++++++++++++++++++--
+ include/grub/kernel.h       |  4 +++-
+ include/grub/util/install.h |  7 +++++--
+ 6 files changed, 80 insertions(+), 9 deletions(-)
+
+diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c
+index 355a43844a..b81ac0ae46 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 a74fee16e2..c603f5b308 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;
+ 
+@@ -501,6 +503,12 @@ grub_install_parse (int key, char *arg)
+     case GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK:
+       disable_shim_lock = 1;
+       return 1;
++    case 'x':
++      x509keys = xrealloc (x509keys,
++			  sizeof (x509keys[0])
++			  * (nx509keys + 1));
++      x509keys[nx509keys++] = xstrdup (arg);
++      return 1;
+ 
+     case GRUB_INSTALL_OPTIONS_VERBOSITY:
+       verbosity++;
+@@ -627,6 +635,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 +666,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++ = '\'';
+@@ -684,7 +703,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 8a53310548..e1f1112784 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.appsig_size, arguments.comp,
+ 			       arguments.dtb, arguments.sbat,
+diff --git a/util/mkimage.c b/util/mkimage.c
+index bab1227601..8319e8dfbd 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 55849777ea..98edc0863f 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 cf4531e02b..51f3b13ac1 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/0168-appended-signatures-import-GNUTLS-s-ASN.1-descriptio.patch b/SOURCES/0168-appended-signatures-import-GNUTLS-s-ASN.1-descriptio.patch
new file mode 100644
index 0000000..0ebc6c7
--- /dev/null
+++ b/SOURCES/0168-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 0000000000..ddd1314e63
+--- /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 0000000000..adef69d95c
+--- /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/0169-appended-signatures-parse-PKCS-7-signedData-and-X.50.patch b/SOURCES/0169-appended-signatures-parse-PKCS-7-signedData-and-X.50.patch
new file mode 100644
index 0000000..5a13d5b
--- /dev/null
+++ b/SOURCES/0169-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 0000000000..eff095a9df
+--- /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 0000000000..dc6afe203f
+--- /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 0000000000..2b38b3670a
+--- /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 0000000000..9792ef3901
+--- /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/0170-appended-signatures-support-verifying-appended-signa.patch b/SOURCES/0170-appended-signatures-support-verifying-appended-signa.patch
new file mode 100644
index 0000000..9e90d3b
--- /dev/null
+++ b/SOURCES/0170-appended-signatures-support-verifying-appended-signa.patch
@@ -0,0 +1,718 @@
+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>
+[pjones: fix missing format specifier]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ 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 a32e6ada59..6404384d90 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 0000000000..bf8b18b620
+--- /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 31567483cc..96827a4f89 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/0171-appended-signatures-verification-tests.patch b/SOURCES/0171-appended-signatures-verification-tests.patch
new file mode 100644
index 0000000..b39a5af
--- /dev/null
+++ b/SOURCES/0171-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 6404384d90..9ea5fb38f1 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -2162,6 +2162,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 0000000000..88a485200d
+--- /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 96781fb39b..403fa5c789 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 0000000000..aa3dc6278e
+--- /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/0172-appended-signatures-documentation.patch b/SOURCES/0172-appended-signatures-documentation.patch
new file mode 100644
index 0000000..eb58046
--- /dev/null
+++ b/SOURCES/0172-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 afbde7c1f7..4816be8561 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/0173-ieee1275-enter-lockdown-based-on-ibm-secure-boot.patch b/SOURCES/0173-ieee1275-enter-lockdown-based-on-ibm-secure-boot.patch
new file mode 100644
index 0000000..c90cc7e
--- /dev/null
+++ b/SOURCES/0173-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 9ea5fb38f1..4a57de975e 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 937c1bc44c..fc7d971272 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 40531fa823..ebfee4bf06 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 4816be8561..a4da9c2a1b 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/0174-ieee1275-drop-HEAP_MAX_ADDR-HEAP_MIN_SIZE.patch b/SOURCES/0174-ieee1275-drop-HEAP_MAX_ADDR-HEAP_MIN_SIZE.patch
new file mode 100644
index 0000000..a40caea
--- /dev/null
+++ b/SOURCES/0174-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 fc7d971272..0dcd114ce5 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/0175-appendedsig-x509-Also-handle-the-Extended-Key-Usage-.patch b/SOURCES/0175-appendedsig-x509-Also-handle-the-Extended-Key-Usage-.patch
new file mode 100644
index 0000000..5728f26
--- /dev/null
+++ b/SOURCES/0175-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 2b38b3670a..42ec65c54a 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 88a485200d..dbba061662 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 aa3dc6278e..2e5ebd7d8b 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/0176-ieee1275-ofdisk-retry-on-open-failure.patch b/SOURCES/0176-ieee1275-ofdisk-retry-on-open-failure.patch
new file mode 100644
index 0000000..9149773
--- /dev/null
+++ b/SOURCES/0176-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 ea7f78ac7d..55346849d3 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 2f69e3f191..7d2d540930 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/0177-Allow-chainloading-EFI-apps-from-loop-mounts.patch b/SOURCES/0177-Allow-chainloading-EFI-apps-from-loop-mounts.patch
new file mode 100644
index 0000000..c3101e1
--- /dev/null
+++ b/SOURCES/0177-Allow-chainloading-EFI-apps-from-loop-mounts.patch
@@ -0,0 +1,138 @@
+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.
+
+Signed-off-by: Dimitri John Ledkov <Dimitri.ledkov@canonical.com>
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ 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 41bebd14fe..99f47924ec 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 d41e8ea14a..3af6b12292 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 0000000000..3b9a9e32e8
+--- /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/0178-efinet-Add-DHCP-proxy-support.patch b/SOURCES/0178-efinet-Add-DHCP-proxy-support.patch
new file mode 100644
index 0000000..eed2dbb
--- /dev/null
+++ b/SOURCES/0178-efinet-Add-DHCP-proxy-support.patch
@@ -0,0 +1,53 @@
+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.
+
+Signed-off-by: Ian Page Hands <iphands@gmail.com>
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ 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 e11d759f19..1a24f38a21 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/0179-fs-ext2-Ignore-checksum-seed-incompat-feature.patch b/SOURCES/0179-fs-ext2-Ignore-checksum-seed-incompat-feature.patch
new file mode 100644
index 0000000..3d7c641
--- /dev/null
+++ b/SOURCES/0179-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 e7dd78e663..731d346f88 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/0180-Don-t-update-the-cmdline-when-generating-legacy-menu.patch b/SOURCES/0180-Don-t-update-the-cmdline-when-generating-legacy-menu.patch
new file mode 100644
index 0000000..b2783d1
--- /dev/null
+++ b/SOURCES/0180-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 e490e1a43a..865af3d6c4 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -256,7 +256,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/0181-Suppress-gettext-error-message.patch b/SOURCES/0181-Suppress-gettext-error-message.patch
new file mode 100644
index 0000000..64b219a
--- /dev/null
+++ b/SOURCES/0181-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 4d02e62c10..7ec81ca0b4 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/0182-grub-set-password-Always-use-boot-grub2-user.cfg-as-.patch b/SOURCES/0182-grub-set-password-Always-use-boot-grub2-user.cfg-as-.patch
new file mode 100644
index 0000000..b5d5e5c
--- /dev/null
+++ b/SOURCES/0182-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 c0b5ebbfdc..d8005e5a14 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/0183-templates-Check-for-EFI-at-runtime-instead-of-config.patch b/SOURCES/0183-templates-Check-for-EFI-at-runtime-instead-of-config.patch
new file mode 100644
index 0000000..265b967
--- /dev/null
+++ b/SOURCES/0183-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 d344d3883d..b6041b55e2 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/0184-efi-Print-an-error-if-boot-to-firmware-setup-is-not-.patch b/SOURCES/0184-efi-Print-an-error-if-boot-to-firmware-setup-is-not-.patch
new file mode 100644
index 0000000..3b1a219
--- /dev/null
+++ b/SOURCES/0184-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 eaca032838..328c45e82e 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/0185-arm64-Fix-EFI-loader-kernel-image-allocation.patch b/SOURCES/0185-arm64-Fix-EFI-loader-kernel-image-allocation.patch
new file mode 100644
index 0000000..cc5458c
--- /dev/null
+++ b/SOURCES/0185-arm64-Fix-EFI-loader-kernel-image-allocation.patch
@@ -0,0 +1,218 @@
+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>
+[pjones: arm: check for the PE magic for the compiled arch]
+Signed-off-by: Peter Jones <pjones@redhat.com>
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/loader/arm64/linux.c | 100 +++++++++++++++++++++++++++--------------
+ include/grub/arm/linux.h       |   1 +
+ include/grub/arm64/linux.h     |   1 +
+ 3 files changed, 68 insertions(+), 34 deletions(-)
+
+diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
+index 47f8cf0d84..f18d90bd74 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_PEXX_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/include/grub/arm/linux.h b/include/grub/arm/linux.h
+index b582f67f66..966a5074f5 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 ea030312df..422bf2bf24 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/0186-normal-main-Discover-the-device-to-read-the-config-f.patch b/SOURCES/0186-normal-main-Discover-the-device-to-read-the-config-f.patch
new file mode 100644
index 0000000..2c9ca3a
--- /dev/null
+++ b/SOURCES/0186-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 7de9e4c36d..8f5fd81003 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -337,18 +337,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;
+@@ -362,7 +357,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);
+@@ -391,6 +386,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)),
+@@ -405,6 +447,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/0187-powerpc-adjust-setting-of-prefix-for-signed-binary-c.patch b/SOURCES/0187-powerpc-adjust-setting-of-prefix-for-signed-binary-c.patch
new file mode 100644
index 0000000..b9bc140
--- /dev/null
+++ b/SOURCES/0187-powerpc-adjust-setting-of-prefix-for-signed-binary-c.patch
@@ -0,0 +1,88 @@
+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>
+[rharwood@redhat.com: squash in fixup commit]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/kern/main.c | 49 ++++++++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 44 insertions(+), 5 deletions(-)
+
+diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c
+index b573be6650..3fc3401472 100644
+--- a/grub-core/kern/main.c
++++ b/grub-core/kern/main.c
+@@ -216,13 +216,52 @@ 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
++
++	  - 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_strchr (device, ',') == NULL)
++        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/0188-fs-xfs-Fix-unreadable-filesystem-with-v4-superblock.patch b/SOURCES/0188-fs-xfs-Fix-unreadable-filesystem-with-v4-superblock.patch
new file mode 100644
index 0000000..7fc40f1
--- /dev/null
+++ b/SOURCES/0188-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 0f524c3a8a..e3816d1ec4 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/0189-Print-module-name-on-license-check-failure.patch b/SOURCES/0189-Print-module-name-on-license-check-failure.patch
new file mode 100644
index 0000000..5c30859
--- /dev/null
+++ b/SOURCES/0189-Print-module-name-on-license-check-failure.patch
@@ -0,0 +1,48 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Tue, 12 Oct 2021 12:34:23 -0400
+Subject: [PATCH] Print module name on license check failure
+
+At the very least, this will make it easier to track down the problem
+module - or, if something else has gone wrong, provide more information
+for debugging.
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/kern/dl.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
+index 9557254035..f304494574 100644
+--- a/grub-core/kern/dl.c
++++ b/grub-core/kern/dl.c
+@@ -528,14 +528,16 @@ grub_dl_find_section_index (Elf_Ehdr *e, const char *name)
+    Be sure to understand your license obligations.
+ */
+ static grub_err_t
+-grub_dl_check_license (Elf_Ehdr *e)
++grub_dl_check_license (grub_dl_t mod, Elf_Ehdr *e)
+ {
+   Elf_Shdr *s = grub_dl_find_section (e, ".module_license");
+   if (s && (grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv3") == 0
+ 	    || grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv3+") == 0
+ 	    || grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv2+") == 0))
+     return GRUB_ERR_NONE;
+-  return grub_error (GRUB_ERR_BAD_MODULE, "incompatible license");
++  return grub_error (GRUB_ERR_BAD_MODULE,
++		     "incompatible license in module %s: %s", mod->name,
++		     (char *) e + s->sh_offset);
+ }
+ 
+ static grub_err_t
+@@ -743,8 +745,8 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size)
+      constitutes linking) and GRUB core being licensed under GPLv3+.
+      Be sure to understand your license obligations.
+   */
+-  if (grub_dl_check_license (e)
+-      || grub_dl_resolve_name (mod, e)
++  if (grub_dl_resolve_name (mod, e)
++      || grub_dl_check_license (mod, e)
+       || grub_dl_resolve_dependencies (mod, e)
+       || grub_dl_load_segments (mod, e)
+       || grub_dl_resolve_symbols (mod, e)
diff --git a/SOURCES/0190-powerpc-ieee1275-load-grub-at-4MB-not-2MB.patch b/SOURCES/0190-powerpc-ieee1275-load-grub-at-4MB-not-2MB.patch
new file mode 100644
index 0000000..216418b
--- /dev/null
+++ b/SOURCES/0190-powerpc-ieee1275-load-grub-at-4MB-not-2MB.patch
@@ -0,0 +1,106 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Fri, 22 Oct 2021 09:53:15 +1100
+Subject: [PATCH] powerpc-ieee1275: load grub at 4MB, not 2MB
+
+This was first reported under PFW but reproduces under SLOF.
+
+ - The core.elf was 2126152 = 0x207148 bytes in size with the following
+   program headers (per readelf):
+
+Entry point 0x200000
+There are 4 program headers, starting at offset 52
+
+Program Headers:
+  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
+  LOAD           0x000160 0x00200000 0x00200000 0x21f98 0x2971c RWE 0x8
+  GNU_STACK      0x0220f8 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4
+  LOAD           0x0220f8 0x00232000 0x00232000 0x1e4e50 0x1e4e50 RWE 0x4
+  NOTE           0x206f48 0x00000000 0x00000000 0x00200 0x00000 R   0x4
+
+ - SLOF places the ELF file at 0x4000 (after the reserved space for
+   interrupt handlers etc.) upwards. The image was 2126152 = 0x207148
+   bytes in size, so it runs from 0x4000 - 0x20b148. We'll call 0x4000 the
+   load address.
+
+0x0        0x4000         0x20b148
+ |----------|--------------|
+ | reserved | ELF contents |
+
+ - SLOF then copies the first LOAD program header (for .text). That runs
+   for 0x21f98 bytes. It runs from
+      (load addr + 0x160) to (load addr + 0x160 + 0x21f98)
+    = 0x4160 to 0x260f8
+   and we copy it to 0x200000 to 0x221f98. This overwrites the end of the
+   image:
+
+0x0       0x4000     0x200000        0x221f98
+ |----------|------------|---------------|
+ | reserved | ELF cont.. | .text section |
+
+ - SLOF zeros the bss up to PhysAddr + MemSize = 0x22971c
+
+0x0       0x4000      0x200000       0x221f98 0x22971c
+ |----------|------------|---------------|--------|
+ | reserved | ELF cont.. | .text section | bss 0s |
+
+ - SLOF then goes to fulfil the next LOAD header (for mods), which is
+   for 0x1e4e50 bytes. We copy from
+      (load addr + 0x220f8) to (load addr + 0x220f8 + 0x1e4e50)
+    = 0x260f8 to 0x20af48
+   and we copy it to 0x232000 to 0x416e50:
+
+0x0       0x4000      0x200000       0x221f98 0x22971c
+ |----------|------------|---------------|--------|
+ | reserved | ELF cont.. | .text section | bss 0s |
+               |-------------|
+               | copied area |
+            0x260f8      0x20af48
+
+   This goes poorly:
+
+0x0       0x4000      0x200000       0x221f98 0x22971c 0x232000 0x40bf08      0x416e50
+ |----------|------------|---------------|--------|-----|-----------|-------------|
+ | reserved | ELF cont.. | .text section | bss 0s | pad | some mods | .text start |
+
+This matches the observations on the running system - 0x40bf08 was where
+the contents of memory no longer matched the contents of the ELF file.
+
+This was reported as a license verification failure on SLOF as the
+last module's .module_license section fell past where the corruption
+began.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+[rharwood@redhat.com: trim very detailed commit message]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/Makefile.core.def | 2 +-
+ include/grub/offsets.h      | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 4a57de975e..08ac0fb15f 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -89,7 +89,7 @@ kernel = {
+   i386_xen_pvh_ldflags     = '$(TARGET_IMG_BASE_LDOPT),0x100000';
+ 
+   mips_loongson_ldflags    = '-Wl,-Ttext,0x80200000';
+-  powerpc_ieee1275_ldflags = '-Wl,-Ttext,0x200000';
++  powerpc_ieee1275_ldflags = '-Wl,-Ttext,0x400000';
+   sparc64_ieee1275_ldflags = '-Wl,-Ttext,0x4400';
+   mips_arc_ldflags    = '-Wl,-Ttext,$(TARGET_LINK_ADDR)';
+   mips_qemu_mips_ldflags    = '-Wl,-Ttext,0x80200000';
+diff --git a/include/grub/offsets.h b/include/grub/offsets.h
+index 871e1cd4c3..69211aa798 100644
+--- a/include/grub/offsets.h
++++ b/include/grub/offsets.h
+@@ -63,7 +63,7 @@
+ #define GRUB_KERNEL_SPARC64_IEEE1275_LINK_ADDR 0x4400
+ 
+ #define GRUB_KERNEL_POWERPC_IEEE1275_LINK_ALIGN 4
+-#define GRUB_KERNEL_POWERPC_IEEE1275_LINK_ADDR 0x200000
++#define GRUB_KERNEL_POWERPC_IEEE1275_LINK_ADDR 0x400000
+ 
+ #define GRUB_KERNEL_MIPS_LOONGSON_LINK_ADDR         0x80200000
+ 
diff --git a/SOURCES/0191-grub-mkconfig-restore-umask-for-grub.cfg.patch b/SOURCES/0191-grub-mkconfig-restore-umask-for-grub.cfg.patch
new file mode 100644
index 0000000..76b73f5
--- /dev/null
+++ b/SOURCES/0191-grub-mkconfig-restore-umask-for-grub.cfg.patch
@@ -0,0 +1,40 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Chang via Grub-devel <grub-devel@gnu.org>
+Date: Fri, 3 Dec 2021 16:13:28 +0800
+Subject: [PATCH] grub-mkconfig: restore umask for grub.cfg
+
+Since commit:
+
+  ab2e53c8a grub-mkconfig: Honor a symlink when generating configuration
+by grub-mkconfig
+
+has inadvertently discarded umask for creating grub.cfg in the process
+of grub-mkconfig. The resulting wrong permission (0644) would allow
+unprivileged users to read grub's configuration file content. This
+presents a low confidentiality risk as grub.cfg may contain non-secured
+plain-text passwords.
+
+This patch restores the missing umask and set the file mode of creation
+to 0600 preventing unprivileged access.
+
+Fixes: CVE-2021-3981
+
+Signed-off-by: Michael Chang <mchang@suse.com>
+---
+ util/grub-mkconfig.in | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in
+index f55339a3f6..520a672cd2 100644
+--- a/util/grub-mkconfig.in
++++ b/util/grub-mkconfig.in
+@@ -311,7 +311,9 @@ and /etc/grub.d/* files or please file a bug report with
+     exit 1
+   else
+     # none of the children aborted with error, install the new grub.cfg
++    oldumask=$(umask); umask 077
+     cat ${grub_cfg}.new > ${grub_cfg}
++    umask $oldumask
+     rm -f ${grub_cfg}.new
+   fi
+ fi
diff --git a/SOURCES/0192-fs-btrfs-Use-full-btrfs-bootloader-area.patch b/SOURCES/0192-fs-btrfs-Use-full-btrfs-bootloader-area.patch
new file mode 100644
index 0000000..3f7198f
--- /dev/null
+++ b/SOURCES/0192-fs-btrfs-Use-full-btrfs-bootloader-area.patch
@@ -0,0 +1,160 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Chang <mchang@suse.com>
+Date: Mon, 13 Dec 2021 14:25:49 +0800
+Subject: [PATCH] fs/btrfs: Use full btrfs bootloader area
+
+Up to now GRUB can only embed to the first 64 KiB before primary
+superblock of btrfs, effectively limiting the GRUB core size. That
+could consequently pose restrictions to feature enablement like
+advanced zstd compression.
+
+This patch attempts to utilize full unused area reserved by btrfs for
+the bootloader outlined in the document [1]:
+
+  The first 1MiB on each device is unused with the exception of primary
+  superblock that is on the offset 64KiB and spans 4KiB.
+
+Apart from that, adjacent sectors to superblock and first block group
+are not used for embedding in case of overflow and logged access to
+adjacent sectors could be useful for tracing it up.
+
+This patch has been tested to provide out of the box support for btrfs
+zstd compression with which GRUB has been installed to the partition.
+
+[1] https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#BOOTLOADER_SUPPORT
+
+Signed-off-by: Michael Chang <mchang@suse.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit b0f06a81c6f31b6fa20be67a96b6683bba8210c9)
+---
+ grub-core/fs/btrfs.c | 90 ++++++++++++++++++++++++++++++++++++++++++++--------
+ include/grub/disk.h  |  2 ++
+ 2 files changed, 79 insertions(+), 13 deletions(-)
+
+diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
+index 4cc86e9b79..07c0ff874b 100644
+--- a/grub-core/fs/btrfs.c
++++ b/grub-core/fs/btrfs.c
+@@ -2476,6 +2476,33 @@ grub_btrfs_label (grub_device_t device, char **label)
+ }
+ 
+ #ifdef GRUB_UTIL
++
++struct embed_region {
++  unsigned int start;
++  unsigned int secs;
++};
++
++/*
++ * https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#BOOTLOADER_SUPPORT
++ * The first 1 MiB on each device is unused with the exception of primary
++ * superblock that is on the offset 64 KiB and spans 4 KiB.
++ */
++
++static const struct {
++  struct embed_region available;
++  struct embed_region used[6];
++} btrfs_head = {
++  .available = {0, GRUB_DISK_KiB_TO_SECTORS (1024)}, /* The first 1 MiB. */
++  .used = {
++    {0, 1},                                                        /* boot.S. */
++    {GRUB_DISK_KiB_TO_SECTORS (64) - 1, 1},                        /* Overflow guard. */
++    {GRUB_DISK_KiB_TO_SECTORS (64), GRUB_DISK_KiB_TO_SECTORS (4)}, /* 4 KiB superblock. */
++    {GRUB_DISK_KiB_TO_SECTORS (68), 1},                            /* Overflow guard. */
++    {GRUB_DISK_KiB_TO_SECTORS (1024) - 1, 1},                      /* Overflow guard. */
++    {0, 0}                                                         /* Array terminator. */
++  }
++};
++
+ static grub_err_t
+ grub_btrfs_embed (grub_device_t device __attribute__ ((unused)),
+ 		  unsigned int *nsectors,
+@@ -2483,25 +2510,62 @@ grub_btrfs_embed (grub_device_t device __attribute__ ((unused)),
+ 		  grub_embed_type_t embed_type,
+ 		  grub_disk_addr_t **sectors)
+ {
+-  unsigned i;
++  unsigned int i, j, n = 0;
++  const struct embed_region *u;
++  grub_disk_addr_t *map;
+ 
+   if (embed_type != GRUB_EMBED_PCBIOS)
+     return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ 		       "BtrFS currently supports only PC-BIOS embedding");
+ 
+-  if (64 * 2 - 1 < *nsectors)
+-    return grub_error (GRUB_ERR_OUT_OF_RANGE,
+-		       N_("your core.img is unusually large.  "
+-			  "It won't fit in the embedding area"));
+-
+-  *nsectors = 64 * 2 - 1;
+-  if (*nsectors > max_nsectors)
+-    *nsectors = max_nsectors;
+-  *sectors = grub_calloc (*nsectors, sizeof (**sectors));
+-  if (!*sectors)
++  map = grub_calloc (btrfs_head.available.secs, sizeof (*map));
++  if (map == NULL)
+     return grub_errno;
+-  for (i = 0; i < *nsectors; i++)
+-    (*sectors)[i] = i + 1;
++
++  /*
++   * Populating the map array so that it can be used to index if a disk
++   * address is available to embed:
++   *   - 0: available,
++   *   - 1: unavailable.
++   */
++  for (u = btrfs_head.used; u->secs; ++u)
++    {
++      unsigned int end = u->start + u->secs;
++
++      if (end > btrfs_head.available.secs)
++        end = btrfs_head.available.secs;
++      for (i = u->start; i < end; ++i)
++        map[i] = 1;
++    }
++
++  /* Adding up n until it matches total size of available embedding area. */
++  for (i = 0; i < btrfs_head.available.secs; ++i)
++    if (map[i] == 0)
++      n++;
++
++  if (n < *nsectors)
++    {
++      grub_free (map);
++      return grub_error (GRUB_ERR_OUT_OF_RANGE,
++		         N_("your core.img is unusually large.  "
++			    "It won't fit in the embedding area"));
++    }
++
++  if (n > max_nsectors)
++    n = max_nsectors;
++
++  /*
++   * Populating the array so that it can used to index disk block address for
++   * an image file's offset to be embedded on disk (the unit is in sectors):
++   *   - i: The disk block address relative to btrfs_head.available.start,
++   *   - j: The offset in image file.
++   */
++  for (i = 0, j = 0; i < btrfs_head.available.secs && j < n; ++i)
++    if (map[i] == 0)
++      map[j++] = btrfs_head.available.start + i;
++
++  *nsectors = n;
++  *sectors = map;
+ 
+   return GRUB_ERR_NONE;
+ }
+diff --git a/include/grub/disk.h b/include/grub/disk.h
+index f95aca929a..06210a7049 100644
+--- a/include/grub/disk.h
++++ b/include/grub/disk.h
+@@ -182,6 +182,8 @@ typedef struct grub_disk_memberlist *grub_disk_memberlist_t;
+ /* Return value of grub_disk_native_sectors() in case disk size is unknown. */
+ #define GRUB_DISK_SIZE_UNKNOWN	 0xffffffffffffffffULL
+ 
++#define GRUB_DISK_KiB_TO_SECTORS(x) ((x) << (10 - GRUB_DISK_SECTOR_BITS))
++
+ /* Convert sector number from one sector size to another. */
+ static inline grub_disk_addr_t
+ grub_convert_sector (grub_disk_addr_t sector,
diff --git a/SOURCES/0193-Add-Fedora-location-of-DejaVu-SANS-font.patch b/SOURCES/0193-Add-Fedora-location-of-DejaVu-SANS-font.patch
new file mode 100644
index 0000000..070fc24
--- /dev/null
+++ b/SOURCES/0193-Add-Fedora-location-of-DejaVu-SANS-font.patch
@@ -0,0 +1,30 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: fluteze <fluteze@gmail.com>
+Date: Sat, 27 Nov 2021 10:54:44 -0600
+Subject: [PATCH] Add Fedora location of DejaVu SANS font
+
+In Fedora 35, and possibly earlier, grub would fail to configure with a
+complaint about DejaVu being "not found" even though it was installed.
+The DejaVu sans font search path is updated to reflect the
+distribution's current install path.
+
+Signed-off-by: Erik Edwards <fluteze@gmail.com>
+[rharwood@redhat.com: slight commit message edits]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ configure.ac | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/configure.ac b/configure.ac
+index ab0d326f00..40c4338bce 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1784,7 +1784,7 @@ fi
+ 
+ if test x"$starfield_excuse" = x; then
+    for ext in pcf pcf.gz bdf bdf.gz ttf ttf.gz; do
+-     for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/truetype/ttf-dejavu /usr/share/fonts/dejavu /usr/share/fonts/truetype; do
++     for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/truetype/ttf-dejavu /usr/share/fonts/dejavu /usr/share/fonts/truetype /usr/share/fonts/dejavu-sans-fonts; do
+         if test -f "$dir/DejaVuSans.$ext"; then
+           DJVU_FONT_SOURCE="$dir/DejaVuSans.$ext"
+           break 2
diff --git a/SOURCES/0194-normal-menu-Don-t-show-Booting-s-msg-when-auto-booti.patch b/SOURCES/0194-normal-menu-Don-t-show-Booting-s-msg-when-auto-booti.patch
new file mode 100644
index 0000000..200cd51
--- /dev/null
+++ b/SOURCES/0194-normal-menu-Don-t-show-Booting-s-msg-when-auto-booti.patch
@@ -0,0 +1,90 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Fri, 28 Jan 2022 11:30:32 +0100
+Subject: [PATCH] normal/menu: Don't show "Booting `%s'" msg when auto-booting
+ with TIMEOUT_STYLE_HIDDEN
+
+When the user has asked the menu code to be hidden/quiet and the current
+entry is being autobooted because the timeout has expired don't show
+the "Booting `%s'" msg.
+
+This is necessary to let flicker-free boots really be flicker free,
+otherwise the "Booting `%s'" msg will kick the EFI fb into text mode
+and show the msg, breaking the flicker-free experience.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ grub-core/normal/menu.c | 24 ++++++++++++++++--------
+ 1 file changed, 16 insertions(+), 8 deletions(-)
+
+diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c
+index ec0c92bade..c8516a5a08 100644
+--- a/grub-core/normal/menu.c
++++ b/grub-core/normal/menu.c
+@@ -606,13 +606,15 @@ print_countdown (struct grub_term_coordinate *pos, int n)
+    entry to be executed is a result of an automatic default selection because
+    of the timeout.  */
+ static int
+-run_menu (grub_menu_t menu, int nested, int *auto_boot)
++run_menu (grub_menu_t menu, int nested, int *auto_boot, int *notify_boot)
+ {
+   grub_uint64_t saved_time;
+   int default_entry, current_entry;
+   int timeout;
+   enum timeout_style timeout_style;
+ 
++  *notify_boot = 1;
++
+   default_entry = get_entry_number (menu, "default");
+ 
+   /* If DEFAULT_ENTRY is not within the menu entries, fall back to
+@@ -687,6 +689,7 @@ run_menu (grub_menu_t menu, int nested, int *auto_boot)
+   if (timeout == 0)
+     {
+       *auto_boot = 1;
++      *notify_boot = timeout_style != TIMEOUT_STYLE_HIDDEN;
+       return default_entry;
+     }
+ 
+@@ -840,12 +843,16 @@ 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,
+-		void *userdata __attribute__((unused)))
++notify_booting (grub_menu_entry_t entry, void *userdata)
+ {
+-  grub_printf ("  ");
+-  grub_printf_ (N_("Booting `%s'"), entry->title);
+-  grub_printf ("\n\n");
++  int *notify_boot = userdata;
++
++  if (*notify_boot)
++    {
++      grub_printf ("  ");
++      grub_printf_ (N_("Booting `%s'"), entry->title);
++      grub_printf ("\n\n");
++    }
+ }
+ 
+ /* Callback invoked when a default menu entry executed because of a timeout
+@@ -893,8 +900,9 @@ show_menu (grub_menu_t menu, int nested, int autobooted)
+       int boot_entry;
+       grub_menu_entry_t e;
+       int auto_boot;
++      int notify_boot;
+ 
+-      boot_entry = run_menu (menu, nested, &auto_boot);
++      boot_entry = run_menu (menu, nested, &auto_boot, &notify_boot);
+       if (boot_entry < 0)
+ 	break;
+ 
+@@ -906,7 +914,7 @@ show_menu (grub_menu_t menu, int nested, int autobooted)
+ 
+       if (auto_boot)
+ 	grub_menu_execute_with_fallback (menu, e, autobooted,
+-					 &execution_callback, 0);
++					 &execution_callback, &notify_boot);
+       else
+ 	grub_menu_execute_entry (e, 0);
+       if (autobooted)
diff --git a/SOURCES/0195-EFI-suppress-the-Welcome-to-GRUB-message-in-EFI-buil.patch b/SOURCES/0195-EFI-suppress-the-Welcome-to-GRUB-message-in-EFI-buil.patch
new file mode 100644
index 0000000..0f99c22
--- /dev/null
+++ b/SOURCES/0195-EFI-suppress-the-Welcome-to-GRUB-message-in-EFI-buil.patch
@@ -0,0 +1,41 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Fri, 28 Jan 2022 11:30:33 +0100
+Subject: [PATCH] EFI: suppress the "Welcome to GRUB!" message in EFI builds
+
+Grub EFI builds are now often used in combination with flicker-free
+boot, but this breaks with upstream grub because the "Welcome to GRUB!"
+message will kick the EFI fb into text mode and show the msg,
+breaking the flicker-free experience.
+
+EFI systems are so fast, that when the menu or the countdown are enabled
+the message will be immediately overwritten, so in these cases not
+printing the message does not matter.
+
+And in case when the timeout_style is set to TIMEOUT_STYLE_HIDDEN,
+the user has asked grub to be quiet (for example to allow flickfree
+boot) annd thus the message should not be printed.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ grub-core/kern/main.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c
+index 3fc3401472..993b8a8598 100644
+--- a/grub-core/kern/main.c
++++ b/grub-core/kern/main.c
+@@ -317,10 +317,13 @@ grub_main (void)
+ 
+   grub_boot_time ("After machine init.");
+ 
++  /* This breaks flicker-free boot on EFI systems, so disable it there. */
++#ifndef GRUB_MACHINE_EFI
+   /* Hello.  */
+   grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
+   grub_printf ("Welcome to GRUB!\n\n");
+   grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
++#endif
+ 
+   /* Init verifiers API. */
+   grub_verifiers_init ();
diff --git a/SOURCES/0196-EFI-console-Do-not-set-colorstate-until-the-first-te.patch b/SOURCES/0196-EFI-console-Do-not-set-colorstate-until-the-first-te.patch
new file mode 100644
index 0000000..ade6aae
--- /dev/null
+++ b/SOURCES/0196-EFI-console-Do-not-set-colorstate-until-the-first-te.patch
@@ -0,0 +1,51 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Fri, 28 Jan 2022 12:43:48 +0100
+Subject: [PATCH] EFI: console: Do not set colorstate until the first text
+ output
+
+GRUB_MOD_INIT(normal) does an unconditional:
+
+grub_env_set ("color_normal", "light-gray/black");
+
+which triggers a grub_term_setcolorstate() call. The original version
+of the "efi/console: Do not set text-mode until we actually need it" patch:
+https://lists.gnu.org/archive/html/grub-devel/2018-03/msg00125.html
+
+Protected against this by caching the requested state in
+grub_console_setcolorstate () and then only applying it when the first
+text output actually happens. During refactoring to move the
+grub_console_setcolorstate () up higher in the grub-core/term/efi/console.c
+file the code to cache the color-state + bail early was accidentally
+dropped.
+
+Restore the cache the color-state + bail early behavior from the original.
+
+Cc: Javier Martinez Canillas <javierm@redhat.com>
+Fixes: 2d7c3abd871f ("efi/console: Do not set text-mode until we actually need it")
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ grub-core/term/efi/console.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/grub-core/term/efi/console.c b/grub-core/term/efi/console.c
+index 2f1ae85ba7..c44b2ac318 100644
+--- a/grub-core/term/efi/console.c
++++ b/grub-core/term/efi/console.c
+@@ -82,6 +82,16 @@ grub_console_setcolorstate (struct grub_term_output *term
+ {
+   grub_efi_simple_text_output_interface_t *o;
+ 
++  if (grub_efi_is_finished || text_mode != GRUB_TEXT_MODE_AVAILABLE)
++    {
++      /*
++       * Cache colorstate changes before the first text-output, this avoids
++       * "color_normal" environment writes causing a switch to textmode.
++       */
++      text_colorstate = state;
++      return;
++    }
++
+   if (grub_efi_is_finished)
+     return;
+ 
diff --git a/SOURCES/0197-EFI-console-Do-not-set-cursor-until-the-first-text-o.patch b/SOURCES/0197-EFI-console-Do-not-set-cursor-until-the-first-text-o.patch
new file mode 100644
index 0000000..5fab5db
--- /dev/null
+++ b/SOURCES/0197-EFI-console-Do-not-set-cursor-until-the-first-text-o.patch
@@ -0,0 +1,71 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Fri, 28 Jan 2022 12:43:49 +0100
+Subject: [PATCH] EFI: console: Do not set cursor until the first text output
+
+To allow flickerfree boot the EFI console code does not call
+grub_efi_set_text_mode (1) until some text is actually output.
+
+Depending on if the output text is because of an error loading
+e.g. the .cfg file; or because of showing the menu the cursor needs
+to be on or off when the first text is shown.
+
+So far the cursor was hardcoded to being on, but this is causing
+drawing artifacts + slow drawing of the menu as reported here:
+https://bugzilla.redhat.com/show_bug.cgi?id=1946969
+
+Handle the cursorstate in the same way as the colorstate to fix this,
+when no text has been output yet, just cache the cursorstate and
+then use the last set value when the first text is output.
+
+Fixes: 2d7c3abd871f ("efi/console: Do not set text-mode until we actually need it")
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ grub-core/term/efi/console.c | 19 ++++++++++++++++---
+ 1 file changed, 16 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/term/efi/console.c b/grub-core/term/efi/console.c
+index c44b2ac318..a3622e4fe5 100644
+--- a/grub-core/term/efi/console.c
++++ b/grub-core/term/efi/console.c
+@@ -31,7 +31,15 @@ typedef enum {
+ }
+ grub_text_mode;
+ 
++typedef enum {
++    GRUB_CURSOR_MODE_UNDEFINED = -1,
++    GRUB_CURSOR_MODE_OFF = 0,
++    GRUB_CURSUR_MODE_ON
++}
++grub_cursor_mode;
++
+ static grub_text_mode text_mode = GRUB_TEXT_MODE_UNDEFINED;
++static grub_cursor_mode cursor_mode = GRUB_CURSOR_MODE_UNDEFINED;
+ static grub_term_color_state text_colorstate = GRUB_TERM_COLOR_UNDEFINED;
+ 
+ static grub_uint32_t
+@@ -119,8 +127,12 @@ grub_console_setcursor (struct grub_term_output *term __attribute__ ((unused)),
+ {
+   grub_efi_simple_text_output_interface_t *o;
+ 
+-  if (grub_efi_is_finished)
+-    return;
++  if (grub_efi_is_finished || text_mode != GRUB_TEXT_MODE_AVAILABLE)
++    {
++      /* Cache cursor changes before the first text-output */
++      cursor_mode = on;
++      return;
++    }
+ 
+   o = grub_efi_system_table->con_out;
+   efi_call_2 (o->enable_cursor, o, on);
+@@ -143,7 +155,8 @@ grub_prepare_for_text_output (struct grub_term_output *term)
+       return GRUB_ERR_BAD_DEVICE;
+     }
+ 
+-  grub_console_setcursor (term, 1);
++  if (cursor_mode != GRUB_CURSOR_MODE_UNDEFINED)
++    grub_console_setcursor (term, cursor_mode);
+   if (text_colorstate != GRUB_TERM_COLOR_UNDEFINED)
+     grub_console_setcolorstate (term, text_colorstate);
+   text_mode = GRUB_TEXT_MODE_AVAILABLE;
diff --git a/SOURCES/0198-Use-visual-indentation-in-config.h.in.patch b/SOURCES/0198-Use-visual-indentation-in-config.h.in.patch
new file mode 100644
index 0000000..978eb07
--- /dev/null
+++ b/SOURCES/0198-Use-visual-indentation-in-config.h.in.patch
@@ -0,0 +1,93 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Wed, 15 Dec 2021 15:46:13 -0500
+Subject: [PATCH] Use visual indentation in config.h.in
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+(cherry picked from commit de8051f34de0aa55c921a510974e5bb27e39c17b)
+[rharwood: GRUB_RPM_CONFIG presence]
+---
+ config.h.in | 58 +++++++++++++++++++++++++++++-----------------------------
+ 1 file changed, 29 insertions(+), 29 deletions(-)
+
+diff --git a/config.h.in b/config.h.in
+index c80e3e0aba..f2ed0066ec 100644
+--- a/config.h.in
++++ b/config.h.in
+@@ -23,47 +23,47 @@
+ #define MINILZO_CFG_SKIP_LZO1X_DECOMPRESS 1
+ 
+ #if defined (GRUB_BUILD)
+-#undef ENABLE_NLS
+-#define BUILD_SIZEOF_LONG @BUILD_SIZEOF_LONG@
+-#define BUILD_SIZEOF_VOID_P @BUILD_SIZEOF_VOID_P@
+-#if defined __APPLE__
+-# if defined __BIG_ENDIAN__
+-#  define BUILD_WORDS_BIGENDIAN 1
+-# else
+-#  define BUILD_WORDS_BIGENDIAN 0
+-# endif
+-#else
+-#define BUILD_WORDS_BIGENDIAN @BUILD_WORDS_BIGENDIAN@
+-#endif
++#  undef ENABLE_NLS
++#  define BUILD_SIZEOF_LONG @BUILD_SIZEOF_LONG@
++#  define BUILD_SIZEOF_VOID_P @BUILD_SIZEOF_VOID_P@
++#  if defined __APPLE__
++#    if defined __BIG_ENDIAN__
++#      define BUILD_WORDS_BIGENDIAN 1
++#    else
++#      define BUILD_WORDS_BIGENDIAN 0
++#    endif
++#  else /* !defined __APPLE__ */
++#    define BUILD_WORDS_BIGENDIAN @BUILD_WORDS_BIGENDIAN@
++#  endif /* !defined __APPLE__ */
+ #elif defined (GRUB_UTIL) || !defined (GRUB_MACHINE)
+-#include <config-util.h>
+-#else
+-#define HAVE_FONT_SOURCE @HAVE_FONT_SOURCE@
++#  include <config-util.h>
++#else /* !defined GRUB_UTIL && defined GRUB_MACHINE */
++#  define HAVE_FONT_SOURCE @HAVE_FONT_SOURCE@
+ /* Define if C symbols get an underscore after compilation. */
+-#define HAVE_ASM_USCORE @HAVE_ASM_USCORE@
++#  define HAVE_ASM_USCORE @HAVE_ASM_USCORE@
+ /* Define it to one of __bss_start, edata and _edata.  */
+-#define BSS_START_SYMBOL @BSS_START_SYMBOL@
++#  define BSS_START_SYMBOL @BSS_START_SYMBOL@
+ /* Define it to either end or _end.  */
+-#define END_SYMBOL @END_SYMBOL@
++#  define END_SYMBOL @END_SYMBOL@
+ /* Name of package.  */
+-#define PACKAGE "@PACKAGE@"
++#  define PACKAGE "@PACKAGE@"
+ /* Version number of package.  */
+-#define VERSION "@VERSION@"
++#  define VERSION "@VERSION@"
+ /* Define to the full name and version of this package. */
+-#define PACKAGE_STRING "@PACKAGE_STRING@"
++#  define PACKAGE_STRING "@PACKAGE_STRING@"
+ /* Define to the version of this package. */
+-#define PACKAGE_VERSION "@PACKAGE_VERSION@"
++#  define PACKAGE_VERSION "@PACKAGE_VERSION@"
+ /* Define to the full name of this package. */
+-#define PACKAGE_NAME "@PACKAGE_NAME@"
++#  define PACKAGE_NAME "@PACKAGE_NAME@"
+ /* Define to the address where bug reports for this package should be sent. */
+-#define PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@"
++#  define PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@"
+ 
+-#define GRUB_TARGET_CPU "@GRUB_TARGET_CPU@"
+-#define GRUB_PLATFORM "@GRUB_PLATFORM@"
+-#define GRUB_RPM_VERSION "@GRUB_RPM_VERSION@"
++#  define GRUB_TARGET_CPU "@GRUB_TARGET_CPU@"
++#  define GRUB_PLATFORM "@GRUB_PLATFORM@"
++#  define GRUB_RPM_VERSION "@GRUB_RPM_VERSION@"
+ 
+-#define RE_ENABLE_I18N 1
++#  define RE_ENABLE_I18N 1
+ 
+-#define _GNU_SOURCE 1
++#  define _GNU_SOURCE 1
+ 
+ #endif
diff --git a/SOURCES/0199-Where-present-ensure-config-util.h-precedes-config.h.patch b/SOURCES/0199-Where-present-ensure-config-util.h-precedes-config.h.patch
new file mode 100644
index 0000000..22c2d9c
--- /dev/null
+++ b/SOURCES/0199-Where-present-ensure-config-util.h-precedes-config.h.patch
@@ -0,0 +1,275 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Tue, 22 Feb 2022 16:57:54 -0500
+Subject: [PATCH] Where present, ensure config-util.h precedes config.h
+
+gnulib defines go in config-util.h, and we need to know whether to
+provide duplicates in config.h or not.
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+(cherry picked from commit 46e82b28e1a75703d0424c7e13d009171310c6cd)
+[rharwood: gensymlist isn't part of tarballs]
+---
+ grub-core/disk/host.c                | 2 +-
+ grub-core/kern/emu/argp_common.c     | 2 +-
+ grub-core/kern/emu/main.c            | 2 +-
+ grub-core/osdep/aros/config.c        | 2 +-
+ grub-core/osdep/basic/emunet.c       | 2 +-
+ grub-core/osdep/basic/init.c         | 2 +-
+ grub-core/osdep/haiku/getroot.c      | 2 +-
+ grub-core/osdep/linux/emunet.c       | 2 +-
+ grub-core/osdep/unix/config.c        | 2 +-
+ grub-core/osdep/unix/cputime.c       | 2 +-
+ grub-core/osdep/unix/dl.c            | 2 +-
+ grub-core/osdep/unix/emuconsole.c    | 2 +-
+ grub-core/osdep/unix/getroot.c       | 2 +-
+ grub-core/osdep/windows/config.c     | 2 +-
+ grub-core/osdep/windows/cputime.c    | 2 +-
+ grub-core/osdep/windows/dl.c         | 2 +-
+ grub-core/osdep/windows/emuconsole.c | 2 +-
+ grub-core/osdep/windows/init.c       | 2 +-
+ 18 files changed, 18 insertions(+), 18 deletions(-)
+
+diff --git a/grub-core/disk/host.c b/grub-core/disk/host.c
+index c151d225df..f34529f86a 100644
+--- a/grub-core/disk/host.c
++++ b/grub-core/disk/host.c
+@@ -20,8 +20,8 @@
+ /* When using the disk, make a reference to this module.  Otherwise
+    the user will end up with a useless module :-).  */
+ 
+-#include <config.h>
+ #include <config-util.h>
++#include <config.h>
+ 
+ #include <grub/dl.h>
+ #include <grub/disk.h>
+diff --git a/grub-core/kern/emu/argp_common.c b/grub-core/kern/emu/argp_common.c
+index 1668858703..8cb4608c3d 100644
+--- a/grub-core/kern/emu/argp_common.c
++++ b/grub-core/kern/emu/argp_common.c
+@@ -17,8 +17,8 @@
+  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ 
+-#include <config.h>
+ #include <config-util.h>
++#include <config.h>
+ 
+ #pragma GCC diagnostic ignored "-Wmissing-prototypes"
+ #pragma GCC diagnostic ignored "-Wmissing-declarations"
+diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c
+index 55ea5a11cc..12277c34d2 100644
+--- a/grub-core/kern/emu/main.c
++++ b/grub-core/kern/emu/main.c
+@@ -16,8 +16,8 @@
+  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ 
+-#include <config.h>
+ #include <config-util.h>
++#include <config.h>
+ 
+ #include <time.h>
+ #include <stdio.h>
+diff --git a/grub-core/osdep/aros/config.c b/grub-core/osdep/aros/config.c
+index c82d0ea8e7..55f5728efc 100644
+--- a/grub-core/osdep/aros/config.c
++++ b/grub-core/osdep/aros/config.c
+@@ -16,8 +16,8 @@
+  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ 
+-#include <config.h>
+ #include <config-util.h>
++#include <config.h>
+ 
+ #include <grub/emu/hostdisk.h>
+ #include <grub/emu/exec.h>
+diff --git a/grub-core/osdep/basic/emunet.c b/grub-core/osdep/basic/emunet.c
+index 6362e5cfbb..dbfd316d61 100644
+--- a/grub-core/osdep/basic/emunet.c
++++ b/grub-core/osdep/basic/emunet.c
+@@ -16,8 +16,8 @@
+  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ 
+-#include <config.h>
+ #include <config-util.h>
++#include <config.h>
+ 
+ #include <grub/i18n.h>
+ #include <grub/emu/net.h>
+diff --git a/grub-core/osdep/basic/init.c b/grub-core/osdep/basic/init.c
+index c54c710dbc..b104c7e162 100644
+--- a/grub-core/osdep/basic/init.c
++++ b/grub-core/osdep/basic/init.c
+@@ -16,8 +16,8 @@
+  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ 
+-#include <config.h>
+ #include <config-util.h>
++#include <config.h>
+ 
+ #include <grub/util/misc.h>
+ #include <grub/i18n.h>
+diff --git a/grub-core/osdep/haiku/getroot.c b/grub-core/osdep/haiku/getroot.c
+index 4e123c0903..927a1ebc94 100644
+--- a/grub-core/osdep/haiku/getroot.c
++++ b/grub-core/osdep/haiku/getroot.c
+@@ -1,5 +1,5 @@
+-#include <config.h>
+ #include <config-util.h>
++#include <config.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <string.h>
+diff --git a/grub-core/osdep/linux/emunet.c b/grub-core/osdep/linux/emunet.c
+index 19b188f09e..d5a6417355 100644
+--- a/grub-core/osdep/linux/emunet.c
++++ b/grub-core/osdep/linux/emunet.c
+@@ -16,8 +16,8 @@
+  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ 
+-#include <config.h>
+ #include <config-util.h>
++#include <config.h>
+ 
+ #include <sys/socket.h>
+ #include <sys/types.h>
+diff --git a/grub-core/osdep/unix/config.c b/grub-core/osdep/unix/config.c
+index 46a881530c..0ce0e309ac 100644
+--- a/grub-core/osdep/unix/config.c
++++ b/grub-core/osdep/unix/config.c
+@@ -16,8 +16,8 @@
+  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ 
+-#include <config.h>
+ #include <config-util.h>
++#include <config.h>
+ 
+ #include <grub/emu/hostdisk.h>
+ #include <grub/emu/exec.h>
+diff --git a/grub-core/osdep/unix/cputime.c b/grub-core/osdep/unix/cputime.c
+index cff359a3b9..fb6ff55a1a 100644
+--- a/grub-core/osdep/unix/cputime.c
++++ b/grub-core/osdep/unix/cputime.c
+@@ -1,5 +1,5 @@
+-#include <config.h>
+ #include <config-util.h>
++#include <config.h>
+ 
+ #include <sys/times.h>
+ #include <unistd.h>
+diff --git a/grub-core/osdep/unix/dl.c b/grub-core/osdep/unix/dl.c
+index 562b101a28..99b189bc1c 100644
+--- a/grub-core/osdep/unix/dl.c
++++ b/grub-core/osdep/unix/dl.c
+@@ -16,8 +16,8 @@
+  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ 
+-#include <config.h>
+ #include <config-util.h>
++#include <config.h>
+ 
+ #include <grub/dl.h>
+ #include <grub/misc.h>
+diff --git a/grub-core/osdep/unix/emuconsole.c b/grub-core/osdep/unix/emuconsole.c
+index 7308798efe..cac159424d 100644
+--- a/grub-core/osdep/unix/emuconsole.c
++++ b/grub-core/osdep/unix/emuconsole.c
+@@ -17,8 +17,8 @@
+  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ 
+-#include <config.h>
+ #include <config-util.h>
++#include <config.h>
+ 
+ #include <grub/term.h>
+ #include <grub/types.h>
+diff --git a/grub-core/osdep/unix/getroot.c b/grub-core/osdep/unix/getroot.c
+index 46d7116c6e..4f436284ce 100644
+--- a/grub-core/osdep/unix/getroot.c
++++ b/grub-core/osdep/unix/getroot.c
+@@ -16,8 +16,8 @@
+  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ 
+-#include <config-util.h>
+ #include <config.h>
++#include <config-util.h>
+ 
+ #include <sys/stat.h>
+ #include <sys/types.h>
+diff --git a/grub-core/osdep/windows/config.c b/grub-core/osdep/windows/config.c
+index 928ab1a49b..2bb8a2fd88 100644
+--- a/grub-core/osdep/windows/config.c
++++ b/grub-core/osdep/windows/config.c
+@@ -16,8 +16,8 @@
+  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ 
+-#include <config.h>
+ #include <config-util.h>
++#include <config.h>
+ 
+ #include <grub/emu/hostfile.h>
+ #include <grub/emu/config.h>
+diff --git a/grub-core/osdep/windows/cputime.c b/grub-core/osdep/windows/cputime.c
+index 3568aa2d35..5d06d79dd5 100644
+--- a/grub-core/osdep/windows/cputime.c
++++ b/grub-core/osdep/windows/cputime.c
+@@ -1,5 +1,5 @@
+-#include <config.h>
+ #include <config-util.h>
++#include <config.h>
+ 
+ #include <grub/emu/misc.h>
+ #include <windows.h>
+diff --git a/grub-core/osdep/windows/dl.c b/grub-core/osdep/windows/dl.c
+index eec6a24ad7..8eab7057e4 100644
+--- a/grub-core/osdep/windows/dl.c
++++ b/grub-core/osdep/windows/dl.c
+@@ -16,8 +16,8 @@
+  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ 
+-#include <config.h>
+ #include <config-util.h>
++#include <config.h>
+ 
+ #include <grub/dl.h>
+ #include <grub/misc.h>
+diff --git a/grub-core/osdep/windows/emuconsole.c b/grub-core/osdep/windows/emuconsole.c
+index 4fb3693cc0..17a44de469 100644
+--- a/grub-core/osdep/windows/emuconsole.c
++++ b/grub-core/osdep/windows/emuconsole.c
+@@ -16,8 +16,8 @@
+  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ 
+-#include <config.h>
+ #include <config-util.h>
++#include <config.h>
+ 
+ #include <grub/term.h>
+ #include <grub/misc.h>
+diff --git a/grub-core/osdep/windows/init.c b/grub-core/osdep/windows/init.c
+index 6297de6326..51a9647dde 100644
+--- a/grub-core/osdep/windows/init.c
++++ b/grub-core/osdep/windows/init.c
+@@ -16,8 +16,8 @@
+  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ 
+-#include <config.h>
+ #include <config-util.h>
++#include <config.h>
+ #include <grub/util/misc.h>
+ #include <grub/osdep/hostfile.h>
+ #include <grub/util/windows.h>
diff --git a/SOURCES/0200-Drop-gnulib-fix-base64.patch.patch b/SOURCES/0200-Drop-gnulib-fix-base64.patch.patch
new file mode 100644
index 0000000..054e476
--- /dev/null
+++ b/SOURCES/0200-Drop-gnulib-fix-base64.patch.patch
@@ -0,0 +1,140 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Thu, 28 Oct 2021 15:07:50 -0400
+Subject: [PATCH] Drop gnulib fix-base64.patch
+
+Originally added in 9fbdec2f6b4fa8b549daa4d49134d1fe89d95ef9 and
+subsequently modified in 552c9fd08122a3036c724ce96dfe68aa2f75705f,
+fix-base64.patch handled two problems we have using gnulib, which are
+exerciesd by the base64 module but not directly caused by it.
+
+First, grub2 defines its own bool type, while gnulib expects the
+equivalent of stdbool.h to be present.  Rather than patching gnulib,
+instead use gnulib's stdbool module to provide a bool type if needed.
+(Suggested by Simon Josefsson.)
+
+Second, our config.h doesn't always inherit config-util.h, which is
+where gnulib-related options like _GL_ATTRIBUTE_CONST end up.
+fix-base64.h worked around this by defining the attribute away, but this
+workaround is better placed in config.h itself, not a gnulib patch.
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+(cherry picked from commit 54fd1c3301dd15f6b6212c12887265e8a6cbc076)
+---
+ grub-core/lib/posix_wrap/sys/types.h          |  7 +++----
+ grub-core/lib/xzembed/xz.h                    |  5 +----
+ bootstrap.conf                                |  3 ++-
+ conf/Makefile.extra-dist                      |  1 -
+ config.h.in                                   |  4 ++++
+ grub-core/lib/gnulib-patches/fix-base64.patch | 21 ---------------------
+ 6 files changed, 10 insertions(+), 31 deletions(-)
+ delete mode 100644 grub-core/lib/gnulib-patches/fix-base64.patch
+
+diff --git a/grub-core/lib/posix_wrap/sys/types.h b/grub-core/lib/posix_wrap/sys/types.h
+index f63412c8da..2f3e865495 100644
+--- a/grub-core/lib/posix_wrap/sys/types.h
++++ b/grub-core/lib/posix_wrap/sys/types.h
+@@ -23,11 +23,10 @@
+ 
+ #include <stddef.h>
+ 
++/* Provided by gnulib if not present. */
++#include <stdbool.h>
++
+ typedef grub_ssize_t ssize_t;
+-#ifndef GRUB_POSIX_BOOL_DEFINED
+-typedef enum { false = 0, true = 1 } bool;
+-#define GRUB_POSIX_BOOL_DEFINED 1
+-#endif
+ 
+ typedef grub_uint8_t uint8_t;
+ typedef grub_uint16_t uint16_t;
+diff --git a/grub-core/lib/xzembed/xz.h b/grub-core/lib/xzembed/xz.h
+index f7b32d8003..d1417039aa 100644
+--- a/grub-core/lib/xzembed/xz.h
++++ b/grub-core/lib/xzembed/xz.h
+@@ -29,10 +29,7 @@
+ #include <unistd.h>
+ #include <string.h>
+ #include <grub/misc.h>
+-
+-#ifndef GRUB_POSIX_BOOL_DEFINED
+-typedef enum { false = 0, true = 1 } bool;
+-#endif
++#include <stdbool.h>
+ 
+ /**
+  * enum xz_ret - Return codes
+diff --git a/bootstrap.conf b/bootstrap.conf
+index 52d4af44be..645e3a459c 100644
+--- a/bootstrap.conf
++++ b/bootstrap.conf
+@@ -35,6 +35,7 @@ gnulib_modules="
+   realloc-gnu
+   regex
+   save-cwd
++  stdbool
+ "
+ 
+ gnulib_tool_option_extras="\
+@@ -79,7 +80,7 @@ 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 \
++  for patchname in 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"
+diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist
+index ad235de7fc..f4791dc6ca 100644
+--- a/conf/Makefile.extra-dist
++++ b/conf/Makefile.extra-dist
+@@ -31,7 +31,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
+diff --git a/config.h.in b/config.h.in
+index f2ed0066ec..9c7b4afaaa 100644
+--- a/config.h.in
++++ b/config.h.in
+@@ -66,4 +66,8 @@
+ 
+ #  define _GNU_SOURCE 1
+ 
++#  ifndef _GL_INLINE_HEADER_BEGIN
++#    define _GL_ATTRIBUTE_CONST __attribute__ ((const))
++#  endif /* !_GL_INLINE_HEADER_BEGIN */
++
+ #endif
+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 985db12797..0000000000
+--- 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/SOURCES/0201-Drop-gnulib-no-abort.patch.patch b/SOURCES/0201-Drop-gnulib-no-abort.patch.patch
new file mode 100644
index 0000000..a12789f
--- /dev/null
+++ b/SOURCES/0201-Drop-gnulib-no-abort.patch.patch
@@ -0,0 +1,97 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Wed, 5 Jan 2022 16:42:11 -0500
+Subject: [PATCH] Drop gnulib no-abort.patch
+
+Originally added in db7337a3d353a817ffe9eb4a3702120527100be9, this
+patched out all relevant invocations of abort() in gnulib.  While it was
+not documented why at the time, testing suggests that there's no abort()
+implementation available for gnulib to use.
+
+gnulib's position is that the use of abort() is correct here, since it
+happens when input violates a "shall" from POSIX.  Additionally, the
+code in question is probably not reachable.  Since abort() is more
+friendly to user-space, they prefer to make no change, so we can just
+carry a define instead.  (Suggested by Paul Eggert.)
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+(cherry picked from commit 5137c8eb3ec11c3217acea1a93a3f88f3fa4cbca)
+---
+ bootstrap.conf                              |  2 +-
+ conf/Makefile.extra-dist                    |  1 -
+ config.h.in                                 |  3 +++
+ grub-core/lib/gnulib-patches/no-abort.patch | 26 --------------------------
+ 4 files changed, 4 insertions(+), 28 deletions(-)
+ delete mode 100644 grub-core/lib/gnulib-patches/no-abort.patch
+
+diff --git a/bootstrap.conf b/bootstrap.conf
+index 645e3a459c..71ce943c7d 100644
+--- a/bootstrap.conf
++++ b/bootstrap.conf
+@@ -81,7 +81,7 @@ cp -a INSTALL INSTALL.grub
+ bootstrap_post_import_hook () {
+   set -e
+   for patchname in 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; do
+     patch -d grub-core/lib/gnulib -p2 \
+       < "grub-core/lib/gnulib-patches/$patchname.patch"
+   done
+diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist
+index f4791dc6ca..5eef708338 100644
+--- a/conf/Makefile.extra-dist
++++ b/conf/Makefile.extra-dist
+@@ -38,7 +38,6 @@ 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
+diff --git a/config.h.in b/config.h.in
+index 9c7b4afaaa..c3134309c6 100644
+--- a/config.h.in
++++ b/config.h.in
+@@ -68,6 +68,9 @@
+ 
+ #  ifndef _GL_INLINE_HEADER_BEGIN
+ #    define _GL_ATTRIBUTE_CONST __attribute__ ((const))
++
++/* We don't have an abort() for gnulib to call in regexp. */
++#    define abort __builtin_unreachable
+ #  endif /* !_GL_INLINE_HEADER_BEGIN */
+ 
+ #endif
+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 e469c4762e..0000000000
+--- 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/0202-Update-gnulib-version-and-drop-most-gnulib-patches.patch b/SOURCES/0202-Update-gnulib-version-and-drop-most-gnulib-patches.patch
new file mode 100644
index 0000000..c2c50f1
--- /dev/null
+++ b/SOURCES/0202-Update-gnulib-version-and-drop-most-gnulib-patches.patch
@@ -0,0 +1,832 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Wed, 15 Dec 2021 15:07:50 -0500
+Subject: [PATCH] Update gnulib version and drop most gnulib patches
+
+In addition to the changes carried in our gnulib patches, several
+Coverity and code hygiene fixes that were previously downstream are also
+included in this 3-year gnulib increment.
+
+Unfortunately, fix-width.patch is retained.
+
+Bump minimum autoconf version from 2.63 to 2.64 and automake from 1.11
+to 1.14, as required by gnulib.
+
+Sync bootstrap script itself with gnulib.
+
+Update regexp module for new dynarray dependency.
+
+Fix various new warnings.
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+(cherry picked from commit deb18ff931c3133c2aa536a92bd92e50d6615303)
+[rharwood: backport around requirements in INSTALL]
+---
+ configure.ac                                       |   2 +-
+ grub-core/Makefile.core.def                        |   3 +
+ grub-core/disk/luks2.c                             |   4 +-
+ grub-core/lib/posix_wrap/limits.h                  |   6 +-
+ include/grub/compiler.h                            |   4 +-
+ include/grub/list.h                                |   2 +-
+ INSTALL                                            |   2 +-
+ bootstrap                                          | 291 ++++++++++++---------
+ bootstrap.conf                                     |  22 +-
+ conf/Makefile.extra-dist                           |   6 -
+ config.h.in                                        |  68 +++++
+ 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 -
+ .../lib/gnulib-patches/fix-uninit-structure.patch  |  11 -
+ .../lib/gnulib-patches/fix-unused-value.patch      |  14 -
+ 17 files changed, 262 insertions(+), 225 deletions(-)
+ 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-uninit-structure.patch
+ delete mode 100644 grub-core/lib/gnulib-patches/fix-unused-value.patch
+
+diff --git a/configure.ac b/configure.ac
+index 40c4338bce..79f45ef1e1 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -49,7 +49,7 @@ AC_CANONICAL_TARGET
+ program_prefix="${save_program_prefix}"
+ 
+ AM_INIT_AUTOMAKE([1.11])
+-AC_PREREQ(2.63)
++AC_PREREQ(2.64)
+ AC_CONFIG_SRCDIR([include/grub/dl.h])
+ AC_CONFIG_HEADER([config-util.h])
+ 
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index 08ac0fb15f..ec1ec5083b 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -762,6 +762,9 @@ module = {
+   name = regexp;
+   common = commands/regexp.c;
+   common = commands/wildcard.c;
++  common = lib/gnulib/malloc/dynarray_finalize.c;
++  common = lib/gnulib/malloc/dynarray_emplace_enlarge.c;
++  common = lib/gnulib/malloc/dynarray_resize.c;
+   common = lib/gnulib/regex.c;
+   cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
+   cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)';
+diff --git a/grub-core/disk/luks2.c b/grub-core/disk/luks2.c
+index 371a53b837..c917a5f91e 100644
+--- a/grub-core/disk/luks2.c
++++ b/grub-core/disk/luks2.c
+@@ -389,7 +389,7 @@ luks2_verify_key (grub_luks2_digest_t *d, grub_uint8_t *candidate_key,
+ {
+   grub_uint8_t candidate_digest[GRUB_CRYPTODISK_MAX_KEYLEN];
+   grub_uint8_t digest[GRUB_CRYPTODISK_MAX_KEYLEN], salt[GRUB_CRYPTODISK_MAX_KEYLEN];
+-  grub_size_t saltlen = sizeof (salt), digestlen = sizeof (digest);
++  idx_t saltlen = sizeof (salt), digestlen = sizeof (digest);
+   const gcry_md_spec_t *hash;
+   gcry_err_code_t gcry_ret;
+ 
+@@ -428,7 +428,7 @@ luks2_decrypt_key (grub_uint8_t *out_key,
+   grub_uint8_t area_key[GRUB_CRYPTODISK_MAX_KEYLEN];
+   grub_uint8_t salt[GRUB_CRYPTODISK_MAX_KEYLEN];
+   grub_uint8_t *split_key = NULL;
+-  grub_size_t saltlen = sizeof (salt);
++  idx_t saltlen = sizeof (salt);
+   char cipher[32], *p;
+   const gcry_md_spec_t *hash;
+   gcry_err_code_t gcry_ret;
+diff --git a/grub-core/lib/posix_wrap/limits.h b/grub-core/lib/posix_wrap/limits.h
+index 591dbf3289..4be7b40806 100644
+--- a/grub-core/lib/posix_wrap/limits.h
++++ b/grub-core/lib/posix_wrap/limits.h
+@@ -25,7 +25,11 @@
+ #define USHRT_MAX GRUB_USHRT_MAX
+ #define UINT_MAX GRUB_UINT_MAX
+ #define ULONG_MAX GRUB_ULONG_MAX
+-#define SIZE_MAX GRUB_SIZE_MAX
++
++/* gnulib also defines this type */
++#ifndef SIZE_MAX
++#  define SIZE_MAX GRUB_SIZE_MAX
++#endif
+ 
+ #define SCHAR_MIN GRUB_SCHAR_MIN
+ #define SCHAR_MAX GRUB_SCHAR_MAX
+diff --git a/include/grub/compiler.h b/include/grub/compiler.h
+index ebafec6895..441a9eca07 100644
+--- a/include/grub/compiler.h
++++ b/include/grub/compiler.h
+@@ -30,10 +30,10 @@
+ 
+ /* Does this compiler support compile-time error attributes? */
+ #if GNUC_PREREQ(4,3)
+-#  define ATTRIBUTE_ERROR(msg) \
++#  define GRUB_ATTRIBUTE_ERROR(msg) \
+ 	__attribute__ ((__error__ (msg)))
+ #else
+-#  define ATTRIBUTE_ERROR(msg) __attribute__ ((noreturn))
++#  define GRUB_ATTRIBUTE_ERROR(msg) __attribute__ ((noreturn))
+ #endif
+ 
+ #if GNUC_PREREQ(4,4)
+diff --git a/include/grub/list.h b/include/grub/list.h
+index b13acb9624..21f4b4b44a 100644
+--- a/include/grub/list.h
++++ b/include/grub/list.h
+@@ -40,7 +40,7 @@ void EXPORT_FUNC(grub_list_remove) (grub_list_t item);
+ 
+ static inline void *
+ grub_bad_type_cast_real (int line, const char *file)
+-     ATTRIBUTE_ERROR ("bad type cast between incompatible grub types");
++     GRUB_ATTRIBUTE_ERROR ("bad type cast between incompatible grub types");
+ 
+ static inline void *
+ grub_bad_type_cast_real (int line, const char *file)
+diff --git a/INSTALL b/INSTALL
+index 79a0af7d93..ee9f536f76 100644
+--- a/INSTALL
++++ b/INSTALL
+@@ -42,7 +42,7 @@ If you use a development snapshot or want to hack on GRUB you may
+ need the following.
+ 
+ * Python 2.6 or later
+-* Autoconf 2.63 or later
++* Autoconf 2.64 or later
+ * Automake 1.11 or later
+ 
+ Prerequisites for make-check:
+diff --git a/bootstrap b/bootstrap
+index 5b08e7e2d4..dc2238f4ad 100755
+--- a/bootstrap
++++ b/bootstrap
+@@ -1,10 +1,10 @@
+ #! /bin/sh
+ # Print a version string.
+-scriptversion=2019-01-04.17; # UTC
++scriptversion=2022-01-26.05; # UTC
+ 
+ # Bootstrap this package from checked-out sources.
+ 
+-# Copyright (C) 2003-2019 Free Software Foundation, Inc.
++# Copyright (C) 2003-2022 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
+@@ -47,7 +47,7 @@ PERL="${PERL-perl}"
+ 
+ me=$0
+ 
+-default_gnulib_url=git://git.sv.gnu.org/gnulib
++default_gnulib_url=https://git.savannah.gnu.org/git/gnulib.git
+ 
+ usage() {
+   cat <<EOF
+@@ -71,7 +71,9 @@ Options:
+  --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
+-
++EOF
++  bootstrap_print_option_usage_hook
++  cat <<EOF
+ If the file $me.conf exists in the same directory as this script, its
+ contents are read as shell variables to configure the bootstrap.
+ 
+@@ -113,6 +115,12 @@ Running without arguments will suffice in most cases.
+ EOF
+ }
+ 
++copyright_year=`echo "$scriptversion" | sed -e 's/[^0-9].*//'`
++copyright="Copyright (C) ${copyright_year} Free Software Foundation, Inc.
++License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
++This is free software: you are free to change and redistribute it.
++There is NO WARRANTY, to the extent permitted by law."
++
+ # warnf_ FORMAT-STRING ARG1...
+ warnf_ ()
+ {
+@@ -154,6 +162,18 @@ gnulib_files=
+ : ${AUTOPOINT=autopoint}
+ : ${AUTORECONF=autoreconf}
+ 
++# A function to be called for each unrecognized option.  Returns 0 if
++# the option in $1 has been processed by the function.  Returns 1 if
++# the option has not been processed by the function.  Override it via
++# your own definition in bootstrap.conf
++
++bootstrap_option_hook() { return 1; }
++
++# A function to be called in order to print the --help information
++# corresponding to user-defined command-line options.
++
++bootstrap_print_option_usage_hook() { :; }
++
+ # A function to be called right after gnulib-tool is run.
+ # Override it via your own definition in bootstrap.conf.
+ bootstrap_post_import_hook() { :; }
+@@ -166,11 +186,11 @@ bootstrap_epilogue() { :; }
+ # 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' \
++"wget --mirror --level=1 -nd -nv -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)
++# fall back to the package name (1st argument with munging).
+ extract_package_name='
+   /^AC_INIT(\[*/{
+      s///
+@@ -187,8 +207,11 @@ extract_package_name='
+      p
+   }
+ '
+-package=$(sed -n "$extract_package_name" configure.ac) \
+-  || die 'cannot find package name in configure.ac'
++package=$(${AUTOCONF:-autoconf} --trace AC_INIT:\$4 configure.ac 2>/dev/null)
++if test -z "$package"; then
++  package=$(sed -n "$extract_package_name" configure.ac) \
++      || die 'cannot find package name in configure.ac'
++fi
+ gnulib_name=lib$package
+ 
+ build_aux=build-aux
+@@ -290,6 +313,116 @@ find_tool ()
+   eval "export $find_tool_envvar"
+ }
+ 
++# 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"
++}
++
++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
++  }
++}
++
+ # Override the default configuration, if necessary.
+ # Make sure that bootstrap.conf is sourced from the current directory
+ # if we were invoked as "sh bootstrap".
+@@ -320,6 +453,12 @@ do
+   --help)
+     usage
+     exit;;
++  --version)
++    set -e
++    echo "bootstrap $scriptversion"
++    echo "$copyright"
++    exit 0
++    ;;
+   --gnulib-srcdir=*)
+     GNULIB_SRCDIR=${option#--gnulib-srcdir=};;
+   --skip-po)
+@@ -335,7 +474,7 @@ do
+   --no-git)
+     use_git=false;;
+   *)
+-    die "$option: unknown option";;
++    bootstrap_option_hook $option || die "$option: unknown option";;
+   esac
+ done
+ 
+@@ -346,47 +485,6 @@ 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 \
+@@ -665,9 +763,25 @@ if $use_gnulib; then
+       shallow=
+       if test -z "$GNULIB_REVISION"; then
+         git clone -h 2>&1 | grep -- --depth > /dev/null && shallow='--depth 2'
++        git clone $shallow ${GNULIB_URL:-$default_gnulib_url} "$gnulib_path" \
++          || cleanup_gnulib
++      else
++        git fetch -h 2>&1 | grep -- --depth > /dev/null && shallow='--depth 2'
++        mkdir -p "$gnulib_path"
++        # Only want a shallow checkout of $GNULIB_REVISION, but git does not
++        # support cloning by commit hash. So attempt a shallow fetch by commit
++        # hash to minimize the amount of data downloaded and changes needed to
++        # be processed, which can drastically reduce download and processing
++        # time for checkout. If the fetch by commit fails, a shallow fetch can
++        # not be performed because we do not know what the depth of the commit
++        # is without fetching all commits. So fallback to fetching all commits.
++        git -C "$gnulib_path" init
++        git -C "$gnulib_path" remote add origin ${GNULIB_URL:-$default_gnulib_url}
++        git -C "$gnulib_path" fetch $shallow origin "$GNULIB_REVISION" \
++          || git -C "$gnulib_path" fetch origin \
++          || cleanup_gnulib
++        git -C "$gnulib_path" reset --hard FETCH_HEAD
+       fi
+-      git clone $shallow ${GNULIB_URL:-$default_gnulib_url} "$gnulib_path" \
+-        || cleanup_gnulib
+ 
+       trap - 1 2 13 15
+     fi
+@@ -784,75 +898,6 @@ case $SKIP_PO in
+   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
+@@ -970,7 +1015,7 @@ bootstrap_post_import_hook \
+ # 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."
++      "Run 'git submodule update --init' and bootstrap again."
+ fi
+ 
+ # Remove any dangling symlink matching "*.m4" or "*.[ch]" in some
+@@ -1064,7 +1109,7 @@ bootstrap_epilogue
+ 
+ echo "$0: done.  Now you can run './configure'."
+ 
+-# Local variables:
++# Local Variables:
+ # eval: (add-hook 'before-save-hook 'time-stamp)
+ # time-stamp-start: "scriptversion="
+ # time-stamp-format: "%:y-%02m-%02d.%02H"
+diff --git a/bootstrap.conf b/bootstrap.conf
+index 71ce943c7d..e4e5f3750a 100644
+--- a/bootstrap.conf
++++ b/bootstrap.conf
+@@ -1,6 +1,6 @@
+ # Bootstrap configuration.
+ 
+-# Copyright (C) 2006-2019 Free Software Foundation, Inc.
++# Copyright (C) 2006-2022 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
+@@ -16,11 +16,10 @@
+ # along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ 
+ 
+-GNULIB_REVISION=d271f868a8df9bbec29049d01e056481b7a1a263
++GNULIB_REVISION=9f48fb992a3d7e96610c4ce8be969cff2d61a01b
+ 
+ # gnulib modules used by this package.
+-# mbswidth is used by gnulib-fix-width.diff's changes to argp rather than
+-# directly.
++# mbswidth is used by fix-width.diff's changes to argp rather than directly.
+ gnulib_modules="
+   argp
+   base64
+@@ -67,8 +66,8 @@ SKIP_PO=t
+ 
+ # Build prerequisites
+ buildreq="\
+-autoconf   2.63
+-automake   1.11
++autoconf   2.64
++automake   1.14
+ gettext    0.18.3
+ git        1.5.5
+ tar        -
+@@ -80,11 +79,12 @@ cp -a INSTALL INSTALL.grub
+ 
+ bootstrap_post_import_hook () {
+   set -e
+-  for patchname in fix-null-deref fix-null-state-deref fix-regcomp-uninit-token \
+-      fix-regexec-null-deref fix-uninit-structure fix-unused-value fix-width; do
+-    patch -d grub-core/lib/gnulib -p2 \
+-      < "grub-core/lib/gnulib-patches/$patchname.patch"
+-  done
++
++  # Instead of patching our gnulib and therefore maintaining a fork, submit
++  # changes to gnulib and update the hash above when they've merged.  Do not
++  # add new patches here.
++  patch -d grub-core/lib/gnulib -p2 < grub-core/lib/gnulib-patches/fix-width.patch
++
+   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 5eef708338..26ac8765e3 100644
+--- a/conf/Makefile.extra-dist
++++ b/conf/Makefile.extra-dist
+@@ -31,12 +31,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-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/libgcrypt
+diff --git a/config.h.in b/config.h.in
+index c3134309c6..512d1bbe13 100644
+--- a/config.h.in
++++ b/config.h.in
+@@ -67,10 +67,78 @@
+ #  define _GNU_SOURCE 1
+ 
+ #  ifndef _GL_INLINE_HEADER_BEGIN
++/* gnulib gets configured against the host, not the target, and the rest of
++ * our buildsystem works around that.  This is difficult to avoid as gnulib's
++ * detection requires a more capable system than our target.  Instead, we
++ * reach in and set values appropriately - intentionally setting more than the
++ * bare minimum.  If, when updating gnulib, something breaks, there's probably
++ * a change needed here or in grub-core/Makefile.core.def. */
++#    define SIZE_MAX ((size_t) -1)
++#    define _GL_ATTRIBUTE_ALLOC_SIZE(args) \
++    __attribute__ ((__alloc_size__ args))
++#    define _GL_ATTRIBUTE_ALWAYS_INLINE __attribute__ ((__always_inline__))
++#    define _GL_ATTRIBUTE_ARTIFICIAL __attribute__ ((__artificial__))
++#    define _GL_ATTRIBUTE_COLD __attribute__ ((cold))
+ #    define _GL_ATTRIBUTE_CONST __attribute__ ((const))
++#    define _GL_ATTRIBUTE_DEALLOC(f, i) __attribute ((__malloc__ (f, i)))
++#    define _GL_ATTRIBUTE_DEALLOC_FREE _GL_ATTRIBUTE_DEALLOC (free, 1)
++#    define _GL_ATTRIBUTE_DEPRECATED __attribute__ ((__deprecated__))
++#    define _GL_ATTRIBUTE_ERROR(msg) __attribute__ ((__error__ (msg)))
++#    define _GL_ATTRIBUTE_EXTERNALLY_VISIBLE \
++    __attribute__ ((externally_visible))
++#    define _GL_ATTRIBUTE_FORMAT(spec) __attribute__ ((__format__ spec))
++#    define _GL_ATTRIBUTE_LEAF __attribute__ ((__leaf__))
++#    define _GL_ATTRIBUTE_MALLOC __attribute__ ((malloc))
++#    define _GL_ATTRIBUTE_MAYBE_UNUSED _GL_ATTRIBUTE_UNUSED
++#    define _GL_ATTRIBUTE_MAY_ALIAS __attribute__ ((__may_alias__))
++#    define _GL_ATTRIBUTE_NODISCARD __attribute__ ((__warn_unused_result__))
++#    define _GL_ATTRIBUTE_NOINLINE __attribute__ ((__noinline__))
++#    define _GL_ATTRIBUTE_NONNULL(args) __attribute__ ((__nonnull__ args))
++#    define _GL_ATTRIBUTE_NONSTRING __attribute__ ((__nonstring__))
++#    define _GL_ATTRIBUTE_PACKED __attribute__ ((__packed__))
++#    define _GL_ATTRIBUTE_PURE __attribute__ ((__pure__))
++#    define _GL_ATTRIBUTE_RETURNS_NONNULL \
++    __attribute__ ((__returns_nonnull__))
++#    define _GL_ATTRIBUTE_SENTINEL(pos) __attribute__ ((__sentinel__ pos))
++#    define _GL_ATTRIBUTE_UNUSED __attribute__ ((__unused__))
++#    define _GL_ATTRIBUTE_WARNING(msg) __attribute__ ((__warning__ (msg)))
++#    define _GL_CMP(n1, n2) (((n1) > (n2)) - ((n1) < (n2)))
++#    define _GL_GNUC_PREREQ GNUC_PREREQ
++#    define _GL_INLINE inline
++#    define _GL_UNUSED_LABEL _GL_ATTRIBUTE_UNUSED
++
++/* We can't use __has_attribute for these because gcc-5.1 is too old for
++ * that.  Everything above is present in that version, though. */
++#    if __GNUC__ >= 7
++#      define _GL_ATTRIBUTE_FALLTHROUGH __attribute__ ((fallthrough))
++#    else
++#      define _GL_ATTRIBUTE_FALLTHROUGH /* empty */
++#    endif
++
++#    ifndef ASM_FILE
++typedef __INT_FAST32_TYPE__ int_fast32_t;
++typedef __UINT_FAST32_TYPE__ uint_fast32_t;
++#    endif
++
++/* Ensure ialloc nests static/non-static inline properly. */
++#    define IALLOC_INLINE static inline
++
++/* gnulib uses these for blocking out warnings they can't/won't fix.  gnulib
++ * also makes the decision about whether to provide a declaration for
++ * reallocarray() at compile-time, so this is a convenient place to override -
++ * it's used by the ialloc module, which is used by base64. */
++#    define _GL_INLINE_HEADER_BEGIN _Pragma ("GCC diagnostic push")	\
++    void *								\
++    reallocarray (void *ptr, unsigned int nmemb, unsigned int size);
++#    define _GL_INLINE_HEADER_END   _Pragma ("GCC diagnostic pop")
+ 
+ /* We don't have an abort() for gnulib to call in regexp. */
+ #    define abort __builtin_unreachable
+ #  endif /* !_GL_INLINE_HEADER_BEGIN */
+ 
++/* gnulib doesn't build cleanly with older compilers. */
++#  if __GNUC__ < 11
++_Pragma ("GCC diagnostic ignored \"-Wtype-limits\"")
++#  endif
++
+ #endif
+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 8fafa153a4..0000000000
+--- 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 813ec09c8a..0000000000
+--- 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 02e06315df..0000000000
+--- 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 db6dac9c9e..0000000000
+--- 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-uninit-structure.patch b/grub-core/lib/gnulib-patches/fix-uninit-structure.patch
+deleted file mode 100644
+index 7b4d9f67af..0000000000
+--- 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 ba51f1bf22..0000000000
+--- 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/SOURCES/0203-commands-search-Fix-bug-stopping-iteration-when-no-f.patch b/SOURCES/0203-commands-search-Fix-bug-stopping-iteration-when-no-f.patch
new file mode 100644
index 0000000..4f7feb9
--- /dev/null
+++ b/SOURCES/0203-commands-search-Fix-bug-stopping-iteration-when-no-f.patch
@@ -0,0 +1,33 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
+Date: Tue, 8 Feb 2022 08:39:10 +0100
+Subject: [PATCH] commands/search: Fix bug stopping iteration when --no-floppy
+ is used
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+When using --no-floppy and a floppy was encountered, iterate_device()
+was returning 1, causing the iteration to stop instead of continuing.
+
+Signed-off-by: Renaud Métrich <rmetrich@redhat.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 68ba54c2298604146be83cae144dafd1cfd1fe2d)
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/commands/search.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c
+index ed090b3af8..51656e361c 100644
+--- a/grub-core/commands/search.c
++++ b/grub-core/commands/search.c
+@@ -64,7 +64,7 @@ iterate_device (const char *name, void *data)
+   /* Skip floppy drives when requested.  */
+   if (ctx->no_floppy &&
+       name[0] == 'f' && name[1] == 'd' && name[2] >= '0' && name[2] <= '9')
+-    return 1;
++    return 0;
+ 
+ #ifdef DO_SEARCH_FS_UUID
+ #define compare_fn grub_strcasecmp
diff --git a/SOURCES/0204-search-new-efidisk-only-option-on-EFI-systems.patch b/SOURCES/0204-search-new-efidisk-only-option-on-EFI-systems.patch
new file mode 100644
index 0000000..3f6194a
--- /dev/null
+++ b/SOURCES/0204-search-new-efidisk-only-option-on-EFI-systems.patch
@@ -0,0 +1,168 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
+Date: Tue, 8 Feb 2022 08:39:11 +0100
+Subject: [PATCH] search: new --efidisk-only option on EFI systems
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+When using 'search' on EFI systems, we sometimes want to exclude devices
+that are not EFI disks (e.g. md, lvm).
+This is typically used when wanting to chainload when having a software
+raid (md) for EFI partition:
+with no option, 'search --file /EFI/redhat/shimx64.efi' sets root envvar
+to 'md/boot_efi' which cannot be used for chainloading since there is no
+effective EFI device behind.
+
+This commit also refactors handling of --no-floppy option.
+
+Signed-off-by: Renaud Métrich <rmetrich@redhat.com>
+[rharwood: apply rmetrich's flags initialization fix]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/commands/search.c      | 27 +++++++++++++++++++++++----
+ grub-core/commands/search_wrap.c | 18 ++++++++++++------
+ include/grub/search.h            | 15 ++++++++++++---
+ 3 files changed, 47 insertions(+), 13 deletions(-)
+
+diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c
+index 51656e361c..57d26ced8a 100644
+--- a/grub-core/commands/search.c
++++ b/grub-core/commands/search.c
+@@ -47,7 +47,7 @@ struct search_ctx
+ {
+   const char *key;
+   const char *var;
+-  int no_floppy;
++  enum search_flags flags;
+   char **hints;
+   unsigned nhints;
+   int count;
+@@ -62,10 +62,29 @@ iterate_device (const char *name, void *data)
+   int found = 0;
+ 
+   /* Skip floppy drives when requested.  */
+-  if (ctx->no_floppy &&
++  if (ctx->flags & SEARCH_FLAGS_NO_FLOPPY &&
+       name[0] == 'f' && name[1] == 'd' && name[2] >= '0' && name[2] <= '9')
+     return 0;
+ 
++  /* Limit to EFI disks when requested.  */
++  if (ctx->flags & SEARCH_FLAGS_EFIDISK_ONLY)
++    {
++      grub_device_t dev;
++      dev = grub_device_open (name);
++      if (! dev)
++	{
++	  grub_errno = GRUB_ERR_NONE;
++	  return 0;
++	}
++      if (! dev->disk || dev->disk->dev->id != GRUB_DISK_DEVICE_EFIDISK_ID)
++	{
++	  grub_device_close (dev);
++	  grub_errno = GRUB_ERR_NONE;
++	  return 0;
++	}
++      grub_device_close (dev);
++    }
++
+ #ifdef DO_SEARCH_FS_UUID
+ #define compare_fn grub_strcasecmp
+ #else
+@@ -261,13 +280,13 @@ try (struct search_ctx *ctx)
+ }
+ 
+ void
+-FUNC_NAME (const char *key, const char *var, int no_floppy,
++FUNC_NAME (const char *key, const char *var, enum search_flags flags,
+ 	   char **hints, unsigned nhints)
+ {
+   struct search_ctx ctx = {
+     .key = key,
+     .var = var,
+-    .no_floppy = no_floppy,
++    .flags = flags,
+     .hints = hints,
+     .nhints = nhints,
+     .count = 0,
+diff --git a/grub-core/commands/search_wrap.c b/grub-core/commands/search_wrap.c
+index 47fc8eb996..0b62acf853 100644
+--- a/grub-core/commands/search_wrap.c
++++ b/grub-core/commands/search_wrap.c
+@@ -40,6 +40,7 @@ static const struct grub_arg_option options[] =
+      N_("Set a variable to the first device found."), N_("VARNAME"),
+      ARG_TYPE_STRING},
+     {"no-floppy",	'n', 0, N_("Do not probe any floppy drive."), 0, 0},
++    {"efidisk-only",	0, 0, N_("Only probe EFI disks."), 0, 0},
+     {"hint",	        'h', GRUB_ARG_OPTION_REPEATABLE,
+      N_("First try the device HINT. If HINT ends in comma, "
+ 	"also try subpartitions"), N_("HINT"), ARG_TYPE_STRING},
+@@ -73,6 +74,7 @@ enum options
+     SEARCH_FS_UUID,
+     SEARCH_SET,
+     SEARCH_NO_FLOPPY,
++    SEARCH_EFIDISK_ONLY,
+     SEARCH_HINT,
+     SEARCH_HINT_IEEE1275,
+     SEARCH_HINT_BIOS,
+@@ -89,6 +91,7 @@ grub_cmd_search (grub_extcmd_context_t ctxt, int argc, char **args)
+   const char *id = 0;
+   int i = 0, j = 0, nhints = 0;
+   char **hints = NULL;
++  enum search_flags flags = 0;
+ 
+   if (state[SEARCH_HINT].set)
+     for (i = 0; state[SEARCH_HINT].args[i]; i++)
+@@ -180,15 +183,18 @@ grub_cmd_search (grub_extcmd_context_t ctxt, int argc, char **args)
+       goto out;
+     }
+ 
++  if (state[SEARCH_NO_FLOPPY].set)
++    flags |= SEARCH_FLAGS_NO_FLOPPY;
++
++  if (state[SEARCH_EFIDISK_ONLY].set)
++    flags |= SEARCH_FLAGS_EFIDISK_ONLY;
++
+   if (state[SEARCH_LABEL].set)
+-    grub_search_label (id, var, state[SEARCH_NO_FLOPPY].set, 
+-		       hints, nhints);
++    grub_search_label (id, var, flags, hints, nhints);
+   else if (state[SEARCH_FS_UUID].set)
+-    grub_search_fs_uuid (id, var, state[SEARCH_NO_FLOPPY].set,
+-			 hints, nhints);
++    grub_search_fs_uuid (id, var, flags, hints, nhints);
+   else if (state[SEARCH_FILE].set)
+-    grub_search_fs_file (id, var, state[SEARCH_NO_FLOPPY].set, 
+-			 hints, nhints);
++    grub_search_fs_file (id, var, flags, hints, nhints);
+   else
+     grub_error (GRUB_ERR_INVALID_COMMAND, "unspecified search type");
+ 
+diff --git a/include/grub/search.h b/include/grub/search.h
+index d80347df34..4190aeb2cb 100644
+--- a/include/grub/search.h
++++ b/include/grub/search.h
+@@ -19,11 +19,20 @@
+ #ifndef GRUB_SEARCH_HEADER
+ #define GRUB_SEARCH_HEADER 1
+ 
+-void grub_search_fs_file (const char *key, const char *var, int no_floppy,
++enum search_flags
++  {
++    SEARCH_FLAGS_NO_FLOPPY	= 1,
++    SEARCH_FLAGS_EFIDISK_ONLY	= 2
++  };
++
++void grub_search_fs_file (const char *key, const char *var,
++			  enum search_flags flags,
+ 			  char **hints, unsigned nhints);
+-void grub_search_fs_uuid (const char *key, const char *var, int no_floppy,
++void grub_search_fs_uuid (const char *key, const char *var,
++			  enum search_flags flags,
+ 			  char **hints, unsigned nhints);
+-void grub_search_label (const char *key, const char *var, int no_floppy,
++void grub_search_label (const char *key, const char *var,
++			enum search_flags flags,
+ 			char **hints, unsigned nhints);
+ 
+ #endif
diff --git a/SOURCES/0205-efi-new-connectefi-command.patch b/SOURCES/0205-efi-new-connectefi-command.patch
new file mode 100644
index 0000000..f80de22
--- /dev/null
+++ b/SOURCES/0205-efi-new-connectefi-command.patch
@@ -0,0 +1,395 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
+Date: Tue, 15 Feb 2022 14:05:22 +0100
+Subject: [PATCH] efi: new 'connectefi' command
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+When efi.quickboot is enabled on VMWare (which is the default for
+hardware release 16 and later), it may happen that not all EFI devices
+are connected. Due to this, browsing the devices in make_devices() just
+fails to find devices, in particular disks or partitions for a given
+disk.
+This typically happens when network booting, then trying to chainload to
+local disk (this is used in deployment tools such as Red Hat Satellite),
+which is done through using the following grub.cfg snippet:
+-------- 8< ---------------- 8< ---------------- 8< --------
+unset prefix
+search --file --set=prefix /EFI/redhat/grubx64.efi
+if [ -n "$prefix" ]; then
+  chainloader ($prefix)/EFI/redhat/grubx64/efi
+...
+-------- 8< ---------------- 8< ---------------- 8< --------
+
+With efi.quickboot, none of the devices are connected, causing "search"
+to fail. Sometimes devices are connected but not the partition of the
+disk matching $prefix, causing partition to not be found by
+"chainloader".
+
+This patch introduces a new "connectefi pciroot|scsi" command which
+recursively connects all EFI devices starting from a given controller
+type:
+- if 'pciroot' is specified, recursion is performed for all PCI root
+  handles
+- if 'scsi' is specified, recursion is performed for all SCSI I/O
+  handles (recommended usage to avoid connecting unwanted handles which
+  may impact Grub performances)
+
+Typical grub.cfg snippet would then be:
+-------- 8< ---------------- 8< ---------------- 8< --------
+connectefi scsi
+unset prefix
+search --file --set=prefix /EFI/redhat/grubx64.efi
+if [ -n "$prefix" ]; then
+  chainloader ($prefix)/EFI/redhat/grubx64/efi
+...
+-------- 8< ---------------- 8< ---------------- 8< --------
+
+The code is easily extensible to handle other arguments in the future if
+needed.
+
+Signed-off-by: Renaud Métrich <rmetrich@redhat.com>
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/Makefile.core.def         |   6 ++
+ grub-core/commands/efi/connectefi.c | 205 ++++++++++++++++++++++++++++++++++++
+ grub-core/commands/efi/lsefi.c      |   1 +
+ grub-core/disk/efi/efidisk.c        |  13 +++
+ grub-core/kern/efi/efi.c            |  13 +++
+ include/grub/efi/disk.h             |   2 +
+ include/grub/efi/efi.h              |   5 +
+ NEWS                                |   2 +-
+ 8 files changed, 246 insertions(+), 1 deletion(-)
+ create mode 100644 grub-core/commands/efi/connectefi.c
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index ec1ec5083b..741a033978 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -836,6 +836,12 @@ module = {
+   enable = efi;
+ };
+ 
++module = {
++  name = connectefi;
++  common = commands/efi/connectefi.c;
++  enable = efi;
++};
++
+ module = {
+   name = blocklist;
+   common = commands/blocklist.c;
+diff --git a/grub-core/commands/efi/connectefi.c b/grub-core/commands/efi/connectefi.c
+new file mode 100644
+index 0000000000..8ab75bd51b
+--- /dev/null
++++ b/grub-core/commands/efi/connectefi.c
+@@ -0,0 +1,205 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2022  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/types.h>
++#include <grub/mm.h>
++#include <grub/misc.h>
++#include <grub/efi/api.h>
++#include <grub/efi/pci.h>
++#include <grub/efi/efi.h>
++#include <grub/command.h>
++#include <grub/err.h>
++#include <grub/i18n.h>
++
++GRUB_MOD_LICENSE ("GPLv3+");
++
++typedef struct handle_list
++{
++  grub_efi_handle_t handle;
++  struct handle_list *next;
++} handle_list_t;
++
++static handle_list_t *already_handled = NULL;
++
++static grub_err_t
++add_handle (grub_efi_handle_t handle)
++{
++  handle_list_t *e;
++  e = grub_malloc (sizeof (*e));
++  if (! e)
++    return grub_errno;
++  e->handle = handle;
++  e->next = already_handled;
++  already_handled = e;
++  return GRUB_ERR_NONE;
++}
++
++static int
++is_in_list (grub_efi_handle_t handle)
++{
++  handle_list_t *e;
++  for (e = already_handled; e != NULL; e = e->next)
++    if (e->handle == handle)
++      return 1;
++  return 0;
++}
++
++static void
++free_handle_list (void)
++{
++  handle_list_t *e;
++  while ((e = already_handled) != NULL)
++    {
++      already_handled = already_handled->next;
++      grub_free (e);
++    }
++}
++
++typedef enum searched_item_flag
++{
++  SEARCHED_ITEM_FLAG_LOOP = 1,
++  SEARCHED_ITEM_FLAG_RECURSIVE = 2
++} searched_item_flags;
++
++typedef struct searched_item
++{
++  grub_efi_guid_t guid;
++  const char *name;
++  searched_item_flags flags;
++} searched_items;
++
++static grub_err_t
++grub_cmd_connectefi (grub_command_t cmd __attribute__ ((unused)),
++		     int argc, char **args)
++{
++  unsigned s;
++  searched_items pciroot_items[] =
++    {
++      { GRUB_EFI_PCI_ROOT_IO_GUID, "PCI root", SEARCHED_ITEM_FLAG_RECURSIVE }
++    };
++  searched_items scsi_items[] =
++    {
++      { GRUB_EFI_PCI_ROOT_IO_GUID, "PCI root", 0 },
++      { GRUB_EFI_PCI_IO_GUID, "PCI", SEARCHED_ITEM_FLAG_LOOP },
++      { GRUB_EFI_SCSI_IO_PROTOCOL_GUID, "SCSI I/O", SEARCHED_ITEM_FLAG_RECURSIVE }
++    };
++  searched_items *items = NULL;
++  unsigned nitems = 0;
++  grub_err_t grub_err = GRUB_ERR_NONE;
++  unsigned total_connected = 0;
++
++  if (argc != 1)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
++
++  if (grub_strcmp(args[0], N_("pciroot")) == 0)
++    {
++      items = pciroot_items;
++      nitems = ARRAY_SIZE (pciroot_items);
++    }
++  else if (grub_strcmp(args[0], N_("scsi")) == 0)
++    {
++      items = scsi_items;
++      nitems = ARRAY_SIZE (scsi_items);
++    }
++  else
++    return grub_error (GRUB_ERR_BAD_ARGUMENT,
++		       N_("unexpected argument `%s'"), args[0]);
++
++  for (s = 0; s < nitems; s++)
++    {
++      grub_efi_handle_t *handles;
++      grub_efi_uintn_t num_handles;
++      unsigned i, connected = 0, loop = 0;
++
++loop:
++      loop++;
++      grub_dprintf ("efi", "step '%s' loop %d:\n", items[s].name, loop);
++
++      handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL,
++					&items[s].guid, 0, &num_handles);
++
++      if (!handles)
++	continue;
++
++      for (i = 0; i < num_handles; i++)
++	{
++	  grub_efi_handle_t handle = handles[i];
++	  grub_efi_status_t status;
++	  unsigned j;
++
++	  /* Skip already handled handles  */
++	  if (is_in_list (handle))
++	    {
++	      grub_dprintf ("efi", "  handle %p: already processed\n",
++				   handle);
++	      continue;
++	    }
++
++	  status = grub_efi_connect_controller(handle, NULL, NULL,
++			items[s].flags & SEARCHED_ITEM_FLAG_RECURSIVE ? 1 : 0);
++	  if (status == GRUB_EFI_SUCCESS)
++	    {
++	      connected++;
++	      total_connected++;
++	      grub_dprintf ("efi", "  handle %p: connected\n", handle);
++	    }
++	  else
++	    grub_dprintf ("efi", "  handle %p: failed to connect (%d)\n",
++				 handle, (grub_efi_int8_t) status);
++
++	  if ((grub_err = add_handle (handle)) != GRUB_ERR_NONE)
++	    break; /* fatal  */
++	}
++
++      grub_free (handles);
++      if (grub_err != GRUB_ERR_NONE)
++	break; /* fatal  */
++
++      if (items[s].flags & SEARCHED_ITEM_FLAG_LOOP && connected)
++	{
++	  connected = 0;
++	  goto loop;
++	}
++
++      free_handle_list ();
++    }
++
++  free_handle_list ();
++
++  if (total_connected)
++    grub_efidisk_reenumerate_disks ();
++
++  return grub_err;
++}
++
++static grub_command_t cmd;
++
++GRUB_MOD_INIT(connectefi)
++{
++  cmd = grub_register_command ("connectefi", grub_cmd_connectefi,
++			       N_("pciroot|scsi"),
++			       N_("Connect EFI handles."
++				  " If 'pciroot' is specified, connect PCI"
++				  " root EFI handles recursively."
++				  " If 'scsi' is specified, connect SCSI"
++				  " I/O EFI handles recursively."));
++}
++
++GRUB_MOD_FINI(connectefi)
++{
++  grub_unregister_command (cmd);
++}
+diff --git a/grub-core/commands/efi/lsefi.c b/grub-core/commands/efi/lsefi.c
+index d1ce99af43..f2d2430e66 100644
+--- a/grub-core/commands/efi/lsefi.c
++++ b/grub-core/commands/efi/lsefi.c
+@@ -19,6 +19,7 @@
+ #include <grub/mm.h>
+ #include <grub/misc.h>
+ #include <grub/efi/api.h>
++#include <grub/efi/disk.h>
+ #include <grub/efi/edid.h>
+ #include <grub/efi/pci.h>
+ #include <grub/efi/efi.h>
+diff --git a/grub-core/disk/efi/efidisk.c b/grub-core/disk/efi/efidisk.c
+index fe8ba6e6c9..062143dfff 100644
+--- a/grub-core/disk/efi/efidisk.c
++++ b/grub-core/disk/efi/efidisk.c
+@@ -396,6 +396,19 @@ enumerate_disks (void)
+   free_devices (devices);
+ }
+ 
++void
++grub_efidisk_reenumerate_disks (void)
++{
++  free_devices (fd_devices);
++  free_devices (hd_devices);
++  free_devices (cd_devices);
++  fd_devices = 0;
++  hd_devices = 0;
++  cd_devices = 0;
++
++  enumerate_disks ();
++}
++
+ static int
+ grub_efidisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data,
+ 		      grub_disk_pull_t pull)
+diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
+index 14bc10eb56..7fcca69c17 100644
+--- a/grub-core/kern/efi/efi.c
++++ b/grub-core/kern/efi/efi.c
+@@ -95,6 +95,19 @@ grub_efi_locate_handle (grub_efi_locate_search_type_t search_type,
+   return buffer;
+ }
+ 
++grub_efi_status_t
++grub_efi_connect_controller (grub_efi_handle_t controller_handle,
++			     grub_efi_handle_t *driver_image_handle,
++			     grub_efi_device_path_protocol_t *remaining_device_path,
++			     grub_efi_boolean_t recursive)
++{
++  grub_efi_boot_services_t *b;
++
++  b = grub_efi_system_table->boot_services;
++  return efi_call_4 (b->connect_controller, controller_handle,
++		     driver_image_handle, remaining_device_path, recursive);
++}
++
+ void *
+ grub_efi_open_protocol (grub_efi_handle_t handle,
+ 			grub_efi_guid_t *protocol,
+diff --git a/include/grub/efi/disk.h b/include/grub/efi/disk.h
+index 254475c842..6845c2f1fd 100644
+--- a/include/grub/efi/disk.h
++++ b/include/grub/efi/disk.h
+@@ -27,6 +27,8 @@ grub_efi_handle_t
+ EXPORT_FUNC(grub_efidisk_get_device_handle) (grub_disk_t disk);
+ char *EXPORT_FUNC(grub_efidisk_get_device_name) (grub_efi_handle_t *handle);
+ 
++void EXPORT_FUNC(grub_efidisk_reenumerate_disks) (void);
++
+ void grub_efidisk_init (void);
+ void grub_efidisk_fini (void);
+ 
+diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
+index 8dfc89a33b..ec52083c49 100644
+--- a/include/grub/efi/efi.h
++++ b/include/grub/efi/efi.h
+@@ -41,6 +41,11 @@ EXPORT_FUNC(grub_efi_locate_handle) (grub_efi_locate_search_type_t search_type,
+ 				     grub_efi_guid_t *protocol,
+ 				     void *search_key,
+ 				     grub_efi_uintn_t *num_handles);
++grub_efi_status_t
++EXPORT_FUNC(grub_efi_connect_controller) (grub_efi_handle_t controller_handle,
++					  grub_efi_handle_t *driver_image_handle,
++					  grub_efi_device_path_protocol_t *remaining_device_path,
++					  grub_efi_boolean_t recursive);
+ void *EXPORT_FUNC(grub_efi_open_protocol) (grub_efi_handle_t handle,
+ 					   grub_efi_guid_t *protocol,
+ 					   grub_efi_uint32_t attributes);
+diff --git a/NEWS b/NEWS
+index 73b8492bc4..d7c1d23aed 100644
+--- a/NEWS
++++ b/NEWS
+@@ -98,7 +98,7 @@ New in 2.02:
+   * Prefer pmtimer for TSC calibration.
+ 
+ * New/improved platform support:
+-  * New `efifwsetup' and `lsefi' commands on EFI platforms.
++  * New `efifwsetup', `lsefi' and `connectefi` commands on EFI platforms.
+   * New `cmosdump' and `cmosset' commands on platforms with CMOS support.
+   * New command `pcidump' for PCI platforms.
+   * Improve opcode parsing in ACPI halt implementation.
diff --git a/SOURCES/0206-grub-core-loader-i386-efi-linux.c-do-not-validate-ke.patch b/SOURCES/0206-grub-core-loader-i386-efi-linux.c-do-not-validate-ke.patch
new file mode 100644
index 0000000..4fe8adf
--- /dev/null
+++ b/SOURCES/0206-grub-core-loader-i386-efi-linux.c-do-not-validate-ke.patch
@@ -0,0 +1,73 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
+Date: Thu, 3 Mar 2022 13:10:56 +0100
+Subject: [PATCH] grub-core/loader/i386/efi/linux.c: do not validate kernels
+ twice
+
+On codebases that have shim-lock-verifier built into the grub core
+(like 2.06 upstream), shim-lock-verifier is in enforcing mode when
+booted with secureboot. It means that grub_cmd_linux() command
+attempts to perform shim validate upon opening linux kernel image,
+including kernel measurement. And the verifier correctly returns file
+open error when shim validate protocol is not present or shim fails to
+validate the kernel.
+
+This makes the call to grub_linuxefi_secure_validate() redundant, but
+also harmful. As validating the kernel image twice, extends the PCRs
+with the same measurement twice. Which breaks existing sealing
+policies when upgrading from grub2.04+rhboot+sb+linuxefi to
+grub2.06+rhboot+sb+linuxefi builds. It is also incorrect to measure
+the kernel twice.
+
+This patch must not be ported to older editions of grub code bases
+that do not have verifiers framework, or it is not builtin, or
+shim-lock-verifier is an optional module.
+
+This patch is tested to ensure that unsigned kernels are not possible
+to boot in secureboot mode when shim rejects kernel, or shim protocol
+is missing, and that the measurements become stable once again. The
+above also ensures that CVE-2020-15705 is not reintroduced.
+
+Signed-off-by: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
+---
+ grub-core/loader/i386/efi/linux.c | 13 -------------
+ 1 file changed, 13 deletions(-)
+
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index 3cf0f9b330..941df6400b 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -30,7 +30,6 @@
+ #include <grub/cpu/efi/memory.h>
+ #include <grub/tpm.h>
+ #include <grub/safemath.h>
+-#include <grub/efi/sb.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -278,7 +277,6 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   grub_ssize_t start, filelen;
+   void *kernel = NULL;
+   int setup_header_end_offset;
+-  int rc;
+ 
+   grub_dl_ref (my_mod);
+ 
+@@ -308,17 +306,6 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+       goto fail;
+     }
+ 
+-  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
+-    {
+-      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;
+   grub_dprintf ("linux", "original lh is at %p\n", kernel);
+ 
diff --git a/SOURCES/0207-grub-core-loader-arm64-linux.c-do-not-validate-kerne.patch b/SOURCES/0207-grub-core-loader-arm64-linux.c-do-not-validate-kerne.patch
new file mode 100644
index 0000000..5b450f9
--- /dev/null
+++ b/SOURCES/0207-grub-core-loader-arm64-linux.c-do-not-validate-kerne.patch
@@ -0,0 +1,58 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
+Date: Fri, 4 Mar 2022 11:29:31 +0100
+Subject: [PATCH] grub-core/loader/arm64/linux.c: do not validate kernel twice
+
+Call to grub_file_open(, GRUB_FILE_TYPE_LINUX_KERNEL) already passes
+the kernel file through shim-lock verifier when secureboot is on. Thus
+there is no need to validate the kernel image again. And when doing so
+again, duplicate PCR measurement is performed, breaking measurements
+compatibility with 2.04+linuxefi.
+
+This patch must not be ported to older editions of grub code bases
+that do not have verifiers framework, or it is not builtin, or
+shim-lock-verifier is an optional module.
+
+Signed-off-by: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
+---
+ grub-core/loader/arm64/linux.c | 13 -------------
+ 1 file changed, 13 deletions(-)
+
+diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
+index f18d90bd74..d2af47c2c0 100644
+--- a/grub-core/loader/arm64/linux.c
++++ b/grub-core/loader/arm64/linux.c
+@@ -34,7 +34,6 @@
+ #include <grub/i18n.h>
+ #include <grub/lib/cmdline.h>
+ #include <grub/verify.h>
+-#include <grub/efi/sb.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+@@ -341,7 +340,6 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   grub_off_t filelen;
+   grub_uint32_t align;
+   void *kernel = NULL;
+-  int rc;
+ 
+   grub_dl_ref (my_mod);
+ 
+@@ -370,17 +368,6 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+       goto fail;
+     }
+ 
+-  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
+-    {
+-      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;
+-	}
+-    }
+-
+   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)
diff --git a/SOURCES/0208-grub-core-loader-efi-chainloader.c-do-not-validate-c.patch b/SOURCES/0208-grub-core-loader-efi-chainloader.c-do-not-validate-c.patch
new file mode 100644
index 0000000..4c56d60
--- /dev/null
+++ b/SOURCES/0208-grub-core-loader-efi-chainloader.c-do-not-validate-c.patch
@@ -0,0 +1,80 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
+Date: Fri, 4 Mar 2022 09:31:43 +0100
+Subject: [PATCH] grub-core/loader/efi/chainloader.c: do not validate
+ chainloader twice
+
+On secureboot systems, with shimlock verifier, call to
+grub_file_open(, GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE) will already
+pass the chainloader target through shim-lock protocol verify
+call. And create a TPM measurement. If verification fails,
+grub_cmd_chainloader will fail at file open time.
+
+This makes previous code paths for negative, and zero return codes
+from grub_linuxefi_secure_validate unreachable under secureboot. But
+also breaking measurements compatibility with 2.04+linuxefi codebases,
+as the chainloader file is passed through shim_lock->verify() twice
+(via verifier & direct call to grub_linuxefi_secure_validate)
+extending the PCRs twice.
+
+This reduces grub_loader options to perform
+grub_secureboot_chainloader when secureboot is on, and otherwise
+attempt grub_chainloader_boot.
+
+It means that booting with secureboot off, yet still with shim (which
+always verifies things successfully), will stop choosing
+grub_secureboot_chainloader, and opting for a more regular
+loadimage/startimage codepath. If we want to use the
+grub_secureboot_chainloader codepath in such scenarios we should adapt
+the code to simply check for shim_lock protocol presence /
+shim_lock->context() success?! But I am not sure if that is necessary.
+
+This patch must not be ported to older editions of grub code bases
+that do not have verifiers framework, or it is not builtin, or
+shim-lock-verifier is an optional module.
+
+Signed-off-by: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
+---
+ grub-core/loader/efi/chainloader.c | 8 ++------
+ 1 file changed, 2 insertions(+), 6 deletions(-)
+
+diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
+index 3af6b12292..644cd2e56f 100644
+--- a/grub-core/loader/efi/chainloader.c
++++ b/grub-core/loader/efi/chainloader.c
+@@ -906,7 +906,6 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+   grub_efi_device_path_t *dp = 0;
+   char *filename;
+   void *boot_image = 0;
+-  int rc;
+ 
+   if (argc == 0)
+     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+@@ -1082,9 +1081,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+       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)
++  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
+     {
+       grub_file_close (file);
+       grub_device_close (dev);
+@@ -1092,7 +1089,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+ 		       grub_secureboot_chainloader_unload, 0);
+       return 0;
+     }
+-  else if (rc == 0)
++  else
+     {
+       grub_load_and_start_image(boot_image);
+       grub_file_close (file);
+@@ -1101,7 +1098,6 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+ 
+       return 0;
+     }
+-  // -1 fall-through to fail
+ 
+ fail:
+   if (orig_dev)
diff --git a/SOURCES/0209-grub-core-loader-efi-linux.c-drop-now-unused-grub_li.patch b/SOURCES/0209-grub-core-loader-efi-linux.c-drop-now-unused-grub_li.patch
new file mode 100644
index 0000000..c683fcb
--- /dev/null
+++ b/SOURCES/0209-grub-core-loader-efi-linux.c-drop-now-unused-grub_li.patch
@@ -0,0 +1,83 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
+Date: Fri, 4 Mar 2022 11:36:09 +0100
+Subject: [PATCH] grub-core/loader/efi/linux.c: drop now unused
+ grub_linuxefi_secure_validate
+
+Drop the now unused grub_linuxefi_secure_validate() as all prior users
+of this API now rely on the shim-lock-verifier codepath instead.
+
+This patch must not be ported to older editions of grub code bases
+that do not have verifiers framework, or it is not builtin, or
+shim-lock-verifier is an optional module.
+
+Signed-off-by: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
+---
+ grub-core/loader/efi/linux.c | 40 ----------------------------------------
+ include/grub/efi/linux.h     |  2 --
+ 2 files changed, 42 deletions(-)
+
+diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c
+index 9260731c10..9265cf4200 100644
+--- a/grub-core/loader/efi/linux.c
++++ b/grub-core/loader/efi/linux.c
+@@ -24,46 +24,6 @@
+ #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;
+-
+-// Returns 1 on success, -1 on error, 0 when not available
+-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)
+-    {
+-      grub_dprintf ("secureboot", "shim not available\n");
+-      return 0;
+-    }
+-
+-  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;
+-    }
+-
+-  grub_dprintf ("secureboot", "Kernel signature verification failed (0x%lx)\n",
+-		(unsigned long) status);
+-
+-  return -1;
+-}
+-
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wcast-align"
+ 
+diff --git a/include/grub/efi/linux.h b/include/grub/efi/linux.h
+index 0033d9305a..887b02fd9f 100644
+--- a/include/grub/efi/linux.h
++++ b/include/grub/efi/linux.h
+@@ -22,8 +22,6 @@
+ #include <grub/err.h>
+ #include <grub/symbol.h>
+ 
+-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,
+ 				  void *kernel_param);
diff --git a/SOURCES/0210-powerpc-prefix-detection-support-device-names-with-c.patch b/SOURCES/0210-powerpc-prefix-detection-support-device-names-with-c.patch
new file mode 100644
index 0000000..1c16fd3
--- /dev/null
+++ b/SOURCES/0210-powerpc-prefix-detection-support-device-names-with-c.patch
@@ -0,0 +1,71 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Thu, 24 Mar 2022 14:34:32 +1100
+Subject: [PATCH] powerpc: prefix detection: support device names with commas
+
+Frustratingly, the device name itself can contain an embedded comma:
+e.g /pci@800000020000015/pci1014,034A@0/sas/disk@5000c50098a0ee8b
+
+So my previous approach was wrong: we cannot rely upon the presence
+of a comma to say that a partition has been specified!
+
+It turns out for prefixes like (,gpt2)/grub2 we really want to make
+up a full (device,partition)/patch prefix, because root discovery code
+in 10_linux will reset the root variable and use search to fill it again.
+If you have run grub-install, you probably don't have search built in,
+and if you don't have prefix containing (device,partition), grub will
+construct ($root)$prefix/powerpc-ieee1275/search.mod - but because $root
+has just been changed, this will no longer work, and the boot will fail!
+
+Retain the gist of the logic, but instead of looking for a comma, look for
+a leading '('. This matches the earlier code better anyway.
+
+There's certainly a better fix to be had. But any time you chose to build
+with a bare prefix like '/grub2', you're almost certainly going to build in
+search anyway, so this will do.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+---
+ grub-core/kern/main.c | 27 +++++++++++++++++++++------
+ 1 file changed, 21 insertions(+), 6 deletions(-)
+
+diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c
+index 993b8a8598..e94a2f78fb 100644
+--- a/grub-core/kern/main.c
++++ b/grub-core/kern/main.c
+@@ -242,14 +242,29 @@ grub_set_prefix_and_root (void)
+ 	    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.
++          - Frustratingly, the device name itself can contain an embedded comma:
++            /pci@800000020000015/pci1014,034A@0/sas/disk@5000c50098a0ee8b
++            So we cannot even rely upon the presence of a comma to say that a
++            partition has 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 we only have a path in $prefix, the code in normal to discover
++	 config files will try all disks, both without partitions and then with
++	 any partitions so we will cover both CDs and HDs.
++
++         However, it doesn't then set the prefix to be something like
++         (discovered partition)/path, and so it is fragile against runtime
++         changes to $root. For example some of the stuff done in 10_linux to
++         reload $root sets root differently and then uses search to find it
++         again. If the search module is not built in, when we change root, grub
++         will look in (new root)/path/powerpc-ieee1275, that won't work, and we
++         will not be able to load the search module and the boot will fail.
++
++         This is particularly likely to hit us in the grub-install
++         (,msdos2)/grub2 case, so we act unless the supplied prefix starts with
++         '(', which would likely indicate a partition has already been
++         specified.
+        */
+-      if (grub_strchr (device, ',') == NULL)
++      if (prefix && prefix[0] != '(')
+         grub_env_set ("prefix", path);
+       else
+ #endif
diff --git a/SOURCES/0211-make-ofdisk_retries-optional.patch b/SOURCES/0211-make-ofdisk_retries-optional.patch
new file mode 100644
index 0000000..fce9702
--- /dev/null
+++ b/SOURCES/0211-make-ofdisk_retries-optional.patch
@@ -0,0 +1,43 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Diego Domingos <diegdo@br.ibm.com>
+Date: Thu, 24 Mar 2022 13:14:42 -0400
+Subject: [PATCH] make ofdisk_retries optional
+
+The feature Retry on Fail added to GRUB can cause a LPM to take
+longer if the SAN is slow.
+
+When a LPM to external site occur, the path of the disk can change
+and thus the disk search function on grub can take some time since
+it is used as a hint. This can cause the Retry on Fail feature to
+try to access the disk 20x times (since this is hardcoded number)
+and, if the SAN is slow, the boot time can increase a lot.
+In some situations not acceptable.
+
+The following patch enables a configuration at user space of the
+maximum number of retries we want for this feature.
+
+The variable ofdisk_retries should be set using grub2-editenv
+and will be checked by retry function. If the variable is not set,
+so the default number of retries will be used instead.
+---
+ include/grub/ieee1275/ofdisk.h | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/include/grub/ieee1275/ofdisk.h b/include/grub/ieee1275/ofdisk.h
+index 7d2d540930..0074d55eee 100644
+--- a/include/grub/ieee1275/ofdisk.h
++++ b/include/grub/ieee1275/ofdisk.h
+@@ -25,7 +25,12 @@ 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++){ \
++#define RETRY_IEEE1275_OFDISK_OPEN(device, last_ihandle) \
++	                                        unsigned max_retries = MAX_RETRIES; \
++                                                if(grub_env_get("ofdisk_retries") != NULL) \
++                                                     max_retries = grub_strtoul(grub_env_get("ofdisk_retries"), 0, 10)+1; \
++                                                grub_dprintf("ofdisk","MAX_RETRIES set to %u\n",max_retries); \
++                                                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); }
diff --git a/SOURCES/0212-loader-efi-chainloader-grub_load_and_start_image-doe.patch b/SOURCES/0212-loader-efi-chainloader-grub_load_and_start_image-doe.patch
new file mode 100644
index 0000000..f61ed28
--- /dev/null
+++ b/SOURCES/0212-loader-efi-chainloader-grub_load_and_start_image-doe.patch
@@ -0,0 +1,69 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Chris Coulson <chris.coulson@canonical.com>
+Date: Thu, 28 Apr 2022 21:53:36 +0100
+Subject: [PATCH] loader/efi/chainloader: grub_load_and_start_image doesn't
+ load and start
+
+grub_load_and_start_image only loads an image - it still requires the
+caller to start it. This renames it to grub_load_image.
+
+It's called from 2 places:
+- grub_cmd_chainloader when not using the shim protocol.
+- grub_secureboot_chainloader_boot if handle_image returns an error.
+In this case, the image is loaded and then nothing else happens which
+seems strange. I assume the intention is that it falls back to LoadImage
+and StartImage if handle_image fails, so I've made it do that.
+
+Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
+(cherry picked from commit b4d70820a65c00561045856b7b8355461a9545f6)
+---
+ grub-core/loader/efi/chainloader.c | 16 +++++++++++++---
+ 1 file changed, 13 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
+index 644cd2e56f..d3bf02ed8a 100644
+--- a/grub-core/loader/efi/chainloader.c
++++ b/grub-core/loader/efi/chainloader.c
+@@ -841,7 +841,7 @@ grub_secureboot_chainloader_unload (void)
+ }
+ 
+ static grub_err_t
+-grub_load_and_start_image(void *boot_image)
++grub_load_image(void *boot_image)
+ {
+   grub_efi_boot_services_t *b;
+   grub_efi_status_t status;
+@@ -883,13 +883,23 @@ grub_load_and_start_image(void *boot_image)
+ static grub_err_t
+ grub_secureboot_chainloader_boot (void)
+ {
++  grub_efi_boot_services_t *b;
+   int rc;
++
+   rc = handle_image ((void *)(unsigned long)address, fsize);
+   if (rc == 0)
+     {
+-      grub_load_and_start_image((void *)(unsigned long)address);
++      /* We weren't able to attempt to execute the image, so fall back
++       * to LoadImage / StartImage.
++       */
++      rc = grub_load_image((void *)(unsigned long)address);
++      if (rc == 0)
++        grub_chainloader_boot ();
+     }
+ 
++  b = grub_efi_system_table->boot_services;
++  efi_call_1 (b->unload_image, image_handle);
++
+   grub_loader_unset ();
+   return grub_errno;
+ }
+@@ -1091,7 +1101,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+     }
+   else
+     {
+-      grub_load_and_start_image(boot_image);
++      grub_load_image(boot_image);
+       grub_file_close (file);
+       grub_device_close (dev);
+       grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
diff --git a/SOURCES/0213-loader-efi-chainloader-simplify-the-loader-state.patch b/SOURCES/0213-loader-efi-chainloader-simplify-the-loader-state.patch
new file mode 100644
index 0000000..205124e
--- /dev/null
+++ b/SOURCES/0213-loader-efi-chainloader-simplify-the-loader-state.patch
@@ -0,0 +1,330 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Chris Coulson <chris.coulson@canonical.com>
+Date: Fri, 29 Apr 2022 21:13:08 +0100
+Subject: [PATCH] loader/efi/chainloader: simplify the loader state
+
+When not using the shim lock protocol, the chainloader command retains
+the source buffer and device path passed to LoadImage, requiring the
+unload hook passed to grub_loader_set to free them. It isn't required
+to retain this state though - they aren't required by StartImage or
+anything else in the boot hook, so clean them up before
+grub_cmd_chainloader finishes.
+
+This also wraps the loader state when using the shim lock protocol
+inside a struct.
+
+Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
+(cherry picked from commit fa39862933b3be1553a580a3a5c28073257d8046)
+[rharwood: fix unitialized handle and double-frees of file/dev]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/loader/efi/chainloader.c | 160 +++++++++++++++++++++++--------------
+ 1 file changed, 102 insertions(+), 58 deletions(-)
+
+diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
+index d3bf02ed8a..3342492ff1 100644
+--- a/grub-core/loader/efi/chainloader.c
++++ b/grub-core/loader/efi/chainloader.c
+@@ -48,38 +48,21 @@ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+ 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);
++struct grub_secureboot_chainloader_context {
++  grub_efi_physical_address_t address;
++  grub_efi_uintn_t pages;
++  grub_ssize_t fsize;
++  grub_efi_device_path_t *file_path;
++  grub_efi_char16_t *cmdline;
++  grub_ssize_t cmdline_len;
++  grub_efi_handle_t dev_handle;
++};
++static struct grub_secureboot_chainloader_context *sb_context;
+ 
+ static grub_err_t
+-grub_chainloader_unload (void)
+-{
+-  grub_efi_boot_services_t *b;
+-
+-  b = grub_efi_system_table->boot_services;
+-  efi_call_1 (b->unload_image, image_handle);
+-  grub_efi_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_chainloader_boot (void)
++grub_start_image (grub_efi_handle_t handle)
+ {
+   grub_efi_boot_services_t *b;
+   grub_efi_status_t status;
+@@ -87,7 +70,7 @@ grub_chainloader_boot (void)
+   grub_efi_char16_t *exit_data = NULL;
+ 
+   b = grub_efi_system_table->boot_services;
+-  status = efi_call_3 (b->start_image, image_handle, &exit_data_size, &exit_data);
++  status = efi_call_3 (b->start_image, handle, &exit_data_size, &exit_data);
+   if (status != GRUB_EFI_SUCCESS)
+     {
+       if (exit_data)
+@@ -111,11 +94,37 @@ grub_chainloader_boot (void)
+   if (exit_data)
+     grub_efi_free_pool (exit_data);
+ 
+-  grub_loader_unset ();
+-
+   return grub_errno;
+ }
+ 
++static grub_err_t
++grub_chainloader_unload (void)
++{
++  grub_efi_loaded_image_t *loaded_image;
++  grub_efi_boot_services_t *b;
++
++  loaded_image = grub_efi_get_loaded_image (image_handle);
++  if (loaded_image != NULL)
++    grub_free (loaded_image->load_options);
++
++  b = grub_efi_system_table->boot_services;
++  efi_call_1 (b->unload_image, image_handle);
++
++  grub_dl_unref (my_mod);
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_chainloader_boot (void)
++{
++  grub_err_t err;
++
++  err = grub_start_image (image_handle);
++
++  grub_loader_unset ();
++  return err;
++}
++
+ static grub_err_t
+ copy_file_path (grub_efi_file_path_device_path_t *fp,
+ 		const char *str, grub_efi_uint16_t len)
+@@ -150,7 +159,7 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename)
+   char *dir_start;
+   char *dir_end;
+   grub_size_t size;
+-  grub_efi_device_path_t *d;
++  grub_efi_device_path_t *d, *file_path;
+ 
+   dir_start = grub_strchr (filename, ')');
+   if (! dir_start)
+@@ -526,10 +535,12 @@ 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)
++handle_image (struct grub_secureboot_chainloader_context *load_context)
+ {
+   grub_efi_loaded_image_t *li, li_bak;
+   grub_efi_status_t efi_status;
++  void *data = (void *)(unsigned long)load_context->address;
++  grub_efi_uint32_t datasize = load_context->fsize;
+   void *buffer = NULL;
+   char *buffer_aligned = NULL;
+   grub_efi_uint32_t i;
+@@ -540,6 +551,7 @@ handle_image (void *data, grub_efi_uint32_t datasize)
+   grub_uint32_t buffer_size;
+   int found_entry_point = 0;
+   int rc;
++  grub_efi_status_t (*entry_point) (grub_efi_handle_t image_handle, grub_efi_system_table_t *system_table);
+ 
+   rc = read_header (data, datasize, &context);
+   if (rc < 0)
+@@ -797,10 +809,10 @@ handle_image (void *data, grub_efi_uint32_t datasize)
+   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;
++  li->load_options = load_context->cmdline;
++  li->load_options_size = load_context->cmdline_len;
++  li->file_path = grub_efi_get_media_file_path (load_context->file_path);
++  li->device_handle = load_context->dev_handle;
+   if (!li->file_path)
+     {
+       grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching file path found");
+@@ -829,19 +841,22 @@ error_exit:
+ static grub_err_t
+ grub_secureboot_chainloader_unload (void)
+ {
+-  grub_efi_free_pages (address, pages);
+-  grub_free (file_path);
+-  grub_free (cmdline);
+-  cmdline = 0;
+-  file_path = 0;
+-  dev_handle = 0;
++  grub_efi_free_pages (sb_context->address, sb_context->pages);
++  grub_free (sb_context->file_path);
++  grub_free (sb_context->cmdline);
++  grub_free (sb_context);
++
++  sb_context = 0;
+ 
+   grub_dl_unref (my_mod);
+   return GRUB_ERR_NONE;
+ }
+ 
+ static grub_err_t
+-grub_load_image(void *boot_image)
++grub_load_image(grub_efi_device_path_t *file_path, void *boot_image,
++		grub_efi_uintn_t image_size, grub_efi_handle_t dev_handle,
++		grub_efi_char16_t *cmdline, grub_ssize_t cmdline_len,
++		grub_efi_handle_t *image_handle_out)
+ {
+   grub_efi_boot_services_t *b;
+   grub_efi_status_t status;
+@@ -850,7 +865,7 @@ grub_load_image(void *boot_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);
++		       boot_image, image_size, image_handle_out);
+   if (status != GRUB_EFI_SUCCESS)
+     {
+       if (status == GRUB_EFI_OUT_OF_RESOURCES)
+@@ -863,7 +878,7 @@ grub_load_image(void *boot_image)
+   /* 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);
++  loaded_image = grub_efi_get_loaded_image (*image_handle_out);
+   if (! loaded_image)
+     {
+       grub_error (GRUB_ERR_BAD_OS, "no loaded image available");
+@@ -885,20 +900,25 @@ grub_secureboot_chainloader_boot (void)
+ {
+   grub_efi_boot_services_t *b;
+   int rc;
++  grub_efi_handle_t handle = 0;
+ 
+-  rc = handle_image ((void *)(unsigned long)address, fsize);
++  rc = handle_image (sb_context);
+   if (rc == 0)
+     {
+       /* We weren't able to attempt to execute the image, so fall back
+        * to LoadImage / StartImage.
+        */
+-      rc = grub_load_image((void *)(unsigned long)address);
++      rc = grub_load_image(sb_context->file_path,
++			   (void *)(unsigned long)sb_context->address,
++			   sb_context->fsize, sb_context->dev_handle,
++			   sb_context->cmdline, sb_context->cmdline_len,
++			   &handle);
+       if (rc == 0)
+-        grub_chainloader_boot ();
++	grub_start_image (handle);
+     }
+ 
+   b = grub_efi_system_table->boot_services;
+-  efi_call_1 (b->unload_image, image_handle);
++  efi_call_1 (b->unload_image, handle);
+ 
+   grub_loader_unset ();
+   return grub_errno;
+@@ -913,9 +933,15 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+   grub_efi_boot_services_t *b;
+   grub_device_t dev = 0;
+   grub_device_t orig_dev = 0;
+-  grub_efi_device_path_t *dp = 0;
++  grub_efi_device_path_t *dp = 0, *file_path = 0;
+   char *filename;
+   void *boot_image = 0;
++  grub_efi_physical_address_t address = 0;
++  grub_ssize_t fsize;
++  grub_efi_uintn_t pages = 0;
++  grub_efi_char16_t *cmdline = 0;
++  grub_ssize_t cmdline_len = 0;
++  grub_efi_handle_t dev_handle = 0;
+ 
+   if (argc == 0)
+     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+@@ -923,12 +949,6 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+ 
+   grub_dl_ref (my_mod);
+ 
+-  /* Initialize some global variables.  */
+-  address = 0;
+-  image_handle = 0;
+-  file_path = 0;
+-  dev_handle = 0;
+-
+   b = grub_efi_system_table->boot_services;
+ 
+   if (argc > 1)
+@@ -1093,17 +1113,35 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+ 
+   if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
+     {
++      sb_context = grub_malloc (sizeof (*sb_context));
++      if (sb_context == NULL)
++        goto fail;
++      sb_context->address = address;
++      sb_context->fsize = fsize;
++      sb_context->pages = pages;
++      sb_context->file_path = file_path;
++      sb_context->cmdline = cmdline;
++      sb_context->cmdline_len = cmdline_len;
++      sb_context->dev_handle = dev_handle;
++
+       grub_file_close (file);
+       grub_device_close (dev);
++
+       grub_loader_set (grub_secureboot_chainloader_boot,
+ 		       grub_secureboot_chainloader_unload, 0);
+       return 0;
+     }
+   else
+     {
+-      grub_load_image(boot_image);
++      grub_load_image(file_path, boot_image, fsize, dev_handle, cmdline,
++		      cmdline_len, &image_handle);
+       grub_file_close (file);
+       grub_device_close (dev);
++
++      /* We're finished with the source image buffer and file path now */
++      efi_call_2 (b->free_pages, address, pages);
++      grub_free (file_path);
++
+       grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
+ 
+       return 0;
+@@ -1130,6 +1168,12 @@ fail:
+   if (cmdline)
+     grub_free (cmdline);
+ 
++  if (image_handle != 0)
++    {
++      efi_call_1 (b->unload_image, image_handle);
++      image_handle = 0;
++    }
++
+   grub_dl_unref (my_mod);
+ 
+   return grub_errno;
diff --git a/SOURCES/0214-commands-boot-Add-API-to-pass-context-to-loader.patch b/SOURCES/0214-commands-boot-Add-API-to-pass-context-to-loader.patch
new file mode 100644
index 0000000..63a2d76
--- /dev/null
+++ b/SOURCES/0214-commands-boot-Add-API-to-pass-context-to-loader.patch
@@ -0,0 +1,158 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Chris Coulson <chris.coulson@canonical.com>
+Date: Fri, 29 Apr 2022 21:16:02 +0100
+Subject: [PATCH] commands/boot: Add API to pass context to loader
+
+Loaders rely on global variables for saving context which is consumed
+in the boot hook and freed in the unload hook. In the case where a loader
+command is executed twice, calling grub_loader_set a second time executes
+the unload hook, but in some cases this runs when the loader's global
+context has already been updated, resulting in the updated context being
+freed and potential use-after-free bugs when the boot hook is subsequently
+called.
+
+This adds a new API (grub_loader_set_ex) which allows a loader to specify
+context that is passed to its boot and unload hooks. This is an alternative
+to requiring that loaders call grub_loader_unset before mutating their
+global context.
+
+Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
+(cherry picked from commit 4322a64dde7e8fedb58e50b79408667129d45dd3)
+---
+ grub-core/commands/boot.c | 66 +++++++++++++++++++++++++++++++++++++++++------
+ include/grub/loader.h     |  5 ++++
+ 2 files changed, 63 insertions(+), 8 deletions(-)
+
+diff --git a/grub-core/commands/boot.c b/grub-core/commands/boot.c
+index bbca81e947..53691a62d9 100644
+--- a/grub-core/commands/boot.c
++++ b/grub-core/commands/boot.c
+@@ -27,10 +27,20 @@
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+-static grub_err_t (*grub_loader_boot_func) (void);
+-static grub_err_t (*grub_loader_unload_func) (void);
++static grub_err_t (*grub_loader_boot_func) (void *);
++static grub_err_t (*grub_loader_unload_func) (void *);
++static void *grub_loader_context;
+ static int grub_loader_flags;
+ 
++struct grub_simple_loader_hooks
++{
++  grub_err_t (*boot) (void);
++  grub_err_t (*unload) (void);
++};
++
++/* Don't heap allocate this to avoid making grub_loader_set fallible. */
++static struct grub_simple_loader_hooks simple_loader_hooks;
++
+ struct grub_preboot
+ {
+   grub_err_t (*preboot_func) (int);
+@@ -44,6 +54,29 @@ static int grub_loader_loaded;
+ static struct grub_preboot *preboots_head = 0,
+   *preboots_tail = 0;
+ 
++static grub_err_t
++grub_simple_boot_hook (void *context)
++{
++  struct grub_simple_loader_hooks *hooks;
++
++  hooks = (struct grub_simple_loader_hooks *) context;
++  return hooks->boot ();
++}
++
++static grub_err_t
++grub_simple_unload_hook (void *context)
++{
++  struct grub_simple_loader_hooks *hooks;
++  grub_err_t ret;
++
++  hooks = (struct grub_simple_loader_hooks *) context;
++
++  ret = hooks->unload ();
++  grub_memset (hooks, 0, sizeof (*hooks));
++
++  return ret;
++}
++
+ int
+ grub_loader_is_loaded (void)
+ {
+@@ -110,28 +143,45 @@ grub_loader_unregister_preboot_hook (struct grub_preboot *hnd)
+ }
+ 
+ void
+-grub_loader_set (grub_err_t (*boot) (void),
+-		 grub_err_t (*unload) (void),
+-		 int flags)
++grub_loader_set_ex (grub_err_t (*boot) (void *),
++		    grub_err_t (*unload) (void *),
++		    void *context,
++		    int flags)
+ {
+   if (grub_loader_loaded && grub_loader_unload_func)
+-    grub_loader_unload_func ();
++    grub_loader_unload_func (grub_loader_context);
+ 
+   grub_loader_boot_func = boot;
+   grub_loader_unload_func = unload;
++  grub_loader_context = context;
+   grub_loader_flags = flags;
+ 
+   grub_loader_loaded = 1;
+ }
+ 
++void
++grub_loader_set (grub_err_t (*boot) (void),
++		 grub_err_t (*unload) (void),
++		 int flags)
++{
++  grub_loader_set_ex (grub_simple_boot_hook,
++		      grub_simple_unload_hook,
++		      &simple_loader_hooks,
++		      flags);
++
++  simple_loader_hooks.boot = boot;
++  simple_loader_hooks.unload = unload;
++}
++
+ void
+ grub_loader_unset(void)
+ {
+   if (grub_loader_loaded && grub_loader_unload_func)
+-    grub_loader_unload_func ();
++    grub_loader_unload_func (grub_loader_context);
+ 
+   grub_loader_boot_func = 0;
+   grub_loader_unload_func = 0;
++  grub_loader_context = 0;
+ 
+   grub_loader_loaded = 0;
+ }
+@@ -158,7 +208,7 @@ grub_loader_boot (void)
+ 	  return err;
+ 	}
+     }
+-  err = (grub_loader_boot_func) ();
++  err = (grub_loader_boot_func) (grub_loader_context);
+ 
+   for (cur = preboots_tail; cur; cur = cur->prev)
+     if (! err)
+diff --git a/include/grub/loader.h b/include/grub/loader.h
+index b208642821..1846fa6c5f 100644
+--- a/include/grub/loader.h
++++ b/include/grub/loader.h
+@@ -40,6 +40,11 @@ void EXPORT_FUNC (grub_loader_set) (grub_err_t (*boot) (void),
+ 				    grub_err_t (*unload) (void),
+ 				    int flags);
+ 
++void EXPORT_FUNC (grub_loader_set_ex) (grub_err_t (*boot) (void *),
++				       grub_err_t (*unload) (void *),
++				       void *context,
++				       int flags);
++
+ /* Unset current loader, if any.  */
+ void EXPORT_FUNC (grub_loader_unset) (void);
+ 
diff --git a/SOURCES/0215-loader-efi-chainloader-Use-grub_loader_set_ex.patch b/SOURCES/0215-loader-efi-chainloader-Use-grub_loader_set_ex.patch
new file mode 100644
index 0000000..fc15b84
--- /dev/null
+++ b/SOURCES/0215-loader-efi-chainloader-Use-grub_loader_set_ex.patch
@@ -0,0 +1,147 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Chris Coulson <chris.coulson@canonical.com>
+Date: Fri, 29 Apr 2022 21:30:56 +0100
+Subject: [PATCH] loader/efi/chainloader: Use grub_loader_set_ex
+
+This ports the EFI chainloader to use grub_loader_set_ex in order to fix
+a use-after-free bug that occurs when grub_cmd_chainloader is executed
+more than once before a boot attempt is performed.
+
+Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
+(cherry picked from commit 4b7f0402b7cb0f67a93be736f2b75b818d7f44c9)
+[rharwood: context sludge from other change]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/loader/efi/chainloader.c | 38 ++++++++++++++++++++++----------------
+ 1 file changed, 22 insertions(+), 16 deletions(-)
+
+diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
+index 3342492ff1..fb874f1855 100644
+--- a/grub-core/loader/efi/chainloader.c
++++ b/grub-core/loader/efi/chainloader.c
+@@ -48,8 +48,6 @@ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+ static grub_dl_t my_mod;
+ 
+-static grub_efi_handle_t image_handle;
+-
+ struct grub_secureboot_chainloader_context {
+   grub_efi_physical_address_t address;
+   grub_efi_uintn_t pages;
+@@ -59,7 +57,6 @@ struct grub_secureboot_chainloader_context {
+   grub_ssize_t cmdline_len;
+   grub_efi_handle_t dev_handle;
+ };
+-static struct grub_secureboot_chainloader_context *sb_context;
+ 
+ static grub_err_t
+ grub_start_image (grub_efi_handle_t handle)
+@@ -98,11 +95,14 @@ grub_start_image (grub_efi_handle_t handle)
+ }
+ 
+ static grub_err_t
+-grub_chainloader_unload (void)
++grub_chainloader_unload (void *context)
+ {
++  grub_efi_handle_t image_handle;
+   grub_efi_loaded_image_t *loaded_image;
+   grub_efi_boot_services_t *b;
+ 
++  image_handle = (grub_efi_handle_t) context;
++
+   loaded_image = grub_efi_get_loaded_image (image_handle);
+   if (loaded_image != NULL)
+     grub_free (loaded_image->load_options);
+@@ -115,10 +115,12 @@ grub_chainloader_unload (void)
+ }
+ 
+ static grub_err_t
+-grub_chainloader_boot (void)
++grub_chainloader_boot (void *context)
+ {
++  grub_efi_handle_t image_handle;
+   grub_err_t err;
+ 
++  image_handle = (grub_efi_handle_t) context;
+   err = grub_start_image (image_handle);
+ 
+   grub_loader_unset ();
+@@ -839,15 +841,17 @@ error_exit:
+ }
+ 
+ static grub_err_t
+-grub_secureboot_chainloader_unload (void)
++grub_secureboot_chainloader_unload (void *context)
+ {
++  struct grub_secureboot_chainloader_context *sb_context;
++
++  sb_context = (struct grub_secureboot_chainloader_context *) context;
++
+   grub_efi_free_pages (sb_context->address, sb_context->pages);
+   grub_free (sb_context->file_path);
+   grub_free (sb_context->cmdline);
+   grub_free (sb_context);
+ 
+-  sb_context = 0;
+-
+   grub_dl_unref (my_mod);
+   return GRUB_ERR_NONE;
+ }
+@@ -896,12 +900,15 @@ grub_load_image(grub_efi_device_path_t *file_path, void *boot_image,
+ }
+ 
+ static grub_err_t
+-grub_secureboot_chainloader_boot (void)
++grub_secureboot_chainloader_boot (void *context)
+ {
++  struct grub_secureboot_chainloader_context *sb_context;
+   grub_efi_boot_services_t *b;
+   int rc;
+   grub_efi_handle_t handle = 0;
+ 
++  sb_context = (struct grub_secureboot_chainloader_context *) context;
++
+   rc = handle_image (sb_context);
+   if (rc == 0)
+     {
+@@ -942,6 +949,8 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+   grub_efi_char16_t *cmdline = 0;
+   grub_ssize_t cmdline_len = 0;
+   grub_efi_handle_t dev_handle = 0;
++  grub_efi_handle_t image_handle = 0;
++  struct grub_secureboot_chainloader_context *sb_context = 0;
+ 
+   if (argc == 0)
+     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+@@ -1127,8 +1136,8 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+       grub_file_close (file);
+       grub_device_close (dev);
+ 
+-      grub_loader_set (grub_secureboot_chainloader_boot,
+-		       grub_secureboot_chainloader_unload, 0);
++      grub_loader_set_ex (grub_secureboot_chainloader_boot,
++			  grub_secureboot_chainloader_unload, sb_context, 0);
+       return 0;
+     }
+   else
+@@ -1142,7 +1151,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+       efi_call_2 (b->free_pages, address, pages);
+       grub_free (file_path);
+ 
+-      grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
++      grub_loader_set_ex (grub_chainloader_boot, grub_chainloader_unload, image_handle, 0);
+ 
+       return 0;
+     }
+@@ -1169,10 +1178,7 @@ fail:
+     grub_free (cmdline);
+ 
+   if (image_handle != 0)
+-    {
+-      efi_call_1 (b->unload_image, image_handle);
+-      image_handle = 0;
+-    }
++    efi_call_1 (b->unload_image, image_handle);
+ 
+   grub_dl_unref (my_mod);
+ 
diff --git a/SOURCES/0216-loader-i386-efi-linux-Avoid-a-use-after-free-in-the-.patch b/SOURCES/0216-loader-i386-efi-linux-Avoid-a-use-after-free-in-the-.patch
new file mode 100644
index 0000000..b79c78c
--- /dev/null
+++ b/SOURCES/0216-loader-i386-efi-linux-Avoid-a-use-after-free-in-the-.patch
@@ -0,0 +1,41 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Chris Coulson <chris.coulson@canonical.com>
+Date: Mon, 2 May 2022 14:39:31 +0200
+Subject: [PATCH] loader/i386/efi/linux: Avoid a use-after-free in the linuxefi
+ loader
+
+In some error paths in grub_cmd_linux, the pointer to lh may be
+dereferenced after the buffer it points to has been freed. There aren't
+any security implications from this because nothing else uses the
+allocator after the buffer is freed and before the pointer is
+dereferenced, but fix it anyway.
+
+Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
+(cherry picked from commit 8224f5a71af94bec8697de17e7e579792db9f9e2)
+---
+ grub-core/loader/i386/efi/linux.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index 941df6400b..27bc2aa161 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -465,9 +465,6 @@ fail:
+   if (file)
+     grub_file_close (file);
+ 
+-  if (kernel)
+-    grub_free (kernel);
+-
+   if (grub_errno != GRUB_ERR_NONE)
+     {
+       grub_dl_unref (my_mod);
+@@ -483,6 +480,8 @@ fail:
+       kernel_free (params, sizeof(*params));
+     }
+ 
++  grub_free (kernel);
++
+   return grub_errno;
+ }
+ 
diff --git a/SOURCES/0217-loader-i386-efi-linux-Use-grub_loader_set_ex.patch b/SOURCES/0217-loader-i386-efi-linux-Use-grub_loader_set_ex.patch
new file mode 100644
index 0000000..1a129db
--- /dev/null
+++ b/SOURCES/0217-loader-i386-efi-linux-Use-grub_loader_set_ex.patch
@@ -0,0 +1,296 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Chris Coulson <chris.coulson@canonical.com>
+Date: Mon, 2 May 2022 17:04:23 +0200
+Subject: [PATCH] loader/i386/efi/linux: Use grub_loader_set_ex
+
+This ports the linuxefi loader to use grub_loader_set_ex in order to fix
+a use-after-fre bug that occurs when grub_cmd_linux is executed more than
+once before a boot attempt is performed.
+
+This is more complicated than for the chainloader command, as the initrd
+command needs access to the loader state. To solve this, the linuxefi
+module registers a dummy initrd command at startup that returns an error.
+The linuxefi command then registers a proper initrd command with a higher
+priority that is passed the loader state.
+
+Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
+(cherry picked from commit 7cf736436b4c934df5ddfa6f44b46a7e07d99fdc)
+[rharwood/pjones: set kernel_size in context]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/loader/i386/efi/linux.c | 146 +++++++++++++++++++++++---------------
+ 1 file changed, 87 insertions(+), 59 deletions(-)
+
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index 27bc2aa161..e3c2d6fe0b 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -34,13 +34,19 @@
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+ static grub_dl_t my_mod;
+-static int loaded;
+-static void *kernel_mem;
+-static grub_uint64_t kernel_size;
+-static void *initrd_mem;
+-static grub_uint32_t handover_offset;
+-struct linux_kernel_params *params;
+-static char *linux_cmdline;
++
++static grub_command_t cmd_linux, cmd_initrd;
++static grub_command_t cmd_linuxefi, cmd_initrdefi;
++
++struct grub_linuxefi_context {
++  void *kernel_mem;
++  grub_uint64_t kernel_size;
++  grub_uint32_t handover_offset;
++  struct linux_kernel_params *params;
++  char *cmdline;
++
++  void *initrd_mem;
++};
+ 
+ #define MIN(a, b) \
+   ({ typeof (a) _a = (a); \
+@@ -123,25 +129,32 @@ kernel_alloc(grub_efi_uintn_t size, const char * const errmsg)
+ }
+ 
+ static grub_err_t
+-grub_linuxefi_boot (void)
++grub_linuxefi_boot (void *data)
+ {
++  struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) data;
++
+   asm volatile ("cli");
+ 
+-  return grub_efi_linux_boot ((char *)kernel_mem,
+-			      handover_offset,
+-			      params);
++  return grub_efi_linux_boot ((char *)context->kernel_mem,
++			      context->handover_offset,
++			      context->params);
+ }
+ 
+ static grub_err_t
+-grub_linuxefi_unload (void)
++grub_linuxefi_unload (void *data)
+ {
++  struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) data;
++  struct linux_kernel_params *params = context->params;
++
+   grub_dl_unref (my_mod);
+-  loaded = 0;
+ 
+-  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));
++  kernel_free (context->initrd_mem, params->ramdisk_size);
++  kernel_free (context->cmdline, params->cmdline_size + 1);
++  kernel_free (context->kernel_mem, context->kernel_size);
++  kernel_free (params, sizeof(*params));
++  cmd_initrd->data = 0;
++  cmd_initrdefi->data = 0;
++  grub_free (context);
+ 
+   return GRUB_ERR_NONE;
+ }
+@@ -188,13 +201,14 @@ read(grub_file_t file, grub_uint8_t *bufp, grub_size_t len)
+ #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[])
++grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[])
+ {
+   grub_file_t *files = 0;
+   int i, nfiles = 0;
+   grub_size_t size = 0;
+   grub_uint8_t *ptr;
++  struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) cmd->data;
++  struct linux_kernel_params *params;
+ 
+   if (argc == 0)
+     {
+@@ -202,12 +216,14 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+       goto fail;
+     }
+ 
+-  if (!loaded)
++  if (!context)
+     {
+       grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
+       goto fail;
+     }
+ 
++  params = context->params;
++
+   files = grub_calloc (argc, sizeof (files[0]));
+   if (!files)
+     goto fail;
+@@ -225,19 +241,19 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+ 	}
+     }
+ 
+-  initrd_mem = kernel_alloc(size, N_("can't allocate initrd"));
+-  if (initrd_mem == NULL)
++  context->initrd_mem = kernel_alloc(size, N_("can't allocate initrd"));
++  if (context->initrd_mem == NULL)
+     goto fail;
+-  grub_dprintf ("linux", "initrd_mem = %p\n", initrd_mem);
++  grub_dprintf ("linux", "initrd_mem = %p\n", context->initrd_mem);
+ 
+   params->ramdisk_size = LOW_U32(size);
+-  params->ramdisk_image = LOW_U32(initrd_mem);
++  params->ramdisk_image = LOW_U32(context->initrd_mem);
+ #if defined(__x86_64__)
+   params->ext_ramdisk_size = HIGH_U32(size);
+-  params->ext_ramdisk_image = HIGH_U32(initrd_mem);
++  params->ext_ramdisk_image = HIGH_U32(context->initrd_mem);
+ #endif
+ 
+-  ptr = initrd_mem;
++  ptr = context->initrd_mem;
+ 
+   for (i = 0; i < nfiles; i++)
+     {
+@@ -261,8 +277,8 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+     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,
++  if (context->initrd_mem && grub_errno)
++    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)context->initrd_mem,
+ 			 BYTES_TO_PAGES(size));
+ 
+   return grub_errno;
+@@ -277,6 +293,12 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   grub_ssize_t start, filelen;
+   void *kernel = NULL;
+   int setup_header_end_offset;
++  void *kernel_mem = 0;
++  grub_uint64_t kernel_size = 0;
++  grub_uint32_t handover_offset;
++  struct linux_kernel_params *params = 0;
++  char *cmdline = 0;
++  struct grub_linuxefi_context *context = 0;
+ 
+   grub_dl_ref (my_mod);
+ 
+@@ -390,27 +412,27 @@ 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 = kernel_alloc (lh->cmdline_size + 1, N_("can't allocate cmdline"));
+-  if (!linux_cmdline)
++  cmdline = kernel_alloc (lh->cmdline_size + 1, N_("can't allocate cmdline"));
++  if (!cmdline)
+     goto fail;
+-  grub_dprintf ("linux", "linux_cmdline = %p\n", linux_cmdline);
++  grub_dprintf ("linux", "cmdline = %p\n", cmdline);
+ 
+-  grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
++  grub_memcpy (cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
+   grub_create_loader_cmdline (argc, argv,
+-                              linux_cmdline + sizeof (LINUX_IMAGE) - 1,
++                              cmdline + sizeof (LINUX_IMAGE) - 1,
+ 			      lh->cmdline_size - (sizeof (LINUX_IMAGE) - 1),
+ 			      GRUB_VERIFY_KERNEL_CMDLINE);
+ 
+-  grub_dprintf ("linux", "cmdline:%s\n", linux_cmdline);
++  grub_dprintf ("linux", "cmdline:%s\n", cmdline);
+   grub_dprintf ("linux", "setting lh->cmd_line_ptr to 0x%08x\n",
+-		LOW_U32(linux_cmdline));
+-  lh->cmd_line_ptr = LOW_U32(linux_cmdline);
++		LOW_U32(cmdline));
++  lh->cmd_line_ptr = LOW_U32(cmdline);
+ #if defined(__x86_64__)
+-  if ((grub_efi_uintn_t)linux_cmdline > 0xffffffffull)
++  if ((grub_efi_uintn_t)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);
++		    HIGH_U32(cmdline));
++      params->ext_cmd_line_ptr = HIGH_U32(cmdline);
+     }
+ #endif
+ 
+@@ -435,16 +457,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+     }
+   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"));
++  kernel_size = lh->init_size;
++  kernel_mem = kernel_alloc (kernel_size, N_("can't allocate kernel"));
+   restore_addresses();
+   if (!kernel_mem)
+     goto fail;
+   grub_dprintf("linux", "kernel_mem = %p\n", kernel_mem);
+ 
+-  grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0);
+-
+-  loaded = 1;
+-
+   grub_dprintf ("linux", "setting lh->code32_start to 0x%08x\n",
+ 		LOW_U32(kernel_mem));
+   lh->code32_start = LOW_U32(kernel_mem);
+@@ -461,33 +480,42 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 		"setting lh->ext_loader_{type,ver} = {0x%02x,0x%02x}\n",
+ 		params->ext_loader_type, params->ext_loader_ver);
+ 
++  context = grub_zalloc (sizeof (*context));
++  if (!context)
++    goto fail;
++  context->kernel_mem = kernel_mem;
++  context->kernel_size = kernel_size;
++  context->handover_offset = handover_offset;
++  context->params = params;
++  context->cmdline = cmdline;
++
++  grub_loader_set_ex (grub_linuxefi_boot, grub_linuxefi_unload, context, 0);
++
++  cmd_initrd->data = context;
++  cmd_initrdefi->data = context;
++
++  grub_file_close (file);
++  grub_free (kernel);
++  return 0;
++
+ fail:
+   if (file)
+     grub_file_close (file);
+ 
+-  if (grub_errno != GRUB_ERR_NONE)
+-    {
+-      grub_dl_unref (my_mod);
+-      loaded = 0;
+-    }
++  grub_dl_unref (my_mod);
+ 
+-  if (!loaded)
+-    {
+-      if (lh)
+-	kernel_free (linux_cmdline, lh->cmdline_size + 1);
++  if (lh)
++    kernel_free (cmdline, lh->cmdline_size + 1);
+ 
+-      kernel_free (kernel_mem, kernel_size);
+-      kernel_free (params, sizeof(*params));
+-    }
++  kernel_free (kernel_mem, kernel_size);
++  kernel_free (params, sizeof(*params));
+ 
++  grub_free (context);
+   grub_free (kernel);
+ 
+   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 =
diff --git a/SOURCES/0218-loader-i386-efi-linux-Fix-a-memory-leak-in-the-initr.patch b/SOURCES/0218-loader-i386-efi-linux-Fix-a-memory-leak-in-the-initr.patch
new file mode 100644
index 0000000..51953fd
--- /dev/null
+++ b/SOURCES/0218-loader-i386-efi-linux-Fix-a-memory-leak-in-the-initr.patch
@@ -0,0 +1,75 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Chris Coulson <chris.coulson@canonical.com>
+Date: Tue, 3 May 2022 09:47:35 +0200
+Subject: [PATCH] loader/i386/efi/linux: Fix a memory leak in the initrd
+ command
+
+Subsequent invocations of the initrd command result in the previous
+initrd being leaked, so fix that.
+
+Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
+(cherry picked from commit d98af31ce1e31bb22163960d53f5eb28c66582a0)
+---
+ grub-core/loader/i386/efi/linux.c | 21 ++++++++++++---------
+ 1 file changed, 12 insertions(+), 9 deletions(-)
+
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index e3c2d6fe0b..9e5c11ac69 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -209,6 +209,7 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[])
+   grub_uint8_t *ptr;
+   struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) cmd->data;
+   struct linux_kernel_params *params;
++  void *initrd_mem = 0;
+ 
+   if (argc == 0)
+     {
+@@ -241,19 +242,19 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[])
+ 	}
+     }
+ 
+-  context->initrd_mem = kernel_alloc(size, N_("can't allocate initrd"));
+-  if (context->initrd_mem == NULL)
++  initrd_mem = kernel_alloc(size, N_("can't allocate initrd"));
++  if (initrd_mem == NULL)
+     goto fail;
+-  grub_dprintf ("linux", "initrd_mem = %p\n", context->initrd_mem);
++  grub_dprintf ("linux", "initrd_mem = %p\n", initrd_mem);
+ 
+   params->ramdisk_size = LOW_U32(size);
+-  params->ramdisk_image = LOW_U32(context->initrd_mem);
++  params->ramdisk_image = LOW_U32(initrd_mem);
+ #if defined(__x86_64__)
+   params->ext_ramdisk_size = HIGH_U32(size);
+-  params->ext_ramdisk_image = HIGH_U32(context->initrd_mem);
++  params->ext_ramdisk_image = HIGH_U32(initrd_mem);
+ #endif
+ 
+-  ptr = context->initrd_mem;
++  ptr = initrd_mem;
+ 
+   for (i = 0; i < nfiles; i++)
+     {
+@@ -270,6 +271,9 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[])
+       ptr += ALIGN_UP_OVERHEAD (cursize, 4);
+     }
+ 
++  kernel_free(context->initrd_mem, params->ramdisk_size);
++
++  context->initrd_mem = initrd_mem;
+   params->ramdisk_size = size;
+ 
+  fail:
+@@ -277,9 +281,8 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[])
+     grub_file_close (files[i]);
+   grub_free (files);
+ 
+-  if (context->initrd_mem && grub_errno)
+-    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)context->initrd_mem,
+-			 BYTES_TO_PAGES(size));
++  if (initrd_mem && grub_errno)
++    kernel_free (initrd_mem, size);
+ 
+   return grub_errno;
+ }
diff --git a/SOURCES/0219-kern-efi-sb-Reject-non-kernel-files-in-the-shim_lock.patch b/SOURCES/0219-kern-efi-sb-Reject-non-kernel-files-in-the-shim_lock.patch
new file mode 100644
index 0000000..715e6e1
--- /dev/null
+++ b/SOURCES/0219-kern-efi-sb-Reject-non-kernel-files-in-the-shim_lock.patch
@@ -0,0 +1,101 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Julian Andres Klode <julian.klode@canonical.com>
+Date: Thu, 2 Dec 2021 15:03:53 +0100
+Subject: [PATCH] kern/efi/sb: Reject non-kernel files in the shim_lock
+ verifier
+
+We must not allow other verifiers to pass things like the GRUB modules.
+Instead of maintaining a blocklist, maintain an allowlist of things
+that we do not care about.
+
+This allowlist really should be made reusable, and shared by the
+lockdown verifier, but this is the minimal patch addressing
+security concerns where the TPM verifier was able to mark modules
+as verified (or the OpenPGP verifier for that matter), when it
+should not do so on shim-powered secure boot systems.
+
+Fixes: CVE-2022-28735
+
+Signed-off-by: Julian Andres Klode <julian.klode@canonical.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit fa61ad69861c1cb3f68bf853d78fae7fd93986a0)
+---
+ grub-core/kern/efi/sb.c | 39 ++++++++++++++++++++++++++++++++++++---
+ include/grub/verify.h   |  1 +
+ 2 files changed, 37 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/kern/efi/sb.c b/grub-core/kern/efi/sb.c
+index c52ec6226a..89c4bb3fd1 100644
+--- a/grub-core/kern/efi/sb.c
++++ b/grub-core/kern/efi/sb.c
+@@ -119,10 +119,11 @@ shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)),
+ 			 void **context __attribute__ ((unused)),
+ 			 enum grub_verify_flags *flags)
+ {
+-  *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
++  *flags = GRUB_VERIFY_FLAGS_NONE;
+ 
+   switch (type & GRUB_FILE_TYPE_MASK)
+     {
++    /* Files we check. */
+     case GRUB_FILE_TYPE_LINUX_KERNEL:
+     case GRUB_FILE_TYPE_MULTIBOOT_KERNEL:
+     case GRUB_FILE_TYPE_BSD_KERNEL:
+@@ -130,11 +131,43 @@ shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)),
+     case GRUB_FILE_TYPE_PLAN9_KERNEL:
+     case GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE:
+       *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK;
++      return GRUB_ERR_NONE;
+ 
+-      /* Fall through. */
++    /* Files that do not affect secureboot state. */
++    case GRUB_FILE_TYPE_NONE:
++    case GRUB_FILE_TYPE_LOOPBACK:
++    case GRUB_FILE_TYPE_LINUX_INITRD:
++    case GRUB_FILE_TYPE_OPENBSD_RAMDISK:
++    case GRUB_FILE_TYPE_XNU_RAMDISK:
++    case GRUB_FILE_TYPE_SIGNATURE:
++    case GRUB_FILE_TYPE_PUBLIC_KEY:
++    case GRUB_FILE_TYPE_PUBLIC_KEY_TRUST:
++    case GRUB_FILE_TYPE_PRINT_BLOCKLIST:
++    case GRUB_FILE_TYPE_TESTLOAD:
++    case GRUB_FILE_TYPE_GET_SIZE:
++    case GRUB_FILE_TYPE_FONT:
++    case GRUB_FILE_TYPE_ZFS_ENCRYPTION_KEY:
++    case GRUB_FILE_TYPE_CAT:
++    case GRUB_FILE_TYPE_HEXCAT:
++    case GRUB_FILE_TYPE_CMP:
++    case GRUB_FILE_TYPE_HASHLIST:
++    case GRUB_FILE_TYPE_TO_HASH:
++    case GRUB_FILE_TYPE_KEYBOARD_LAYOUT:
++    case GRUB_FILE_TYPE_PIXMAP:
++    case GRUB_FILE_TYPE_GRUB_MODULE_LIST:
++    case GRUB_FILE_TYPE_CONFIG:
++    case GRUB_FILE_TYPE_THEME:
++    case GRUB_FILE_TYPE_GETTEXT_CATALOG:
++    case GRUB_FILE_TYPE_FS_SEARCH:
++    case GRUB_FILE_TYPE_LOADENV:
++    case GRUB_FILE_TYPE_SAVEENV:
++    case GRUB_FILE_TYPE_VERIFY_SIGNATURE:
++      *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
++      return GRUB_ERR_NONE;
+ 
++    /* Other files. */
+     default:
+-      return GRUB_ERR_NONE;
++      return grub_error (GRUB_ERR_ACCESS_DENIED, N_("prohibited by secure boot policy"));
+     }
+ }
+ 
+diff --git a/include/grub/verify.h b/include/grub/verify.h
+index cd129c398f..672ae16924 100644
+--- a/include/grub/verify.h
++++ b/include/grub/verify.h
+@@ -24,6 +24,7 @@
+ 
+ enum grub_verify_flags
+   {
++    GRUB_VERIFY_FLAGS_NONE		= 0,
+     GRUB_VERIFY_FLAGS_SKIP_VERIFICATION	= 1,
+     GRUB_VERIFY_FLAGS_SINGLE_CHUNK	= 2,
+     /* Defer verification to another authority. */
diff --git a/SOURCES/0220-kern-file-Do-not-leak-device_name-on-error-in-grub_f.patch b/SOURCES/0220-kern-file-Do-not-leak-device_name-on-error-in-grub_f.patch
new file mode 100644
index 0000000..59f9471
--- /dev/null
+++ b/SOURCES/0220-kern-file-Do-not-leak-device_name-on-error-in-grub_f.patch
@@ -0,0 +1,39 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Fri, 25 Jun 2021 02:19:05 +1000
+Subject: [PATCH] kern/file: Do not leak device_name on error in
+ grub_file_open()
+
+If we have an error in grub_file_open() before we free device_name, we
+will leak it.
+
+Free device_name in the error path and null out the pointer in the good
+path once we free it there.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 1499a5068839fa37cb77ecef4b5bdacbd1ed12ea)
+---
+ grub-core/kern/file.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c
+index ec10e54fc0..db938e099d 100644
+--- a/grub-core/kern/file.c
++++ b/grub-core/kern/file.c
+@@ -84,6 +84,7 @@ grub_file_open (const char *name, enum grub_file_type type)
+ 
+   device = grub_device_open (device_name);
+   grub_free (device_name);
++  device_name = NULL;
+   if (! device)
+     goto fail;
+ 
+@@ -138,6 +139,7 @@ grub_file_open (const char *name, enum grub_file_type type)
+   return file;
+ 
+  fail:
++  grub_free (device_name);
+   if (device)
+     grub_device_close (device);
+ 
diff --git a/SOURCES/0221-video-readers-png-Abort-sooner-if-a-read-operation-f.patch b/SOURCES/0221-video-readers-png-Abort-sooner-if-a-read-operation-f.patch
new file mode 100644
index 0000000..385d3ed
--- /dev/null
+++ b/SOURCES/0221-video-readers-png-Abort-sooner-if-a-read-operation-f.patch
@@ -0,0 +1,198 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Tue, 6 Jul 2021 14:02:55 +1000
+Subject: [PATCH] video/readers/png: Abort sooner if a read operation fails
+
+Fuzzing revealed some inputs that were taking a long time, potentially
+forever, because they did not bail quickly upon encountering an I/O error.
+
+Try to catch I/O errors sooner and bail out.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 882be97d1df6449b9fd4d593f0cb70005fde3494)
+---
+ grub-core/video/readers/png.c | 55 ++++++++++++++++++++++++++++++++++++-------
+ 1 file changed, 47 insertions(+), 8 deletions(-)
+
+diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c
+index 0157ff7420..e2a6b1cf3c 100644
+--- a/grub-core/video/readers/png.c
++++ b/grub-core/video/readers/png.c
+@@ -142,6 +142,7 @@ static grub_uint8_t
+ grub_png_get_byte (struct grub_png_data *data)
+ {
+   grub_uint8_t r;
++  grub_ssize_t bytes_read = 0;
+ 
+   if ((data->inside_idat) && (data->idat_remain == 0))
+     {
+@@ -175,7 +176,14 @@ grub_png_get_byte (struct grub_png_data *data)
+     }
+ 
+   r = 0;
+-  grub_file_read (data->file, &r, 1);
++  bytes_read = grub_file_read (data->file, &r, 1);
++
++  if (bytes_read != 1)
++    {
++      grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		  "png: unexpected end of data");
++      return 0;
++    }
+ 
+   if (data->inside_idat)
+     data->idat_remain--;
+@@ -231,15 +239,16 @@ grub_png_decode_image_palette (struct grub_png_data *data,
+   if (len == 0)
+     return GRUB_ERR_NONE;
+ 
+-  for (i = 0; 3 * i < len && i < 256; i++)
++  grub_errno = GRUB_ERR_NONE;
++  for (i = 0; 3 * i < len && i < 256 && grub_errno == GRUB_ERR_NONE; i++)
+     for (j = 0; j < 3; j++)
+       data->palette[i][j] = grub_png_get_byte (data);
+-  for (i *= 3; i < len; i++)
++  for (i *= 3; i < len && grub_errno == GRUB_ERR_NONE; i++)
+     grub_png_get_byte (data);
+ 
+   grub_png_get_dword (data);
+ 
+-  return GRUB_ERR_NONE;
++  return grub_errno;
+ }
+ 
+ static grub_err_t
+@@ -256,9 +265,13 @@ grub_png_decode_image_header (struct grub_png_data *data)
+     return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: invalid image size");
+ 
+   color_bits = grub_png_get_byte (data);
++  if (grub_errno != GRUB_ERR_NONE)
++    return grub_errno;
+   data->is_16bit = (color_bits == 16);
+ 
+   color_type = grub_png_get_byte (data);
++  if (grub_errno != GRUB_ERR_NONE)
++    return grub_errno;
+ 
+   /* According to PNG spec, no other types are valid.  */
+   if ((color_type & ~(PNG_COLOR_MASK_ALPHA | PNG_COLOR_MASK_COLOR))
+@@ -340,14 +353,20 @@ grub_png_decode_image_header (struct grub_png_data *data)
+   if (grub_png_get_byte (data) != PNG_COMPRESSION_BASE)
+     return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ 		       "png: compression method not supported");
++  if (grub_errno != GRUB_ERR_NONE)
++    return grub_errno;
+ 
+   if (grub_png_get_byte (data) != PNG_FILTER_TYPE_BASE)
+     return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ 		       "png: filter method not supported");
++  if (grub_errno != GRUB_ERR_NONE)
++    return grub_errno;
+ 
+   if (grub_png_get_byte (data) != PNG_INTERLACE_NONE)
+     return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ 		       "png: interlace method not supported");
++  if (grub_errno != GRUB_ERR_NONE)
++    return grub_errno;
+ 
+   /* Skip crc checksum.  */
+   grub_png_get_dword (data);
+@@ -449,7 +468,7 @@ grub_png_get_huff_code (struct grub_png_data *data, struct huff_table *ht)
+   int code, i;
+ 
+   code = 0;
+-  for (i = 0; i < ht->max_length; i++)
++  for (i = 0; i < ht->max_length && grub_errno == GRUB_ERR_NONE; i++)
+     {
+       code = (code << 1) + grub_png_get_bits (data, 1);
+       if (code < ht->maxval[i])
+@@ -504,8 +523,14 @@ grub_png_init_dynamic_block (struct grub_png_data *data)
+   grub_uint8_t lens[DEFLATE_HCLEN_MAX];
+ 
+   nl = DEFLATE_HLIT_BASE + grub_png_get_bits (data, 5);
++  if (grub_errno != GRUB_ERR_NONE)
++    return grub_errno;
+   nd = DEFLATE_HDIST_BASE + grub_png_get_bits (data, 5);
++  if (grub_errno != GRUB_ERR_NONE)
++    return grub_errno;
+   nb = DEFLATE_HCLEN_BASE + grub_png_get_bits (data, 4);
++  if (grub_errno != GRUB_ERR_NONE)
++    return grub_errno;
+ 
+   if ((nl > DEFLATE_HLIT_MAX) || (nd > DEFLATE_HDIST_MAX) ||
+       (nb > DEFLATE_HCLEN_MAX))
+@@ -533,7 +558,7 @@ grub_png_init_dynamic_block (struct grub_png_data *data)
+ 			    data->dist_offset);
+ 
+   prev = 0;
+-  for (i = 0; i < nl + nd; i++)
++  for (i = 0; i < nl + nd && grub_errno == GRUB_ERR_NONE; i++)
+     {
+       int n, code;
+       struct huff_table *ht;
+@@ -721,17 +746,21 @@ grub_png_read_dynamic_block (struct grub_png_data *data)
+ 	  len = cplens[n];
+ 	  if (cplext[n])
+ 	    len += grub_png_get_bits (data, cplext[n]);
++	  if (grub_errno != GRUB_ERR_NONE)
++	    return grub_errno;
+ 
+ 	  n = grub_png_get_huff_code (data, &data->dist_table);
+ 	  dist = cpdist[n];
+ 	  if (cpdext[n])
+ 	    dist += grub_png_get_bits (data, cpdext[n]);
++	  if (grub_errno != GRUB_ERR_NONE)
++	    return grub_errno;
+ 
+ 	  pos = data->wp - dist;
+ 	  if (pos < 0)
+ 	    pos += WSIZE;
+ 
+-	  while (len > 0)
++	  while (len > 0 && grub_errno == GRUB_ERR_NONE)
+ 	    {
+ 	      data->slide[data->wp] = data->slide[pos];
+ 	      grub_png_output_byte (data, data->slide[data->wp]);
+@@ -759,7 +788,11 @@ grub_png_decode_image_data (struct grub_png_data *data)
+   int final;
+ 
+   cmf = grub_png_get_byte (data);
++  if (grub_errno != GRUB_ERR_NONE)
++    return grub_errno;
+   flg = grub_png_get_byte (data);
++  if (grub_errno != GRUB_ERR_NONE)
++    return grub_errno;
+ 
+   if ((cmf & 0xF) != Z_DEFLATED)
+     return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+@@ -774,7 +807,11 @@ grub_png_decode_image_data (struct grub_png_data *data)
+       int block_type;
+ 
+       final = grub_png_get_bits (data, 1);
++      if (grub_errno != GRUB_ERR_NONE)
++	return grub_errno;
+       block_type = grub_png_get_bits (data, 2);
++      if (grub_errno != GRUB_ERR_NONE)
++	return grub_errno;
+ 
+       switch (block_type)
+ 	{
+@@ -790,7 +827,7 @@ grub_png_decode_image_data (struct grub_png_data *data)
+ 	    grub_png_get_byte (data);
+ 	    grub_png_get_byte (data);
+ 
+-	    for (i = 0; i < len; i++)
++	    for (i = 0; i < len && grub_errno == GRUB_ERR_NONE; i++)
+ 	      grub_png_output_byte (data, grub_png_get_byte (data));
+ 
+ 	    break;
+@@ -1045,6 +1082,8 @@ grub_png_decode_png (struct grub_png_data *data)
+ 
+       len = grub_png_get_dword (data);
+       type = grub_png_get_dword (data);
++      if (grub_errno != GRUB_ERR_NONE)
++	break;
+       data->next_offset = data->file->offset + len + 4;
+ 
+       switch (type)
diff --git a/SOURCES/0222-video-readers-png-Refuse-to-handle-multiple-image-he.patch b/SOURCES/0222-video-readers-png-Refuse-to-handle-multiple-image-he.patch
new file mode 100644
index 0000000..9168fe5
--- /dev/null
+++ b/SOURCES/0222-video-readers-png-Refuse-to-handle-multiple-image-he.patch
@@ -0,0 +1,28 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Tue, 6 Jul 2021 14:13:40 +1000
+Subject: [PATCH] video/readers/png: Refuse to handle multiple image headers
+
+This causes the bitmap to be leaked. Do not permit multiple image headers.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 8ce433557adeadbc46429aabb9f850b02ad2bdfb)
+---
+ grub-core/video/readers/png.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c
+index e2a6b1cf3c..8955b8ecfd 100644
+--- a/grub-core/video/readers/png.c
++++ b/grub-core/video/readers/png.c
+@@ -258,6 +258,9 @@ grub_png_decode_image_header (struct grub_png_data *data)
+   int color_bits;
+   enum grub_video_blit_format blt;
+ 
++  if (data->image_width || data->image_height)
++    return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: two image headers found");
++
+   data->image_width = grub_png_get_dword (data);
+   data->image_height = grub_png_get_dword (data);
+ 
diff --git a/SOURCES/0223-video-readers-png-Drop-greyscale-support-to-fix-heap.patch b/SOURCES/0223-video-readers-png-Drop-greyscale-support-to-fix-heap.patch
new file mode 100644
index 0000000..4529cb8
--- /dev/null
+++ b/SOURCES/0223-video-readers-png-Drop-greyscale-support-to-fix-heap.patch
@@ -0,0 +1,170 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Tue, 6 Jul 2021 18:51:35 +1000
+Subject: [PATCH] video/readers/png: Drop greyscale support to fix heap
+ out-of-bounds write
+
+A 16-bit greyscale PNG without alpha is processed in the following loop:
+
+      for (i = 0; i < (data->image_width * data->image_height);
+	   i++, d1 += 4, d2 += 2)
+	{
+	  d1[R3] = d2[1];
+	  d1[G3] = d2[1];
+	  d1[B3] = d2[1];
+	}
+
+The increment of d1 is wrong. d1 is incremented by 4 bytes per iteration,
+but there are only 3 bytes allocated for storage. This means that image
+data will overwrite somewhat-attacker-controlled parts of memory - 3 bytes
+out of every 4 following the end of the image.
+
+This has existed since greyscale support was added in 2013 in commit
+3ccf16dff98f (grub-core/video/readers/png.c: Support grayscale).
+
+Saving starfield.png as a 16-bit greyscale image without alpha in the gimp
+and attempting to load it causes grub-emu to crash - I don't think this code
+has ever worked.
+
+Delete all PNG greyscale support.
+
+Fixes: CVE-2021-3695
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 0e1d163382669bd734439d8864ee969616d971d9)
+[rharwood: context conflict]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/video/readers/png.c | 85 +++----------------------------------------
+ 1 file changed, 6 insertions(+), 79 deletions(-)
+
+diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c
+index 8955b8ecfd..a3161e25b6 100644
+--- a/grub-core/video/readers/png.c
++++ b/grub-core/video/readers/png.c
+@@ -100,7 +100,7 @@ struct grub_png_data
+ 
+   unsigned image_width, image_height;
+   int bpp, is_16bit;
+-  int raw_bytes, is_gray, is_alpha, is_palette;
++  int raw_bytes, is_alpha, is_palette;
+   int row_bytes, color_bits;
+   grub_uint8_t *image_data;
+ 
+@@ -296,13 +296,13 @@ grub_png_decode_image_header (struct grub_png_data *data)
+     data->bpp = 3;
+   else
+     {
+-      data->is_gray = 1;
+-      data->bpp = 1;
++      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			 "png: color type not supported");
+     }
+ 
+   if ((color_bits != 8) && (color_bits != 16)
+       && (color_bits != 4
+-	  || !(data->is_gray || data->is_palette)))
++	  || !data->is_palette))
+     return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+                        "png: bit depth must be 8 or 16");
+ 
+@@ -331,7 +331,7 @@ grub_png_decode_image_header (struct grub_png_data *data)
+     }
+ 
+ #ifndef GRUB_CPU_WORDS_BIGENDIAN
+-  if (data->is_16bit || data->is_gray || data->is_palette)
++  if (data->is_16bit || data->is_palette)
+ #endif
+     {
+       data->image_data = grub_calloc (data->image_height, data->row_bytes);
+@@ -899,27 +899,8 @@ grub_png_convert_image (struct grub_png_data *data)
+       int shift;
+       int mask = (1 << data->color_bits) - 1;
+       unsigned j;
+-      if (data->is_gray)
+-	{
+-	  /* Generic formula is
+-	     (0xff * i) / ((1U << data->color_bits) - 1)
+-	     but for allowed bit depth of 1, 2 and for it's
+-	     equivalent to
+-	     (0xff / ((1U << data->color_bits) - 1)) * i
+-	     Precompute the multipliers to avoid division.
+-	  */
+ 
+-	  const grub_uint8_t multipliers[5] = { 0xff, 0xff, 0x55, 0x24, 0x11 };
+-	  for (i = 0; i < (1U << data->color_bits); i++)
+-	    {
+-	      grub_uint8_t col = multipliers[data->color_bits] * i;
+-	      palette[i][0] = col;
+-	      palette[i][1] = col;
+-	      palette[i][2] = col;
+-	    }
+-	}
+-      else
+-	grub_memcpy (palette, data->palette, 3 << data->color_bits);
++      grub_memcpy (palette, data->palette, 3 << data->color_bits);
+       d1c = d1;
+       d2c = d2;
+       for (j = 0; j < data->image_height; j++, d1c += data->image_width * 3,
+@@ -956,60 +937,6 @@ grub_png_convert_image (struct grub_png_data *data)
+ 	}
+       return;
+     }
+-  
+-  if (data->is_gray)
+-    {
+-      switch (data->bpp)
+-	{
+-	case 4:
+-	  /* 16-bit gray with alpha.  */
+-	  for (i = 0; i < (data->image_width * data->image_height);
+-	       i++, d1 += 4, d2 += 4)
+-	    {
+-	      d1[R4] = d2[3];
+-	      d1[G4] = d2[3];
+-	      d1[B4] = d2[3];
+-	      d1[A4] = d2[1];
+-	    }
+-	  break;
+-	case 2:
+-	  if (data->is_16bit)
+-	    /* 16-bit gray without alpha.  */
+-	    {
+-	      for (i = 0; i < (data->image_width * data->image_height);
+-		   i++, d1 += 4, d2 += 2)
+-		{
+-		  d1[R3] = d2[1];
+-		  d1[G3] = d2[1];
+-		  d1[B3] = d2[1];
+-		}
+-	    }
+-	  else
+-	    /* 8-bit gray with alpha.  */
+-	    {
+-	      for (i = 0; i < (data->image_width * data->image_height);
+-		   i++, d1 += 4, d2 += 2)
+-		{
+-		  d1[R4] = d2[1];
+-		  d1[G4] = d2[1];
+-		  d1[B4] = d2[1];
+-		  d1[A4] = d2[0];
+-		}
+-	    }
+-	  break;
+-	  /* 8-bit gray without alpha.  */
+-	case 1:
+-	  for (i = 0; i < (data->image_width * data->image_height);
+-	       i++, d1 += 3, d2++)
+-	    {
+-	      d1[R3] = d2[0];
+-	      d1[G3] = d2[0];
+-	      d1[B3] = d2[0];
+-	    }
+-	  break;
+-	}
+-      return;
+-    }
+ 
+     {
+   /* Only copy the upper 8 bit.  */
diff --git a/SOURCES/0224-video-readers-png-Avoid-heap-OOB-R-W-inserting-huff-.patch b/SOURCES/0224-video-readers-png-Avoid-heap-OOB-R-W-inserting-huff-.patch
new file mode 100644
index 0000000..51dddfe
--- /dev/null
+++ b/SOURCES/0224-video-readers-png-Avoid-heap-OOB-R-W-inserting-huff-.patch
@@ -0,0 +1,40 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Tue, 6 Jul 2021 23:25:07 +1000
+Subject: [PATCH] video/readers/png: Avoid heap OOB R/W inserting huff table
+ items
+
+In fuzzing we observed crashes where a code would attempt to be inserted
+into a huffman table before the start, leading to a set of heap OOB reads
+and writes as table entries with negative indices were shifted around and
+the new code written in.
+
+Catch the case where we would underflow the array and bail.
+
+Fixes: CVE-2021-3696
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 1ae9a91d42cb40da8a6f11fac65541858e340afa)
+---
+ grub-core/video/readers/png.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c
+index a3161e25b6..d7ed5aa6cf 100644
+--- a/grub-core/video/readers/png.c
++++ b/grub-core/video/readers/png.c
+@@ -438,6 +438,13 @@ grub_png_insert_huff_item (struct huff_table *ht, int code, int len)
+   for (i = len; i < ht->max_length; i++)
+     n += ht->maxval[i];
+ 
++  if (n > ht->num_values)
++    {
++      grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		  "png: out of range inserting huffman table item");
++      return;
++    }
++
+   for (i = 0; i < n; i++)
+     ht->values[ht->num_values - i] = ht->values[ht->num_values - i - 1];
+ 
diff --git a/SOURCES/0225-video-readers-png-Sanity-check-some-huffman-codes.patch b/SOURCES/0225-video-readers-png-Sanity-check-some-huffman-codes.patch
new file mode 100644
index 0000000..c9cef25
--- /dev/null
+++ b/SOURCES/0225-video-readers-png-Sanity-check-some-huffman-codes.patch
@@ -0,0 +1,40 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Tue, 6 Jul 2021 19:19:11 +1000
+Subject: [PATCH] video/readers/png: Sanity check some huffman codes
+
+ASAN picked up two OOB global reads: we weren't checking if some code
+values fit within the cplens or cpdext arrays. Check and throw an error
+if not.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit c3a8ab0cbd24153ec7b1f84a96ddfdd72ef8d117)
+---
+ grub-core/video/readers/png.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c
+index d7ed5aa6cf..7f2ba7849b 100644
+--- a/grub-core/video/readers/png.c
++++ b/grub-core/video/readers/png.c
+@@ -753,6 +753,9 @@ grub_png_read_dynamic_block (struct grub_png_data *data)
+ 	  int len, dist, pos;
+ 
+ 	  n -= 257;
++	  if (((unsigned int) n) >= ARRAY_SIZE (cplens))
++	    return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			       "png: invalid huff code");
+ 	  len = cplens[n];
+ 	  if (cplext[n])
+ 	    len += grub_png_get_bits (data, cplext[n]);
+@@ -760,6 +763,9 @@ grub_png_read_dynamic_block (struct grub_png_data *data)
+ 	    return grub_errno;
+ 
+ 	  n = grub_png_get_huff_code (data, &data->dist_table);
++	  if (((unsigned int) n) >= ARRAY_SIZE (cpdist))
++	    return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			       "png: invalid huff code");
+ 	  dist = cpdist[n];
+ 	  if (cpdext[n])
+ 	    dist += grub_png_get_bits (data, cpdext[n]);
diff --git a/SOURCES/0226-video-readers-jpeg-Abort-sooner-if-a-read-operation-.patch b/SOURCES/0226-video-readers-jpeg-Abort-sooner-if-a-read-operation-.patch
new file mode 100644
index 0000000..5491816
--- /dev/null
+++ b/SOURCES/0226-video-readers-jpeg-Abort-sooner-if-a-read-operation-.patch
@@ -0,0 +1,255 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Mon, 28 Jun 2021 14:16:14 +1000
+Subject: [PATCH] video/readers/jpeg: Abort sooner if a read operation fails
+
+Fuzzing revealed some inputs that were taking a long time, potentially
+forever, because they did not bail quickly upon encountering an I/O error.
+
+Try to catch I/O errors sooner and bail out.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit ab2e5d2e4bff488bbb557ed435a61ae102ef9f0c)
+---
+ grub-core/video/readers/jpeg.c | 86 ++++++++++++++++++++++++++++++++++--------
+ 1 file changed, 70 insertions(+), 16 deletions(-)
+
+diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c
+index e31602f766..10225abd53 100644
+--- a/grub-core/video/readers/jpeg.c
++++ b/grub-core/video/readers/jpeg.c
+@@ -109,9 +109,17 @@ static grub_uint8_t
+ grub_jpeg_get_byte (struct grub_jpeg_data *data)
+ {
+   grub_uint8_t r;
++  grub_ssize_t bytes_read;
+ 
+   r = 0;
+-  grub_file_read (data->file, &r, 1);
++  bytes_read = grub_file_read (data->file, &r, 1);
++
++  if (bytes_read != 1)
++    {
++      grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		  "jpeg: unexpected end of data");
++      return 0;
++    }
+ 
+   return r;
+ }
+@@ -120,9 +128,17 @@ static grub_uint16_t
+ grub_jpeg_get_word (struct grub_jpeg_data *data)
+ {
+   grub_uint16_t r;
++  grub_ssize_t bytes_read;
+ 
+   r = 0;
+-  grub_file_read (data->file, &r, sizeof (grub_uint16_t));
++  bytes_read = grub_file_read (data->file, &r, sizeof (grub_uint16_t));
++
++  if (bytes_read != sizeof (grub_uint16_t))
++    {
++      grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		  "jpeg: unexpected end of data");
++      return 0;
++    }
+ 
+   return grub_be_to_cpu16 (r);
+ }
+@@ -135,6 +151,11 @@ grub_jpeg_get_bit (struct grub_jpeg_data *data)
+   if (data->bit_mask == 0)
+     {
+       data->bit_save = grub_jpeg_get_byte (data);
++      if (grub_errno != GRUB_ERR_NONE) {
++	grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		    "jpeg: file read error");
++	return 0;
++      }
+       if (data->bit_save == JPEG_ESC_CHAR)
+ 	{
+ 	  if (grub_jpeg_get_byte (data) != 0)
+@@ -143,6 +164,11 @@ grub_jpeg_get_bit (struct grub_jpeg_data *data)
+ 			  "jpeg: invalid 0xFF in data stream");
+ 	      return 0;
+ 	    }
++	  if (grub_errno != GRUB_ERR_NONE)
++	    {
++	      grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: file read error");
++	      return 0;
++	    }
+ 	}
+       data->bit_mask = 0x80;
+     }
+@@ -161,7 +187,7 @@ grub_jpeg_get_number (struct grub_jpeg_data *data, int num)
+     return 0;
+ 
+   msb = value = grub_jpeg_get_bit (data);
+-  for (i = 1; i < num; i++)
++  for (i = 1; i < num && grub_errno == GRUB_ERR_NONE; i++)
+     value = (value << 1) + (grub_jpeg_get_bit (data) != 0);
+   if (!msb)
+     value += 1 - (1 << num);
+@@ -202,6 +228,8 @@ grub_jpeg_decode_huff_table (struct grub_jpeg_data *data)
+   while (data->file->offset + sizeof (count) + 1 <= next_marker)
+     {
+       id = grub_jpeg_get_byte (data);
++      if (grub_errno != GRUB_ERR_NONE)
++	return grub_errno;
+       ac = (id >> 4) & 1;
+       id &= 0xF;
+       if (id > 1)
+@@ -252,6 +280,8 @@ grub_jpeg_decode_quan_table (struct grub_jpeg_data *data)
+ 
+   next_marker = data->file->offset;
+   next_marker += grub_jpeg_get_word (data);
++  if (grub_errno != GRUB_ERR_NONE)
++    return grub_errno;
+ 
+   if (next_marker > data->file->size)
+     {
+@@ -263,6 +293,8 @@ grub_jpeg_decode_quan_table (struct grub_jpeg_data *data)
+ 	 <= next_marker)
+     {
+       id = grub_jpeg_get_byte (data);
++      if (grub_errno != GRUB_ERR_NONE)
++        return grub_errno;
+       if (id >= 0x10)		/* Upper 4-bit is precision.  */
+ 	return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ 			   "jpeg: only 8-bit precision is supported");
+@@ -294,6 +326,9 @@ grub_jpeg_decode_sof (struct grub_jpeg_data *data)
+   next_marker = data->file->offset;
+   next_marker += grub_jpeg_get_word (data);
+ 
++  if (grub_errno != GRUB_ERR_NONE)
++    return grub_errno;
++
+   if (grub_jpeg_get_byte (data) != 8)
+     return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ 		       "jpeg: only 8-bit precision is supported");
+@@ -319,6 +354,8 @@ grub_jpeg_decode_sof (struct grub_jpeg_data *data)
+ 	return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid index");
+ 
+       ss = grub_jpeg_get_byte (data);	/* Sampling factor.  */
++      if (grub_errno != GRUB_ERR_NONE)
++	return grub_errno;
+       if (!id)
+ 	{
+ 	  grub_uint8_t vs, hs;
+@@ -498,7 +535,7 @@ grub_jpeg_idct_transform (jpeg_data_unit_t du)
+     }
+ }
+ 
+-static void
++static grub_err_t
+ grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du)
+ {
+   int h1, h2, qt;
+@@ -513,6 +550,9 @@ grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du)
+   data->dc_value[id] +=
+     grub_jpeg_get_number (data, grub_jpeg_get_huff_code (data, h1));
+ 
++  if (grub_errno != GRUB_ERR_NONE)
++    return grub_errno;
++
+   du[0] = data->dc_value[id] * (int) data->quan_table[qt][0];
+   pos = 1;
+   while (pos < ARRAY_SIZE (data->quan_table[qt]))
+@@ -527,11 +567,13 @@ grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du)
+       num >>= 4;
+       pos += num;
+ 
++      if (grub_errno != GRUB_ERR_NONE)
++        return grub_errno;
++
+       if (pos >= ARRAY_SIZE (jpeg_zigzag_order))
+ 	{
+-	  grub_error (GRUB_ERR_BAD_FILE_TYPE,
+-		      "jpeg: invalid position in zigzag order!?");
+-	  return;
++	  return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			     "jpeg: invalid position in zigzag order!?");
+ 	}
+ 
+       du[jpeg_zigzag_order[pos]] = val * (int) data->quan_table[qt][pos];
+@@ -539,6 +581,7 @@ grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du)
+     }
+ 
+   grub_jpeg_idct_transform (du);
++  return GRUB_ERR_NONE;
+ }
+ 
+ static void
+@@ -597,7 +640,8 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data)
+   data_offset += grub_jpeg_get_word (data);
+ 
+   cc = grub_jpeg_get_byte (data);
+-
++  if (grub_errno != GRUB_ERR_NONE)
++    return grub_errno;
+   if (cc != 3 && cc != 1)
+     return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ 		       "jpeg: component count must be 1 or 3");
+@@ -610,7 +654,8 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data)
+       id = grub_jpeg_get_byte (data) - 1;
+       if ((id < 0) || (id >= 3))
+ 	return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid index");
+-
++      if (grub_errno != GRUB_ERR_NONE)
++	return grub_errno;
+       ht = grub_jpeg_get_byte (data);
+       data->comp_index[id][1] = (ht >> 4);
+       data->comp_index[id][2] = (ht & 0xF) + 2;
+@@ -618,11 +663,14 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data)
+       if ((data->comp_index[id][1] < 0) || (data->comp_index[id][1] > 3) ||
+ 	  (data->comp_index[id][2] < 0) || (data->comp_index[id][2] > 3))
+ 	return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid hufftable index");
++      if (grub_errno != GRUB_ERR_NONE)
++	return grub_errno;
+     }
+ 
+   grub_jpeg_get_byte (data);	/* Skip 3 unused bytes.  */
+   grub_jpeg_get_word (data);
+-
++  if (grub_errno != GRUB_ERR_NONE)
++    return grub_errno;
+   if (data->file->offset != data_offset)
+     return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: extra byte in sos");
+ 
+@@ -640,6 +688,7 @@ grub_jpeg_decode_data (struct grub_jpeg_data *data)
+ {
+   unsigned c1, vb, hb, nr1, nc1;
+   int rst = data->dri;
++  grub_err_t err = GRUB_ERR_NONE;
+ 
+   vb = 8 << data->log_vs;
+   hb = 8 << data->log_hs;
+@@ -660,17 +709,22 @@ grub_jpeg_decode_data (struct grub_jpeg_data *data)
+ 
+ 	for (r2 = 0; r2 < (1U << data->log_vs); r2++)
+ 	  for (c2 = 0; c2 < (1U << data->log_hs); c2++)
+-	    grub_jpeg_decode_du (data, 0, data->ydu[r2 * 2 + c2]);
++            {
++              err = grub_jpeg_decode_du (data, 0, data->ydu[r2 * 2 + c2]);
++              if (err != GRUB_ERR_NONE)
++                return err;
++            }
+ 
+ 	if (data->color_components >= 3)
+ 	  {
+-	    grub_jpeg_decode_du (data, 1, data->cbdu);
+-	    grub_jpeg_decode_du (data, 2, data->crdu);
++	    err = grub_jpeg_decode_du (data, 1, data->cbdu);
++	    if (err != GRUB_ERR_NONE)
++	      return err;
++	    err = grub_jpeg_decode_du (data, 2, data->crdu);
++	    if (err != GRUB_ERR_NONE)
++	      return err;
+ 	  }
+ 
+-	if (grub_errno)
+-	  return grub_errno;
+-
+ 	nr2 = (data->r1 == nr1 - 1) ? (data->image_height - data->r1 * vb) : vb;
+ 	nc2 = (c1 == nc1 - 1) ? (data->image_width - c1 * hb) : hb;
+ 
diff --git a/SOURCES/0227-video-readers-jpeg-Do-not-reallocate-a-given-huff-ta.patch b/SOURCES/0227-video-readers-jpeg-Do-not-reallocate-a-given-huff-ta.patch
new file mode 100644
index 0000000..199ec32
--- /dev/null
+++ b/SOURCES/0227-video-readers-jpeg-Do-not-reallocate-a-given-huff-ta.patch
@@ -0,0 +1,29 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Mon, 28 Jun 2021 14:16:58 +1000
+Subject: [PATCH] video/readers/jpeg: Do not reallocate a given huff table
+
+Fix a memory leak where an invalid file could cause us to reallocate
+memory for a huffman table we had already allocated memory for.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit bc06e12b4de55cc6f926af9f064170c82b1403e9)
+---
+ grub-core/video/readers/jpeg.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c
+index 10225abd53..caa211f06d 100644
+--- a/grub-core/video/readers/jpeg.c
++++ b/grub-core/video/readers/jpeg.c
+@@ -245,6 +245,9 @@ grub_jpeg_decode_huff_table (struct grub_jpeg_data *data)
+ 	n += count[i];
+ 
+       id += ac * 2;
++      if (data->huff_value[id] != NULL)
++	return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++			   "jpeg: attempt to reallocate huffman table");
+       data->huff_value[id] = grub_malloc (n);
+       if (grub_errno)
+ 	return grub_errno;
diff --git a/SOURCES/0228-video-readers-jpeg-Refuse-to-handle-multiple-start-o.patch b/SOURCES/0228-video-readers-jpeg-Refuse-to-handle-multiple-start-o.patch
new file mode 100644
index 0000000..179238b
--- /dev/null
+++ b/SOURCES/0228-video-readers-jpeg-Refuse-to-handle-multiple-start-o.patch
@@ -0,0 +1,44 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Mon, 28 Jun 2021 14:25:17 +1000
+Subject: [PATCH] video/readers/jpeg: Refuse to handle multiple start of
+ streams
+
+An invalid file could contain multiple start of stream blocks, which
+would cause us to reallocate and leak our bitmap. Refuse to handle
+multiple start of streams.
+
+Additionally, fix a grub_error() call formatting.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit f3a854def3e281b7ad4bbea730cd3046de1da52f)
+---
+ grub-core/video/readers/jpeg.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c
+index caa211f06d..1df1171d78 100644
+--- a/grub-core/video/readers/jpeg.c
++++ b/grub-core/video/readers/jpeg.c
+@@ -677,6 +677,9 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data)
+   if (data->file->offset != data_offset)
+     return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: extra byte in sos");
+ 
++  if (*data->bitmap)
++    return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: too many start of scan blocks");
++
+   if (grub_video_bitmap_create (data->bitmap, data->image_width,
+ 				data->image_height,
+ 				GRUB_VIDEO_BLIT_FORMAT_RGB_888))
+@@ -699,8 +702,8 @@ grub_jpeg_decode_data (struct grub_jpeg_data *data)
+   nc1 = (data->image_width + hb - 1)  >> (3 + data->log_hs);
+ 
+   if (data->bitmap_ptr == NULL)
+-    return grub_error(GRUB_ERR_BAD_FILE_TYPE,
+-		      "jpeg: attempted to decode data before start of stream");
++    return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		       "jpeg: attempted to decode data before start of stream");
+ 
+   for (; data->r1 < nr1 && (!data->dri || rst);
+        data->r1++, data->bitmap_ptr += (vb * data->image_width - hb * nc1) * 3)
diff --git a/SOURCES/0229-video-readers-jpeg-Block-int-underflow-wild-pointer-.patch b/SOURCES/0229-video-readers-jpeg-Block-int-underflow-wild-pointer-.patch
new file mode 100644
index 0000000..99eeb62
--- /dev/null
+++ b/SOURCES/0229-video-readers-jpeg-Block-int-underflow-wild-pointer-.patch
@@ -0,0 +1,53 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Wed, 7 Jul 2021 15:38:19 +1000
+Subject: [PATCH] video/readers/jpeg: Block int underflow -> wild pointer write
+
+Certain 1 px wide images caused a wild pointer write in
+grub_jpeg_ycrcb_to_rgb(). This was caused because in grub_jpeg_decode_data(),
+we have the following loop:
+
+for (; data->r1 < nr1 && (!data->dri || rst);
+     data->r1++, data->bitmap_ptr += (vb * data->image_width - hb * nc1) * 3)
+
+We did not check if vb * width >= hb * nc1.
+
+On a 64-bit platform, if that turns out to be negative, it will underflow,
+be interpreted as unsigned 64-bit, then be added to the 64-bit pointer, so
+we see data->bitmap_ptr jump, e.g.:
+
+0x6180_0000_0480 to
+0x6181_0000_0498
+     ^
+     ~--- carry has occurred and this pointer is now far away from
+          any object.
+
+On a 32-bit platform, it will decrement the pointer, creating a pointer
+that won't crash but will overwrite random data.
+
+Catch the underflow and error out.
+
+Fixes: CVE-2021-3697
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 41aeb2004db9924fecd9f2dd64bc2a5a5594a4b5)
+---
+ grub-core/video/readers/jpeg.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c
+index 1df1171d78..2da04094b3 100644
+--- a/grub-core/video/readers/jpeg.c
++++ b/grub-core/video/readers/jpeg.c
+@@ -705,6 +705,10 @@ grub_jpeg_decode_data (struct grub_jpeg_data *data)
+     return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ 		       "jpeg: attempted to decode data before start of stream");
+ 
++  if (vb * data->image_width <= hb * nc1)
++    return grub_error (GRUB_ERR_BAD_FILE_TYPE,
++		       "jpeg: cannot decode image with these dimensions");
++
+   for (; data->r1 < nr1 && (!data->dri || rst);
+        data->r1++, data->bitmap_ptr += (vb * data->image_width - hb * nc1) * 3)
+     for (c1 = 0;  c1 < nc1 && (!data->dri || rst);
diff --git a/SOURCES/0230-normal-charset-Fix-array-out-of-bounds-formatting-un.patch b/SOURCES/0230-normal-charset-Fix-array-out-of-bounds-formatting-un.patch
new file mode 100644
index 0000000..6e64ed8
--- /dev/null
+++ b/SOURCES/0230-normal-charset-Fix-array-out-of-bounds-formatting-un.patch
@@ -0,0 +1,34 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Tue, 13 Jul 2021 13:24:38 +1000
+Subject: [PATCH] normal/charset: Fix array out-of-bounds formatting unicode
+ for display
+
+In some cases attempting to display arbitrary binary strings leads
+to ASAN splats reading the widthspec array out of bounds.
+
+Check the index. If it would be out of bounds, return a width of 1.
+I don't know if that's strictly correct, but we're not really expecting
+great display of arbitrary binary data, and it's certainly not worse than
+an OOB read.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit fdf32abc7a3928852422c0f291d8cd1dd6b34a8d)
+---
+ grub-core/normal/charset.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/grub-core/normal/charset.c b/grub-core/normal/charset.c
+index 4dfcc31078..7a5a7c153c 100644
+--- a/grub-core/normal/charset.c
++++ b/grub-core/normal/charset.c
+@@ -395,6 +395,8 @@ grub_unicode_estimate_width (const struct grub_unicode_glyph *c)
+ {
+   if (grub_unicode_get_comb_type (c->base))
+     return 0;
++  if (((unsigned long) (c->base >> 3)) >= ARRAY_SIZE (widthspec))
++    return 1;
+   if (widthspec[c->base >> 3] & (1 << (c->base & 7)))
+     return 2;
+   else
diff --git a/SOURCES/0231-net-netbuff-Block-overly-large-netbuff-allocs.patch b/SOURCES/0231-net-netbuff-Block-overly-large-netbuff-allocs.patch
new file mode 100644
index 0000000..2e10d49
--- /dev/null
+++ b/SOURCES/0231-net-netbuff-Block-overly-large-netbuff-allocs.patch
@@ -0,0 +1,46 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Tue, 8 Mar 2022 23:47:46 +1100
+Subject: [PATCH] net/netbuff: Block overly large netbuff allocs
+
+A netbuff shouldn't be too huge. It's bounded by MTU and TCP segment
+reassembly.
+
+This helps avoid some bugs (and provides a spot to instrument to catch
+them at their source).
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit ee9591103004cd13b4efadda671536090ca7fd57)
+---
+ grub-core/net/netbuff.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/grub-core/net/netbuff.c b/grub-core/net/netbuff.c
+index dbeeefe478..d5e9e9a0d7 100644
+--- a/grub-core/net/netbuff.c
++++ b/grub-core/net/netbuff.c
+@@ -79,10 +79,23 @@ grub_netbuff_alloc (grub_size_t len)
+ 
+   COMPILE_TIME_ASSERT (NETBUFF_ALIGN % sizeof (grub_properly_aligned_t) == 0);
+ 
++  /*
++   * The largest size of a TCP packet is 64 KiB, and everything else
++   * should be a lot smaller - most MTUs are 1500 or less. Cap data
++   * size at 64 KiB + a buffer.
++   */
++  if (len > 0xffffUL + 0x1000UL)
++    {
++      grub_error (GRUB_ERR_BUG,
++                  "attempted to allocate a packet that is too big");
++      return NULL;
++    }
++
+   if (len < NETBUFFMINLEN)
+     len = NETBUFFMINLEN;
+ 
+   len = ALIGN_UP (len, NETBUFF_ALIGN);
++
+ #ifdef GRUB_MACHINE_EMU
+   data = grub_malloc (len + sizeof (*nb));
+ #else
diff --git a/SOURCES/0232-net-ip-Do-IP-fragment-maths-safely.patch b/SOURCES/0232-net-ip-Do-IP-fragment-maths-safely.patch
new file mode 100644
index 0000000..118448d
--- /dev/null
+++ b/SOURCES/0232-net-ip-Do-IP-fragment-maths-safely.patch
@@ -0,0 +1,44 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Mon, 20 Dec 2021 19:41:21 +1100
+Subject: [PATCH] net/ip: Do IP fragment maths safely
+
+This avoids an underflow and subsequent unpleasantness.
+
+Fixes: CVE-2022-28733
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit eb74e5743ca7e18a5e75c392fe0b21d1549a1936)
+---
+ grub-core/net/ip.c | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c
+index ce6bdc75c6..cf74f1f794 100644
+--- a/grub-core/net/ip.c
++++ b/grub-core/net/ip.c
+@@ -25,6 +25,7 @@
+ #include <grub/net/netbuff.h>
+ #include <grub/mm.h>
+ #include <grub/priority_queue.h>
++#include <grub/safemath.h>
+ #include <grub/time.h>
+ 
+ struct iphdr {
+@@ -551,7 +552,14 @@ grub_net_recv_ip4_packets (struct grub_net_buff *nb,
+     {
+       rsm->total_len = (8 * (grub_be_to_cpu16 (iph->frags) & OFFSET_MASK)
+ 			+ (nb->tail - nb->data));
+-      rsm->total_len -= ((iph->verhdrlen & 0xf) * sizeof (grub_uint32_t));
++
++      if (grub_sub (rsm->total_len, (iph->verhdrlen & 0xf) * sizeof (grub_uint32_t),
++		    &rsm->total_len))
++	{
++	  grub_dprintf ("net", "IP reassembly size underflow\n");
++	  return GRUB_ERR_NONE;
++	}
++
+       rsm->asm_netbuff = grub_netbuff_alloc (rsm->total_len);
+       if (!rsm->asm_netbuff)
+ 	{
diff --git a/SOURCES/0233-net-dns-Fix-double-free-addresses-on-corrupt-DNS-res.patch b/SOURCES/0233-net-dns-Fix-double-free-addresses-on-corrupt-DNS-res.patch
new file mode 100644
index 0000000..19701b6
--- /dev/null
+++ b/SOURCES/0233-net-dns-Fix-double-free-addresses-on-corrupt-DNS-res.patch
@@ -0,0 +1,56 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Thu, 16 Sep 2021 01:29:54 +1000
+Subject: [PATCH] net/dns: Fix double-free addresses on corrupt DNS response
+
+grub_net_dns_lookup() takes as inputs a pointer to an array of addresses
+("addresses") for the given name, and pointer to a number of addresses
+("naddresses"). grub_net_dns_lookup() is responsible for allocating
+"addresses", and the caller is responsible for freeing it if
+"naddresses" > 0.
+
+The DNS recv_hook will sometimes set and free the addresses array,
+for example if the packet is too short:
+
+      if (ptr + 10 >= nb->tail)
+	{
+	  if (!*data->naddresses)
+	    grub_free (*data->addresses);
+	  grub_netbuff_free (nb);
+	  return GRUB_ERR_NONE;
+	}
+
+Later on the nslookup command code unconditionally frees the "addresses"
+array. Normally this is fine: the array is either populated with valid
+data or is NULL. But in these sorts of error cases it is neither NULL
+nor valid and we get a double-free.
+
+Only free "addresses" if "naddresses" > 0.
+
+It looks like the other use of grub_net_dns_lookup() is not affected.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit eb2e69fcf51307757e43f55ee8c9354d1ee42dd1)
+---
+ grub-core/net/dns.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c
+index 906ec7d678..135faac035 100644
+--- a/grub-core/net/dns.c
++++ b/grub-core/net/dns.c
+@@ -667,9 +667,11 @@ grub_cmd_nslookup (struct grub_command *cmd __attribute__ ((unused)),
+       grub_net_addr_to_str (&addresses[i], buf);
+       grub_printf ("%s\n", buf);
+     }
+-  grub_free (addresses);
+   if (naddresses)
+-    return GRUB_ERR_NONE;
++    {
++      grub_free (addresses);
++      return GRUB_ERR_NONE;
++    }
+   return grub_error (GRUB_ERR_NET_NO_DOMAIN, N_("no DNS record found"));
+ }
+ 
diff --git a/SOURCES/0234-net-dns-Don-t-read-past-the-end-of-the-string-we-re-.patch b/SOURCES/0234-net-dns-Don-t-read-past-the-end-of-the-string-we-re-.patch
new file mode 100644
index 0000000..ab0d471
--- /dev/null
+++ b/SOURCES/0234-net-dns-Don-t-read-past-the-end-of-the-string-we-re-.patch
@@ -0,0 +1,71 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Mon, 20 Dec 2021 21:55:43 +1100
+Subject: [PATCH] net/dns: Don't read past the end of the string we're checking
+ against
+
+I don't really understand what's going on here but fuzzing found
+a bug where we read past the end of check_with. That's a C string,
+so use grub_strlen() to make sure we don't overread it.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 6a97b3f4b1d5173aa516edc6dedbc63de7306d21)
+---
+ grub-core/net/dns.c | 19 ++++++++++++++++---
+ 1 file changed, 16 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c
+index 135faac035..17961a9f18 100644
+--- a/grub-core/net/dns.c
++++ b/grub-core/net/dns.c
+@@ -146,11 +146,18 @@ check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head,
+ 		 int *length, char *set)
+ {
+   const char *readable_ptr = check_with;
++  int readable_len;
+   const grub_uint8_t *ptr;
+   char *optr = set;
+   int bytes_processed = 0;
+   if (length)
+     *length = 0;
++
++  if (readable_ptr != NULL)
++    readable_len = grub_strlen (readable_ptr);
++  else
++    readable_len = 0;
++
+   for (ptr = name_at; ptr < tail && bytes_processed < tail - head + 2; )
+     {
+       /* End marker.  */
+@@ -172,13 +179,16 @@ check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head,
+ 	  ptr = head + (((ptr[0] & 0x3f) << 8) | ptr[1]);
+ 	  continue;
+ 	}
+-      if (readable_ptr && grub_memcmp (ptr + 1, readable_ptr, *ptr) != 0)
++      if (readable_ptr != NULL && (*ptr > readable_len || grub_memcmp (ptr + 1, readable_ptr, *ptr) != 0))
+ 	return 0;
+       if (grub_memchr (ptr + 1, 0, *ptr) 
+ 	  || grub_memchr (ptr + 1, '.', *ptr))
+ 	return 0;
+       if (readable_ptr)
+-	readable_ptr += *ptr;
++	{
++	  readable_ptr += *ptr;
++	  readable_len -= *ptr;
++	}
+       if (readable_ptr && *readable_ptr != '.' && *readable_ptr != 0)
+ 	return 0;
+       bytes_processed += *ptr + 1;
+@@ -192,7 +202,10 @@ check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head,
+       if (optr)
+ 	*optr++ = '.';
+       if (readable_ptr && *readable_ptr)
+-	readable_ptr++;
++	{
++	  readable_ptr++;
++	  readable_len--;
++	}
+       ptr += *ptr + 1;
+     }
+   return 0;
diff --git a/SOURCES/0235-net-tftp-Prevent-a-UAF-and-double-free-from-a-failed.patch b/SOURCES/0235-net-tftp-Prevent-a-UAF-and-double-free-from-a-failed.patch
new file mode 100644
index 0000000..3ff7b6b
--- /dev/null
+++ b/SOURCES/0235-net-tftp-Prevent-a-UAF-and-double-free-from-a-failed.patch
@@ -0,0 +1,112 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Mon, 20 Sep 2021 01:12:24 +1000
+Subject: [PATCH] net/tftp: Prevent a UAF and double-free from a failed seek
+
+A malicious tftp server can cause UAFs and a double free.
+
+An attempt to read from a network file is handled by grub_net_fs_read(). If
+the read is at an offset other than the current offset, grub_net_seek_real()
+is invoked.
+
+In grub_net_seek_real(), if a backwards seek cannot be satisfied from the
+currently received packets, and the underlying transport does not provide
+a seek method, then grub_net_seek_real() will close and reopen the network
+protocol layer.
+
+For tftp, the ->close() call goes to tftp_close() and frees the tftp_data_t
+file->data. The file->data pointer is not nulled out after the free.
+
+If the ->open() call fails, the file->data will not be reallocated and will
+continue point to a freed memory block. This could happen from a server
+refusing to send the requisite ack to the new tftp request, for example.
+
+The seek and the read will then fail, but the grub_file continues to exist:
+the failed seek does not necessarily cause the entire file to be thrown
+away (e.g. where the file is checked to see if it is gzipped/lzio/xz/etc.,
+a read failure is interpreted as a decompressor passing on the file, not as
+an invalidation of the entire grub_file_t structure).
+
+This means subsequent attempts to read or seek the file will use the old
+file->data after free. Eventually, the file will be close()d again and
+file->data will be freed again.
+
+Mark a net_fs file that doesn't reopen as broken. Do not permit read() or
+close() on a broken file (seek is not exposed directly to the file API -
+it is only called as part of read, so this blocks seeks as well).
+
+As an additional defence, null out the ->data pointer if tftp_open() fails.
+That would have lead to a simple null pointer dereference rather than
+a mess of UAFs.
+
+This may affect other protocols, I haven't checked.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit dada1dda695439bb55b2848dddc2d89843552f81)
+---
+ grub-core/net/net.c  | 11 +++++++++--
+ grub-core/net/tftp.c |  1 +
+ include/grub/net.h   |  1 +
+ 3 files changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/net/net.c b/grub-core/net/net.c
+index 55aed92722..1001c611d1 100644
+--- a/grub-core/net/net.c
++++ b/grub-core/net/net.c
+@@ -1625,7 +1625,8 @@ grub_net_fs_close (grub_file_t file)
+       grub_netbuff_free (file->device->net->packs.first->nb);
+       grub_net_remove_packet (file->device->net->packs.first);
+     }
+-  file->device->net->protocol->close (file);
++  if (!file->device->net->broken)
++    file->device->net->protocol->close (file);
+   grub_free (file->device->net->name);
+   return GRUB_ERR_NONE;
+ }
+@@ -1847,7 +1848,10 @@ grub_net_seek_real (struct grub_file *file, grub_off_t offset)
+     file->device->net->stall = 0;
+     err = file->device->net->protocol->open (file, file->device->net->name);
+     if (err)
+-      return err;
++      {
++	file->device->net->broken = 1;
++	return err;
++      }
+     grub_net_fs_read_real (file, NULL, offset);
+     return grub_errno;
+   }
+@@ -1856,6 +1860,9 @@ grub_net_seek_real (struct grub_file *file, grub_off_t offset)
+ static grub_ssize_t
+ grub_net_fs_read (grub_file_t file, char *buf, grub_size_t len)
+ {
++  if (file->device->net->broken)
++    return -1;
++
+   if (file->offset != file->device->net->offset)
+     {
+       grub_err_t err;
+diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c
+index d54b13f09f..788ad1dc44 100644
+--- a/grub-core/net/tftp.c
++++ b/grub-core/net/tftp.c
+@@ -408,6 +408,7 @@ tftp_open (struct grub_file *file, const char *filename)
+     {
+       grub_net_udp_close (data->sock);
+       grub_free (data);
++      file->data = NULL;
+       return grub_errno;
+     }
+ 
+diff --git a/include/grub/net.h b/include/grub/net.h
+index 42af7de250..9e4898cc6b 100644
+--- a/include/grub/net.h
++++ b/include/grub/net.h
+@@ -280,6 +280,7 @@ typedef struct grub_net
+   grub_fs_t fs;
+   int eof;
+   int stall;
++  int broken;
+ } *grub_net_t;
+ 
+ extern grub_net_t (*EXPORT_VAR (grub_net_open)) (const char *name);
diff --git a/SOURCES/0236-net-tftp-Avoid-a-trivial-UAF.patch b/SOURCES/0236-net-tftp-Avoid-a-trivial-UAF.patch
new file mode 100644
index 0000000..4ec3b56
--- /dev/null
+++ b/SOURCES/0236-net-tftp-Avoid-a-trivial-UAF.patch
@@ -0,0 +1,35 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Tue, 18 Jan 2022 14:29:20 +1100
+Subject: [PATCH] net/tftp: Avoid a trivial UAF
+
+Under tftp errors, we print a tftp error message from the tftp header.
+However, the tftph pointer is a pointer inside nb, the netbuff. Previously,
+we were freeing the nb and then dereferencing it. Don't do that, use it
+and then free it later.
+
+This isn't really _bad_ per se, especially as we're single-threaded, but
+it trips up fuzzers.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 956f4329cec23e4375182030ca9b2be631a61ba5)
+---
+ grub-core/net/tftp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c
+index 788ad1dc44..a95766dcbd 100644
+--- a/grub-core/net/tftp.c
++++ b/grub-core/net/tftp.c
+@@ -251,9 +251,9 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)),
+       return GRUB_ERR_NONE;
+     case TFTP_ERROR:
+       data->have_oack = 1;
+-      grub_netbuff_free (nb);
+       grub_error (GRUB_ERR_IO, "%s", tftph->u.err.errmsg);
+       grub_error_save (&data->save_err);
++      grub_netbuff_free (nb);
+       return GRUB_ERR_NONE;
+     default:
+       grub_netbuff_free (nb);
diff --git a/SOURCES/0237-net-http-Do-not-tear-down-socket-if-it-s-already-bee.patch b/SOURCES/0237-net-http-Do-not-tear-down-socket-if-it-s-already-bee.patch
new file mode 100644
index 0000000..186f0c3
--- /dev/null
+++ b/SOURCES/0237-net-http-Do-not-tear-down-socket-if-it-s-already-bee.patch
@@ -0,0 +1,42 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Tue, 1 Mar 2022 23:14:15 +1100
+Subject: [PATCH] net/http: Do not tear down socket if it's already been torn
+ down
+
+It's possible for data->sock to get torn down in tcp error handling.
+If we unconditionally tear it down again we will end up doing writes
+to an offset of the NULL pointer when we go to tear it down again.
+
+Detect if it has been torn down and don't do it again.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit ec233d3ecf995293304de443579aab5c46c49e85)
+---
+ grub-core/net/http.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/net/http.c b/grub-core/net/http.c
+index 7f878b5615..19cb8768e3 100644
+--- a/grub-core/net/http.c
++++ b/grub-core/net/http.c
+@@ -427,7 +427,7 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial)
+       return err;
+     }
+ 
+-  for (i = 0; !data->headers_recv && i < 100; i++)
++  for (i = 0; data->sock && !data->headers_recv && i < 100; i++)
+     {
+       grub_net_tcp_retransmit ();
+       grub_net_poll_cards (300, &data->headers_recv);
+@@ -435,7 +435,8 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial)
+ 
+   if (!data->headers_recv)
+     {
+-      grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
++      if (data->sock)
++        grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
+       if (data->err)
+ 	{
+ 	  char *str = data->errmsg;
diff --git a/SOURCES/0238-net-http-Fix-OOB-write-for-split-http-headers.patch b/SOURCES/0238-net-http-Fix-OOB-write-for-split-http-headers.patch
new file mode 100644
index 0000000..f22960b
--- /dev/null
+++ b/SOURCES/0238-net-http-Fix-OOB-write-for-split-http-headers.patch
@@ -0,0 +1,46 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Tue, 8 Mar 2022 18:17:03 +1100
+Subject: [PATCH] net/http: Fix OOB write for split http headers
+
+GRUB has special code for handling an http header that is split
+across two packets.
+
+The code tracks the end of line by looking for a "\n" byte. The
+code for split headers has always advanced the pointer just past the
+end of the line, whereas the code that handles unsplit headers does
+not advance the pointer. This extra advance causes the length to be
+one greater, which breaks an assumption in parse_line(), leading to
+it writing a NUL byte one byte past the end of the buffer where we
+reconstruct the line from the two packets.
+
+It's conceivable that an attacker controlled set of packets could
+cause this to zero out the first byte of the "next" pointer of the
+grub_mm_region structure following the current_line buffer.
+
+Do not advance the pointer in the split header case.
+
+Fixes: CVE-2022-28734
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit e9fb459638811c12b0989dbf64e3e124974ef617)
+---
+ grub-core/net/http.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/grub-core/net/http.c b/grub-core/net/http.c
+index 19cb8768e3..58546739a2 100644
+--- a/grub-core/net/http.c
++++ b/grub-core/net/http.c
+@@ -193,9 +193,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)),
+ 	  int have_line = 1;
+ 	  char *t;
+ 	  ptr = grub_memchr (nb->data, '\n', nb->tail - nb->data);
+-	  if (ptr)
+-	    ptr++;
+-	  else
++	  if (ptr == NULL)
+ 	    {
+ 	      have_line = 0;
+ 	      ptr = (char *) nb->tail;
diff --git a/SOURCES/0239-net-http-Error-out-on-headers-with-LF-without-CR.patch b/SOURCES/0239-net-http-Error-out-on-headers-with-LF-without-CR.patch
new file mode 100644
index 0000000..b73c169
--- /dev/null
+++ b/SOURCES/0239-net-http-Error-out-on-headers-with-LF-without-CR.patch
@@ -0,0 +1,48 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Tue, 8 Mar 2022 19:04:40 +1100
+Subject: [PATCH] net/http: Error out on headers with LF without CR
+
+In a similar vein to the previous patch, parse_line() would write
+a NUL byte past the end of the buffer if there was an HTTP header
+with a LF rather than a CRLF.
+
+RFC-2616 says:
+
+  Many HTTP/1.1 header field values consist of words separated by LWS
+  or special characters. These special characters MUST be in a quoted
+  string to be used within a parameter value (as defined in section 3.6).
+
+We don't support quoted sections or continuation lines, etc.
+
+If we see an LF that's not part of a CRLF, bail out.
+
+Fixes: CVE-2022-28734
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit d232ad41ac4979a9de4d746e5fdff9caf0e303de)
+---
+ grub-core/net/http.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/grub-core/net/http.c b/grub-core/net/http.c
+index 58546739a2..57d2721719 100644
+--- a/grub-core/net/http.c
++++ b/grub-core/net/http.c
+@@ -69,7 +69,15 @@ parse_line (grub_file_t file, http_data_t data, char *ptr, grub_size_t len)
+   char *end = ptr + len;
+   while (end > ptr && *(end - 1) == '\r')
+     end--;
++
++  /* LF without CR. */
++  if (end == ptr + len)
++    {
++      data->errmsg = grub_strdup (_("invalid HTTP header - LF without CR"));
++      return GRUB_ERR_NONE;
++    }
+   *end = 0;
++
+   /* Trailing CRLF.  */
+   if (data->in_chunk_len == 1)
+     {
diff --git a/SOURCES/0240-fs-f2fs-Do-not-read-past-the-end-of-nat-journal-entr.patch b/SOURCES/0240-fs-f2fs-Do-not-read-past-the-end-of-nat-journal-entr.patch
new file mode 100644
index 0000000..79df1c2
--- /dev/null
+++ b/SOURCES/0240-fs-f2fs-Do-not-read-past-the-end-of-nat-journal-entr.patch
@@ -0,0 +1,72 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
+Date: Wed, 6 Apr 2022 18:03:37 +0530
+Subject: [PATCH] fs/f2fs: Do not read past the end of nat journal entries
+
+A corrupt f2fs file system could specify a nat journal entry count
+that is beyond the maximum NAT_JOURNAL_ENTRIES.
+
+Check if the specified nat journal entry count before accessing the
+array, and throw an error if it is too large.
+
+Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit a3988cb3f0a108dd67ac127a79a4c8479d23334e)
+---
+ grub-core/fs/f2fs.c | 21 ++++++++++++++-------
+ 1 file changed, 14 insertions(+), 7 deletions(-)
+
+diff --git a/grub-core/fs/f2fs.c b/grub-core/fs/f2fs.c
+index 8a9992ca9e..63702214b0 100644
+--- a/grub-core/fs/f2fs.c
++++ b/grub-core/fs/f2fs.c
+@@ -632,23 +632,27 @@ get_nat_journal (struct grub_f2fs_data *data)
+   return err;
+ }
+ 
+-static grub_uint32_t
+-get_blkaddr_from_nat_journal (struct grub_f2fs_data *data, grub_uint32_t nid)
++static grub_err_t
++get_blkaddr_from_nat_journal (struct grub_f2fs_data *data, grub_uint32_t nid,
++                              grub_uint32_t *blkaddr)
+ {
+   grub_uint16_t n = grub_le_to_cpu16 (data->nat_j.n_nats);
+-  grub_uint32_t blkaddr = 0;
+   grub_uint16_t i;
+ 
++  if (n >= NAT_JOURNAL_ENTRIES)
++    return grub_error (GRUB_ERR_BAD_FS,
++                       "invalid number of nat journal entries");
++
+   for (i = 0; i < n; i++)
+     {
+       if (grub_le_to_cpu32 (data->nat_j.entries[i].nid) == nid)
+         {
+-          blkaddr = grub_le_to_cpu32 (data->nat_j.entries[i].ne.block_addr);
++          *blkaddr = grub_le_to_cpu32 (data->nat_j.entries[i].ne.block_addr);
+           break;
+         }
+     }
+ 
+-  return blkaddr;
++  return GRUB_ERR_NONE;
+ }
+ 
+ static grub_uint32_t
+@@ -656,10 +660,13 @@ get_node_blkaddr (struct grub_f2fs_data *data, grub_uint32_t nid)
+ {
+   struct grub_f2fs_nat_block *nat_block;
+   grub_uint32_t seg_off, block_off, entry_off, block_addr;
+-  grub_uint32_t blkaddr;
++  grub_uint32_t blkaddr = 0;
+   grub_err_t err;
+ 
+-  blkaddr = get_blkaddr_from_nat_journal (data, nid);
++  err = get_blkaddr_from_nat_journal (data, nid, &blkaddr);
++  if (err != GRUB_ERR_NONE)
++    return 0;
++
+   if (blkaddr)
+     return blkaddr;
+ 
diff --git a/SOURCES/0241-fs-f2fs-Do-not-read-past-the-end-of-nat-bitmap.patch b/SOURCES/0241-fs-f2fs-Do-not-read-past-the-end-of-nat-bitmap.patch
new file mode 100644
index 0000000..855e882
--- /dev/null
+++ b/SOURCES/0241-fs-f2fs-Do-not-read-past-the-end-of-nat-bitmap.patch
@@ -0,0 +1,132 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
+Date: Wed, 6 Apr 2022 18:49:09 +0530
+Subject: [PATCH] fs/f2fs: Do not read past the end of nat bitmap
+
+A corrupt f2fs filesystem could have a block offset or a bitmap
+offset that would cause us to read beyond the bounds of the nat
+bitmap.
+
+Introduce the nat_bitmap_size member in grub_f2fs_data which holds
+the size of nat bitmap.
+
+Set the size when loading the nat bitmap in nat_bitmap_ptr(), and
+catch when an invalid offset would create a pointer past the end of
+the allocated space.
+
+Check against the bitmap size in grub_f2fs_test_bit() test bit to avoid
+reading past the end of the nat bitmap.
+
+Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 62d63d5e38c67a6e349148bf7cb87c560e935a7e)
+---
+ grub-core/fs/f2fs.c | 33 +++++++++++++++++++++++++++------
+ 1 file changed, 27 insertions(+), 6 deletions(-)
+
+diff --git a/grub-core/fs/f2fs.c b/grub-core/fs/f2fs.c
+index 63702214b0..8898b235e0 100644
+--- a/grub-core/fs/f2fs.c
++++ b/grub-core/fs/f2fs.c
+@@ -122,6 +122,7 @@ GRUB_MOD_LICENSE ("GPLv3+");
+ #define F2FS_INLINE_DOTS          0x10  /* File having implicit dot dentries. */
+ 
+ #define MAX_VOLUME_NAME           512
++#define MAX_NAT_BITMAP_SIZE       3900
+ 
+ enum FILE_TYPE
+ {
+@@ -183,7 +184,7 @@ struct grub_f2fs_checkpoint
+   grub_uint32_t                   checksum_offset;
+   grub_uint64_t                   elapsed_time;
+   grub_uint8_t                    alloc_type[MAX_ACTIVE_LOGS];
+-  grub_uint8_t                    sit_nat_version_bitmap[3900];
++  grub_uint8_t                    sit_nat_version_bitmap[MAX_NAT_BITMAP_SIZE];
+   grub_uint32_t                   checksum;
+ } GRUB_PACKED;
+ 
+@@ -302,6 +303,7 @@ struct grub_f2fs_data
+ 
+   struct grub_f2fs_nat_journal    nat_j;
+   char                            *nat_bitmap;
++  grub_uint32_t                   nat_bitmap_size;
+ 
+   grub_disk_t                     disk;
+   struct grub_f2fs_node           *inode;
+@@ -377,15 +379,20 @@ sum_blk_addr (struct grub_f2fs_data *data, int base, int type)
+ }
+ 
+ static void *
+-nat_bitmap_ptr (struct grub_f2fs_data *data)
++nat_bitmap_ptr (struct grub_f2fs_data *data, grub_uint32_t *nat_bitmap_size)
+ {
+   struct grub_f2fs_checkpoint *ckpt = &data->ckpt;
+   grub_uint32_t offset;
++  *nat_bitmap_size = MAX_NAT_BITMAP_SIZE;
+ 
+   if (grub_le_to_cpu32 (data->sblock.cp_payload) > 0)
+     return ckpt->sit_nat_version_bitmap;
+ 
+   offset = grub_le_to_cpu32 (ckpt->sit_ver_bitmap_bytesize);
++  if (offset >= MAX_NAT_BITMAP_SIZE)
++     return NULL;
++
++  *nat_bitmap_size = *nat_bitmap_size - offset;
+ 
+   return ckpt->sit_nat_version_bitmap + offset;
+ }
+@@ -438,11 +445,15 @@ grub_f2fs_crc_valid (grub_uint32_t blk_crc, void *buf, const grub_uint32_t len)
+ }
+ 
+ static int
+-grub_f2fs_test_bit (grub_uint32_t nr, const char *p)
++grub_f2fs_test_bit (grub_uint32_t nr, const char *p, grub_uint32_t len)
+ {
+   int mask;
++  grub_uint32_t shifted_nr = (nr >> 3);
+ 
+-  p += (nr >> 3);
++  if (shifted_nr >= len)
++    return -1;
++
++  p += shifted_nr;
+   mask = 1 << (7 - (nr & 0x07));
+ 
+   return mask & *p;
+@@ -662,6 +673,7 @@ get_node_blkaddr (struct grub_f2fs_data *data, grub_uint32_t nid)
+   grub_uint32_t seg_off, block_off, entry_off, block_addr;
+   grub_uint32_t blkaddr = 0;
+   grub_err_t err;
++  int result_bit;
+ 
+   err = get_blkaddr_from_nat_journal (data, nid, &blkaddr);
+   if (err != GRUB_ERR_NONE)
+@@ -682,8 +694,15 @@ get_node_blkaddr (struct grub_f2fs_data *data, grub_uint32_t nid)
+         ((seg_off * data->blocks_per_seg) << 1) +
+         (block_off & (data->blocks_per_seg - 1));
+ 
+-  if (grub_f2fs_test_bit (block_off, data->nat_bitmap))
++  result_bit = grub_f2fs_test_bit (block_off, data->nat_bitmap,
++                                   data->nat_bitmap_size);
++  if (result_bit > 0)
+     block_addr += data->blocks_per_seg;
++  else if (result_bit == -1)
++    {
++      grub_free (nat_block);
++      return 0;
++    }
+ 
+   err = grub_f2fs_block_read (data, block_addr, nat_block);
+   if (err)
+@@ -833,7 +852,9 @@ grub_f2fs_mount (grub_disk_t disk)
+   if (err)
+     goto fail;
+ 
+-  data->nat_bitmap = nat_bitmap_ptr (data);
++  data->nat_bitmap = nat_bitmap_ptr (data, &data->nat_bitmap_size);
++  if (data->nat_bitmap == NULL)
++    goto fail;
+ 
+   err = get_nat_journal (data);
+   if (err)
diff --git a/SOURCES/0242-fs-f2fs-Do-not-copy-file-names-that-are-too-long.patch b/SOURCES/0242-fs-f2fs-Do-not-copy-file-names-that-are-too-long.patch
new file mode 100644
index 0000000..0553d60
--- /dev/null
+++ b/SOURCES/0242-fs-f2fs-Do-not-copy-file-names-that-are-too-long.patch
@@ -0,0 +1,38 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
+Date: Wed, 6 Apr 2022 18:17:43 +0530
+Subject: [PATCH] fs/f2fs: Do not copy file names that are too long
+
+A corrupt f2fs file system might specify a name length which is greater
+than the maximum name length supported by the GRUB f2fs driver.
+
+We will allocate enough memory to store the overly long name, but there
+are only F2FS_NAME_LEN bytes in the source, so we would read past the end
+of the source.
+
+While checking directory entries, do not copy a file name with an invalid
+length.
+
+Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 9a891f638509e031d322c94e3cbcf38d36f3993a)
+---
+ grub-core/fs/f2fs.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/grub-core/fs/f2fs.c b/grub-core/fs/f2fs.c
+index 8898b235e0..df6beb544c 100644
+--- a/grub-core/fs/f2fs.c
++++ b/grub-core/fs/f2fs.c
+@@ -1003,6 +1003,10 @@ grub_f2fs_check_dentries (struct grub_f2fs_dir_iter_ctx *ctx)
+ 
+       ftype = ctx->dentry[i].file_type;
+       name_len = grub_le_to_cpu16 (ctx->dentry[i].name_len);
++
++      if (name_len >= F2FS_NAME_LEN)
++        return 0;
++
+       filename = grub_malloc (name_len + 1);
+       if (!filename)
+         return 0;
diff --git a/SOURCES/0243-fs-btrfs-Fix-several-fuzz-issues-with-invalid-dir-it.patch b/SOURCES/0243-fs-btrfs-Fix-several-fuzz-issues-with-invalid-dir-it.patch
new file mode 100644
index 0000000..7ff5821
--- /dev/null
+++ b/SOURCES/0243-fs-btrfs-Fix-several-fuzz-issues-with-invalid-dir-it.patch
@@ -0,0 +1,79 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Darren Kenny <darren.kenny@oracle.com>
+Date: Tue, 29 Mar 2022 10:49:56 +0000
+Subject: [PATCH] fs/btrfs: Fix several fuzz issues with invalid dir item
+ sizing
+
+According to the btrfs code in Linux, the structure of a directory item
+leaf should be of the form:
+
+  |struct btrfs_dir_item|name|data|
+
+in GRUB the name len and data len are in the grub_btrfs_dir_item
+structure's n and m fields respectively.
+
+The combined size of the structure, name and data should be less than
+the allocated memory, a difference to the Linux kernel's struct
+btrfs_dir_item is that the grub_btrfs_dir_item has an extra field for
+where the name is stored, so we adjust for that too.
+
+Signed-off-by: Darren Kenny <darren.kenny@oracle.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 6d3f06c0b6a8992b9b1bb0e62af93ac5ff2781f0)
+[rharwood: we've an extra variable here]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/fs/btrfs.c | 26 ++++++++++++++++++++++++++
+ 1 file changed, 26 insertions(+)
+
+diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
+index 07c0ff874b..2fcfb738fe 100644
+--- a/grub-core/fs/btrfs.c
++++ b/grub-core/fs/btrfs.c
+@@ -2254,6 +2254,7 @@ grub_btrfs_dir (grub_device_t device, const char *path,
+   grub_uint64_t tree;
+   grub_uint8_t type;
+   char *new_path = NULL;
++  grub_size_t est_size = 0;
+ 
+   if (!data)
+     return grub_errno;
+@@ -2320,6 +2321,18 @@ grub_btrfs_dir (grub_device_t device, const char *path,
+ 	  break;
+ 	}
+ 
++      if (direl == NULL ||
++	  grub_add (grub_le_to_cpu16 (direl->n),
++		    grub_le_to_cpu16 (direl->m), &est_size) ||
++	  grub_add (est_size, sizeof (*direl), &est_size) ||
++	  grub_sub (est_size, sizeof (direl->name), &est_size) ||
++	  est_size > allocated)
++       {
++         grub_errno = GRUB_ERR_OUT_OF_RANGE;
++         r = -grub_errno;
++         goto out;
++       }
++
+       for (cdirel = direl;
+ 	   (grub_uint8_t *) cdirel - (grub_uint8_t *) direl
+ 	   < (grub_ssize_t) elemsize;
+@@ -2330,6 +2343,19 @@ grub_btrfs_dir (grub_device_t device, const char *path,
+ 	  char c;
+ 	  struct grub_btrfs_inode inode;
+ 	  struct grub_dirhook_info info;
++
++	  if (cdirel == NULL ||
++	      grub_add (grub_le_to_cpu16 (cdirel->n),
++			grub_le_to_cpu16 (cdirel->m), &est_size) ||
++	      grub_add (est_size, sizeof (*cdirel), &est_size) ||
++	      grub_sub (est_size, sizeof (cdirel->name), &est_size) ||
++	      est_size > allocated)
++	   {
++	     grub_errno = GRUB_ERR_OUT_OF_RANGE;
++	     r = -grub_errno;
++	     goto out;
++	   }
++
+ 	  err = grub_btrfs_read_inode (data, &inode, cdirel->key.object_id,
+ 				       tree);
+ 	  grub_memset (&info, 0, sizeof (info));
diff --git a/SOURCES/0244-fs-btrfs-Fix-more-ASAN-and-SEGV-issues-found-with-fu.patch b/SOURCES/0244-fs-btrfs-Fix-more-ASAN-and-SEGV-issues-found-with-fu.patch
new file mode 100644
index 0000000..d638c11
--- /dev/null
+++ b/SOURCES/0244-fs-btrfs-Fix-more-ASAN-and-SEGV-issues-found-with-fu.patch
@@ -0,0 +1,134 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Darren Kenny <darren.kenny@oracle.com>
+Date: Tue, 29 Mar 2022 15:52:46 +0000
+Subject: [PATCH] fs/btrfs: Fix more ASAN and SEGV issues found with fuzzing
+
+The fuzzer is generating btrfs file systems that have chunks with
+invalid combinations of stripes and substripes for the given RAID
+configurations.
+
+After examining the Linux kernel fs/btrfs/tree-checker.c code, it
+appears that sub-stripes should only be applied to RAID10, and in that
+case there should only ever be 2 of them.
+
+Similarly, RAID single should only have 1 stripe, and RAID1/1C3/1C4
+should have 2. 3 or 4 stripes respectively, which is what redundancy
+corresponds.
+
+Some of the chunks ended up with a size of 0, which grub_malloc() still
+returned memory for and in turn generated ASAN errors later when
+accessed.
+
+While it would be possible to specifically limit the number of stripes,
+a more correct test was on the combination of the chunk item, and the
+number of stripes by the size of the chunk stripe structure in
+comparison to the size of the chunk itself.
+
+Signed-off-by: Darren Kenny <darren.kenny@oracle.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 3849647b4b98a4419366708fc4b7f339c6f55ec7)
+---
+ grub-core/fs/btrfs.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 55 insertions(+)
+
+diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
+index 2fcfb738fe..0e9b450413 100644
+--- a/grub-core/fs/btrfs.c
++++ b/grub-core/fs/btrfs.c
+@@ -941,6 +941,12 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
+ 	return grub_error (GRUB_ERR_BAD_FS,
+ 			   "couldn't find the chunk descriptor");
+ 
++      if (!chsize)
++	{
++	  grub_dprintf ("btrfs", "zero-size chunk\n");
++	  return grub_error (GRUB_ERR_BAD_FS,
++			     "got an invalid zero-size chunk");
++	}
+       chunk = grub_malloc (chsize);
+       if (!chunk)
+ 	return grub_errno;
+@@ -999,6 +1005,16 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
+ 	      stripe_length = grub_divmod64 (grub_le_to_cpu64 (chunk->size),
+ 					     nstripes,
+ 					     NULL);
++
++	      /* For single, there should be exactly 1 stripe. */
++	      if (grub_le_to_cpu16 (chunk->nstripes) != 1)
++		{
++		  grub_dprintf ("btrfs", "invalid RAID_SINGLE: nstripes != 1 (%u)\n",
++				grub_le_to_cpu16 (chunk->nstripes));
++		  return grub_error (GRUB_ERR_BAD_FS,
++				     "invalid RAID_SINGLE: nstripes != 1 (%u)",
++				      grub_le_to_cpu16 (chunk->nstripes));
++		}
+ 	      if (stripe_length == 0)
+ 		stripe_length = 512;
+ 	      stripen = grub_divmod64 (off, stripe_length, &stripe_offset);
+@@ -1018,6 +1034,19 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
+ 	      stripen = 0;
+ 	      stripe_offset = off;
+ 	      csize = grub_le_to_cpu64 (chunk->size) - off;
++
++             /*
++	      * Redundancy, and substripes only apply to RAID10, and there
++	      * should be exactly 2 sub-stripes.
++	      */
++	     if (grub_le_to_cpu16 (chunk->nstripes) != redundancy)
++               {
++                 grub_dprintf ("btrfs", "invalid RAID1: nstripes != %u (%u)\n",
++                               redundancy, grub_le_to_cpu16 (chunk->nstripes));
++                 return grub_error (GRUB_ERR_BAD_FS,
++                                    "invalid RAID1: nstripes != %u (%u)",
++                                    redundancy, grub_le_to_cpu16 (chunk->nstripes));
++               }
+ 	      break;
+ 	    }
+ 	  case GRUB_BTRFS_CHUNK_TYPE_RAID0:
+@@ -1054,6 +1083,20 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
+ 	      stripe_offset = low + chunk_stripe_length
+ 		* high;
+ 	      csize = chunk_stripe_length - low;
++
++	      /*
++	       * Substripes only apply to RAID10, and there
++	       * should be exactly 2 sub-stripes.
++	       */
++	      if (grub_le_to_cpu16 (chunk->nsubstripes) != 2)
++		{
++		  grub_dprintf ("btrfs", "invalid RAID10: nsubstripes != 2 (%u)",
++				grub_le_to_cpu16 (chunk->nsubstripes));
++		  return grub_error (GRUB_ERR_BAD_FS,
++				     "invalid RAID10: nsubstripes != 2 (%u)",
++				     grub_le_to_cpu16 (chunk->nsubstripes));
++		}
++
+ 	      break;
+ 	    }
+ 	  case GRUB_BTRFS_CHUNK_TYPE_RAID5:
+@@ -1153,6 +1196,8 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
+ 
+ 	for (j = 0; j < 2; j++)
+ 	  {
++	    grub_size_t est_chunk_alloc = 0;
++
+ 	    grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
+ 			  "+0x%" PRIxGRUB_UINT64_T
+ 			  " (%d stripes (%d substripes) of %"
+@@ -1165,6 +1210,16 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
+ 	    grub_dprintf ("btrfs", "reading laddr 0x%" PRIxGRUB_UINT64_T "\n",
+ 			  addr);
+ 
++	    if (grub_mul (sizeof (struct grub_btrfs_chunk_stripe),
++			  grub_le_to_cpu16 (chunk->nstripes), &est_chunk_alloc) ||
++		grub_add (est_chunk_alloc,
++			  sizeof (struct grub_btrfs_chunk_item), &est_chunk_alloc) ||
++		est_chunk_alloc > chunk->size)
++	      {
++		err = GRUB_ERR_BAD_FS;
++		break;
++	      }
++
+ 	    if (is_raid56)
+ 	      {
+ 		err = btrfs_read_from_chunk (data, chunk, stripen,
diff --git a/SOURCES/0245-fs-btrfs-Fix-more-fuzz-issues-related-to-chunks.patch b/SOURCES/0245-fs-btrfs-Fix-more-fuzz-issues-related-to-chunks.patch
new file mode 100644
index 0000000..2e5145f
--- /dev/null
+++ b/SOURCES/0245-fs-btrfs-Fix-more-fuzz-issues-related-to-chunks.patch
@@ -0,0 +1,75 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Darren Kenny <darren.kenny@oracle.com>
+Date: Thu, 7 Apr 2022 15:18:12 +0000
+Subject: [PATCH] fs/btrfs: Fix more fuzz issues related to chunks
+
+The corpus we generating issues in grub_btrfs_read_logical() when
+attempting to iterate over nstripes entries in the boot mapping.
+
+In most cases the reason for the failure was that the number of strips
+exceeded the possible space statically allocated in superblock bootmapping
+space. Each stripe entry in the bootmapping block consists of
+a grub_btrfs_key followed by a grub_btrfs_chunk_stripe.
+
+Another issue that came up was that while calculating the chunk size,
+in an earlier piece of code in that function, depending on the data
+provided in the btrfs file system, it would end up calculating a size
+that was too small to contain even 1 grub_btrfs_chunk_item, which is
+obviously invalid too.
+
+Signed-off-by: Darren Kenny <darren.kenny@oracle.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit e00cd76cbadcc897a9cc4087cb2fcb5dbe15e596)
+---
+ grub-core/fs/btrfs.c | 24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
+index 0e9b450413..47325f6ad7 100644
+--- a/grub-core/fs/btrfs.c
++++ b/grub-core/fs/btrfs.c
+@@ -947,6 +947,17 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
+ 	  return grub_error (GRUB_ERR_BAD_FS,
+ 			     "got an invalid zero-size chunk");
+ 	}
++
++      /*
++       * The space being allocated for a chunk should at least be able to
++       * contain one chunk item.
++       */
++      if (chsize < sizeof (struct grub_btrfs_chunk_item))
++       {
++         grub_dprintf ("btrfs", "chunk-size too small\n");
++         return grub_error (GRUB_ERR_BAD_FS,
++                            "got an invalid chunk size");
++       }
+       chunk = grub_malloc (chsize);
+       if (!chunk)
+ 	return grub_errno;
+@@ -1194,6 +1205,13 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
+ 	if (csize > (grub_uint64_t) size)
+ 	  csize = size;
+ 
++	/*
++	 * The space for a chunk stripe is limited to the space provide in the super-block's
++	 * bootstrap mapping with an initial btrfs key at the start of each chunk.
++	 */
++	grub_size_t avail_stripes = sizeof (data->sblock.bootstrap_mapping) /
++	  (sizeof (struct grub_btrfs_key) + sizeof (struct grub_btrfs_chunk_stripe));
++
+ 	for (j = 0; j < 2; j++)
+ 	  {
+ 	    grub_size_t est_chunk_alloc = 0;
+@@ -1220,6 +1238,12 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
+ 		break;
+ 	      }
+ 
++	   if (grub_le_to_cpu16 (chunk->nstripes) > avail_stripes)
++             {
++               err = GRUB_ERR_BAD_FS;
++               break;
++             }
++
+ 	    if (is_raid56)
+ 	      {
+ 		err = btrfs_read_from_chunk (data, chunk, stripen,
diff --git a/SOURCES/0246-misc-Make-grub_min-and-grub_max-more-resilient.patch b/SOURCES/0246-misc-Make-grub_min-and-grub_max-more-resilient.patch
new file mode 100644
index 0000000..eb2e8fd
--- /dev/null
+++ b/SOURCES/0246-misc-Make-grub_min-and-grub_max-more-resilient.patch
@@ -0,0 +1,83 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 21 Mar 2022 16:06:10 -0400
+Subject: [PATCH] misc: Make grub_min() and grub_max() more resilient.
+
+grub_min(a,b) and grub_max(a,b) use a relatively naive implementation
+which leads to several problems:
+- they evaluate their parameters more than once
+- the naive way to address this, to declare temporary variables in a
+  statement-expression, isn't resilient against nested uses, because
+  MIN(a,MIN(b,c)) results in the temporary variables being declared in
+  two nested scopes, which may result in a build warning depending on
+  your build options.
+
+This patch changes our implementation to use a statement-expression
+inside a helper macro, and creates the symbols for the temporary
+variables with __COUNTER__ (A GNU C cpp extension) and token pasting to
+create uniquely named internal variables.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/loader/multiboot_elfxx.c |  4 +---
+ include/grub/misc.h                | 25 +++++++++++++++++++++++--
+ 2 files changed, 24 insertions(+), 5 deletions(-)
+
+diff --git a/grub-core/loader/multiboot_elfxx.c b/grub-core/loader/multiboot_elfxx.c
+index f2318e0d16..87f6e31aa6 100644
+--- a/grub-core/loader/multiboot_elfxx.c
++++ b/grub-core/loader/multiboot_elfxx.c
+@@ -35,9 +35,7 @@
+ #endif
+ 
+ #include <grub/i386/relocator.h>
+-
+-#define CONCAT(a,b)	CONCAT_(a, b)
+-#define CONCAT_(a,b)	a ## b
++#include <grub/misc.h>
+ 
+ #pragma GCC diagnostic ignored "-Wcast-align"
+ 
+diff --git a/include/grub/misc.h b/include/grub/misc.h
+index 6c4aa85ac5..cf84aec1db 100644
+--- a/include/grub/misc.h
++++ b/include/grub/misc.h
+@@ -35,6 +35,14 @@
+ #define ARRAY_SIZE(array) (sizeof (array) / sizeof (array[0]))
+ #define COMPILE_TIME_ASSERT(cond) switch (0) { case 1: case !(cond): ; }
+ 
++#ifndef CONCAT_
++#define CONCAT_(a, b) a ## b
++#endif
++
++#ifndef CONCAT
++#define CONCAT(a, b) CONCAT_(a, b)
++#endif
++
+ #define grub_dprintf(condition, ...) grub_real_dprintf(GRUB_FILE, __LINE__, condition, __VA_ARGS__)
+ 
+ void *EXPORT_FUNC(grub_memmove) (void *dest, const void *src, grub_size_t n);
+@@ -498,8 +506,21 @@ void EXPORT_FUNC(grub_real_boot_time) (const char *file,
+ #define grub_boot_time(...)
+ #endif
+ 
+-#define grub_max(a, b) (((a) > (b)) ? (a) : (b))
+-#define grub_min(a, b) (((a) < (b)) ? (a) : (b))
++#define _grub_min(a, b, _a, _b)						      \
++  ({ typeof (a) _a = (a);						      \
++     typeof (b) _b = (b);						      \
++     _a < _b ? _a : _b; })
++#define grub_min(a, b) _grub_min(a, b,					      \
++				 CONCAT(_a_,__COUNTER__),		      \
++				 CONCAT(_b_,__COUNTER__))
++
++#define _grub_max(a, b, _a, _b)						      \
++  ({ typeof (a) _a = (a);						      \
++     typeof (b) _b = (b);						      \
++     _a > _b ? _a : _b; })
++#define grub_max(a, b) _grub_max(a, b,					      \
++				 CONCAT(_a_,__COUNTER__),		      \
++				 CONCAT(_b_,__COUNTER__))
+ 
+ #define grub_log2ull(n) (GRUB_TYPE_BITS (grub_uint64_t) - __builtin_clzll (n) - 1)
+ 
diff --git a/SOURCES/0247-ReiserFS-switch-to-using-grub_min-grub_max.patch b/SOURCES/0247-ReiserFS-switch-to-using-grub_min-grub_max.patch
new file mode 100644
index 0000000..0707af3
--- /dev/null
+++ b/SOURCES/0247-ReiserFS-switch-to-using-grub_min-grub_max.patch
@@ -0,0 +1,92 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 21 Apr 2022 16:31:17 -0400
+Subject: [PATCH] ReiserFS: switch to using grub_min()/grub_max()
+
+This is a minor cleanup patch to remove the bespoke MIN() and MAX()
+definitions from the reiserfs driver, and uses grub_min() / grub_max()
+instead.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/fs/reiserfs.c | 28 +++++++++-------------------
+ 1 file changed, 9 insertions(+), 19 deletions(-)
+
+diff --git a/grub-core/fs/reiserfs.c b/grub-core/fs/reiserfs.c
+index af6a226a7f..b8253da7fe 100644
+--- a/grub-core/fs/reiserfs.c
++++ b/grub-core/fs/reiserfs.c
+@@ -42,16 +42,6 @@
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+-#define MIN(a, b) \
+-  ({ typeof (a) _a = (a); \
+-     typeof (b) _b = (b); \
+-     _a < _b ? _a : _b; })
+-
+-#define MAX(a, b) \
+-  ({ typeof (a) _a = (a); \
+-     typeof (b) _b = (b); \
+-     _a > _b ? _a : _b; })
+-
+ #define REISERFS_SUPER_BLOCK_OFFSET 0x10000
+ #define REISERFS_MAGIC_LEN 12
+ #define REISERFS_MAGIC_STRING "ReIsEr"
+@@ -1076,7 +1066,7 @@ grub_reiserfs_read_real (struct grub_fshelp_node *node,
+   grub_reiserfs_set_key_type (&key, GRUB_REISERFS_ANY, 2);
+   initial_position = off;
+   current_position = 0;
+-  final_position = MIN (len + initial_position, node->size);
++  final_position = grub_min (len + initial_position, node->size);
+   grub_dprintf ("reiserfs",
+ 		"Reading from %lld to %lld (%lld instead of requested %ld)\n",
+ 		(unsigned long long) initial_position,
+@@ -1115,8 +1105,8 @@ grub_reiserfs_read_real (struct grub_fshelp_node *node,
+           grub_dprintf ("reiserfs_blocktype", "D: %u\n", (unsigned) block);
+           if (initial_position < current_position + item_size)
+             {
+-              offset = MAX ((signed) (initial_position - current_position), 0);
+-              length = (MIN (item_size, final_position - current_position)
++              offset = grub_max ((signed) (initial_position - current_position), 0);
++              length = (grub_min (item_size, final_position - current_position)
+                         - offset);
+               grub_dprintf ("reiserfs",
+                             "Reading direct block %u from %u to %u...\n",
+@@ -1161,9 +1151,9 @@ grub_reiserfs_read_real (struct grub_fshelp_node *node,
+               grub_dprintf ("reiserfs_blocktype", "I: %u\n", (unsigned) block);
+               if (current_position + block_size >= initial_position)
+                 {
+-                  offset = MAX ((signed) (initial_position - current_position),
+-                                0);
+-                  length = (MIN (block_size, final_position - current_position)
++                  offset = grub_max ((signed) (initial_position - current_position),
++				     0);
++                  length = (grub_min (block_size, final_position - current_position)
+                             - offset);
+                   grub_dprintf ("reiserfs",
+                                 "Reading indirect block %u from %u to %u...\n",
+@@ -1205,7 +1195,7 @@ grub_reiserfs_read_real (struct grub_fshelp_node *node,
+   switch (found.type)
+     {
+       case GRUB_REISERFS_DIRECT:
+-        read_length = MIN (len, item_size - file->offset);
++        read_length = grub_min (len, item_size - file->offset);
+         grub_disk_read (found.data->disk,
+                         (found.block_number * block_size) / GRUB_DISK_SECTOR_SIZE,
+                         grub_le_to_cpu16 (found.header.item_location) + file->offset,
+@@ -1224,12 +1214,12 @@ grub_reiserfs_read_real (struct grub_fshelp_node *node,
+                         item_size, (char *) indirect_block_ptr);
+         if (grub_errno)
+           goto fail;
+-        len = MIN (len, file->size - file->offset);
++        len = grub_min (len, file->size - file->offset);
+         for (indirect_block = file->offset / block_size;
+              indirect_block < indirect_block_count && read_length < len;
+              indirect_block++)
+           {
+-            read = MIN (block_size, len - read_length);
++            read = grub_min (block_size, len - read_length);
+             grub_disk_read (found.data->disk,
+                             (grub_le_to_cpu32 (indirect_block_ptr[indirect_block]) * block_size) / GRUB_DISK_SECTOR_SIZE,
+                             file->offset % block_size, read,
diff --git a/SOURCES/0248-misc-make-grub_boot_time-also-call-grub_dprintf-boot.patch b/SOURCES/0248-misc-make-grub_boot_time-also-call-grub_dprintf-boot.patch
new file mode 100644
index 0000000..a7ac6f2
--- /dev/null
+++ b/SOURCES/0248-misc-make-grub_boot_time-also-call-grub_dprintf-boot.patch
@@ -0,0 +1,46 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 24 Mar 2022 14:40:01 -0400
+Subject: [PATCH] misc: make grub_boot_time() also call
+ grub_dprintf("boot",...)
+
+Currently grub_boot_time() includes valuable debugging messages, but if
+you build without BOOT_TIME_STATS enabled, they are silently and
+confusingly compiled away.
+
+This patch changes grub_boot_time() to also log when "boot" is enabled
+in DEBUG, regardless of BOOT_TIME_STATS.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/kern/misc.c | 3 ++-
+ include/grub/misc.h   | 2 +-
+ 2 files changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
+index a186ad3dd4..cb45461402 100644
+--- a/grub-core/kern/misc.c
++++ b/grub-core/kern/misc.c
+@@ -1334,7 +1334,8 @@ grub_real_boot_time (const char *file,
+   n->next = 0;
+ 
+   va_start (args, fmt);
+-  n->msg = grub_xvasprintf (fmt, args);    
++  n->msg = grub_xvasprintf (fmt, args);
++  grub_dprintf ("boot", "%s\n", n->msg);
+   va_end (args);
+ 
+   *boot_time_last = n;
+diff --git a/include/grub/misc.h b/include/grub/misc.h
+index cf84aec1db..faae0ae860 100644
+--- a/include/grub/misc.h
++++ b/include/grub/misc.h
+@@ -503,7 +503,7 @@ void EXPORT_FUNC(grub_real_boot_time) (const char *file,
+ 				       const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 3, 4)));
+ #define grub_boot_time(...) grub_real_boot_time(GRUB_FILE, __LINE__, __VA_ARGS__)
+ #else
+-#define grub_boot_time(...)
++#define grub_boot_time(fmt, ...) grub_dprintf("boot", fmt "\n", ##__VA_ARGS__)
+ #endif
+ 
+ #define _grub_min(a, b, _a, _b)						      \
diff --git a/SOURCES/0249-modules-make-.module_license-read-only.patch b/SOURCES/0249-modules-make-.module_license-read-only.patch
new file mode 100644
index 0000000..ba3b313
--- /dev/null
+++ b/SOURCES/0249-modules-make-.module_license-read-only.patch
@@ -0,0 +1,30 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 24 Feb 2022 16:32:51 -0500
+Subject: [PATCH] modules: make .module_license read-only
+
+Currently .module_license is set writable (that is, the section has the
+SHF_WRITE flag set) in the module's ELF headers.  This probably never
+actually matters, but it can't possibly be correct.
+
+This patch sets that data as "const", which causes that flag not to be
+set.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ include/grub/dl.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/grub/dl.h b/include/grub/dl.h
+index 20d870f2a4..618ae6f474 100644
+--- a/include/grub/dl.h
++++ b/include/grub/dl.h
+@@ -121,7 +121,7 @@ grub_mod_fini (void)
+ #define ATTRIBUTE_USED __unused__
+ #endif
+ #define GRUB_MOD_LICENSE(license)	\
+-  static char grub_module_license[] __attribute__ ((section (GRUB_MOD_SECTION (module_license)), ATTRIBUTE_USED)) = "LICENSE=" license;
++  static const char grub_module_license[] __attribute__ ((section (GRUB_MOD_SECTION (module_license)), ATTRIBUTE_USED)) = "LICENSE=" license;
+ #define GRUB_MOD_DEP(name)	\
+ static const char grub_module_depend_##name[] \
+  __attribute__((section(GRUB_MOD_SECTION(moddeps)), ATTRIBUTE_USED)) = #name
diff --git a/SOURCES/0250-modules-strip-.llvm_addrsig-sections-and-similar.patch b/SOURCES/0250-modules-strip-.llvm_addrsig-sections-and-similar.patch
new file mode 100644
index 0000000..9f26115
--- /dev/null
+++ b/SOURCES/0250-modules-strip-.llvm_addrsig-sections-and-similar.patch
@@ -0,0 +1,39 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 24 Feb 2022 16:40:11 -0500
+Subject: [PATCH] modules: strip .llvm_addrsig sections and similar.
+
+Currently grub modules built with clang or gcc have several sections
+which we don't actually need or support.
+
+We already have a list of section to skip in genmod.sh, and this patch
+adds the following sections to that list (as well as a few newlines):
+
+.note.gnu.property
+.llvm*
+
+Note that the glob there won't work without a new enough linker, but the
+failure is just reversion to the status quo, so that's not a big problem.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/genmod.sh.in | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/genmod.sh.in b/grub-core/genmod.sh.in
+index 1250589b3f..c2c5280d75 100644
+--- a/grub-core/genmod.sh.in
++++ b/grub-core/genmod.sh.in
+@@ -57,8 +57,11 @@ if test x@TARGET_APPLE_LINKER@ != x1; then
+ 	    @TARGET_STRIP@ --strip-unneeded \
+ 		-K grub_mod_init -K grub_mod_fini \
+ 		-K _grub_mod_init -K _grub_mod_fini \
+-		-R .note.gnu.gold-version -R .note.GNU-stack \
++		-R .note.GNU-stack \
++		-R .note.gnu.gold-version \
++		-R .note.gnu.property \
+ 		-R .gnu.build.attributes \
++		-R '.llvm*' \
+ 		-R .rel.gnu.build.attributes \
+ 		-R .rela.gnu.build.attributes \
+ 		-R .eh_frame -R .rela.eh_frame -R .rel.eh_frame \
diff --git a/SOURCES/0251-modules-Don-t-allocate-space-for-non-allocable-secti.patch b/SOURCES/0251-modules-Don-t-allocate-space-for-non-allocable-secti.patch
new file mode 100644
index 0000000..d07d838
--- /dev/null
+++ b/SOURCES/0251-modules-Don-t-allocate-space-for-non-allocable-secti.patch
@@ -0,0 +1,35 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 21 Mar 2022 16:56:10 -0400
+Subject: [PATCH] modules: Don't allocate space for non-allocable sections.
+
+Currently when loading grub modules, we allocate space for all sections,
+including those without SHF_ALLOC set.  We then copy the sections that
+/do/ have SHF_ALLOC set into the allocated memory, leaving some of our
+allocation untouched forever.  Additionally, on platforms with GOT
+fixups and trampolines, we currently compute alignment round-ups for the
+sections and sections with sh_size = 0.
+
+This patch removes the extra space from the allocation computation, and
+makes the allocation computation loop skip empty sections as the loading
+loop does.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/kern/dl.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
+index f304494574..aef8af8aa7 100644
+--- a/grub-core/kern/dl.c
++++ b/grub-core/kern/dl.c
+@@ -289,6 +289,9 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
+        i < e->e_shnum;
+        i++, s = (const Elf_Shdr *)((const char *) s + e->e_shentsize))
+     {
++      if (s->sh_size == 0 || !(s->sh_flags & SHF_ALLOC))
++	continue;
++
+       tsize = ALIGN_UP (tsize, s->sh_addralign) + s->sh_size;
+       if (talign < s->sh_addralign)
+ 	talign = s->sh_addralign;
diff --git a/SOURCES/0252-pe-add-the-DOS-header-struct-and-fix-some-bad-naming.patch b/SOURCES/0252-pe-add-the-DOS-header-struct-and-fix-some-bad-naming.patch
new file mode 100644
index 0000000..ef51214
--- /dev/null
+++ b/SOURCES/0252-pe-add-the-DOS-header-struct-and-fix-some-bad-naming.patch
@@ -0,0 +1,81 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Fri, 25 Mar 2022 15:40:12 -0400
+Subject: [PATCH] pe: add the DOS header struct and fix some bad naming.
+
+In order to properly validate a loaded kernel's support for being loaded
+without a writable stack or executable, we need to be able to properly
+parse arbitrary PE headers.
+
+Currently, pe32.h is written in such a way that the MS-DOS header that
+tells us where to find the PE header in the binary can't be accessed.
+Further, for some reason it calls the DOS MZ magic "GRUB_PE32_MAGIC".
+
+This patch adds the structure for the DOS header, renames the DOS magic
+define, and adds defines for the actual PE magic.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/loader/arm64/linux.c |  2 +-
+ include/grub/efi/pe32.h        | 28 ++++++++++++++++++++++++++--
+ 2 files changed, 27 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
+index d2af47c2c0..cc67f43906 100644
+--- a/grub-core/loader/arm64/linux.c
++++ b/grub-core/loader/arm64/linux.c
+@@ -58,7 +58,7 @@ grub_arch_efi_linux_check_image (struct linux_arch_kernel_header * lh)
+   if (lh->magic != GRUB_LINUX_ARMXX_MAGIC_SIGNATURE)
+     return grub_error(GRUB_ERR_BAD_OS, "invalid magic number");
+ 
+-  if ((lh->code0 & 0xffff) != GRUB_PE32_MAGIC)
++  if ((lh->code0 & 0xffff) != GRUB_DOS_MAGIC)
+     return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ 		       N_("plain image kernel not supported - rebuild with CONFIG_(U)EFI_STUB enabled"));
+ 
+diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h
+index a43adf2746..2a5e1ee003 100644
+--- a/include/grub/efi/pe32.h
++++ b/include/grub/efi/pe32.h
+@@ -46,7 +46,30 @@
+ 
+ #define GRUB_PE32_MSDOS_STUB_SIZE	0x80
+ 
+-#define GRUB_PE32_MAGIC			0x5a4d
++#define GRUB_DOS_MAGIC			0x5a4d
++
++struct grub_dos_header
++{
++  grub_uint16_t magic;
++  grub_uint16_t cblp;
++  grub_uint16_t cp;
++  grub_uint16_t crlc;
++  grub_uint16_t cparhdr;
++  grub_uint16_t minalloc;
++  grub_uint16_t maxalloc;
++  grub_uint16_t ss;
++  grub_uint16_t sp;
++  grub_uint16_t csum;
++  grub_uint16_t ip;
++  grub_uint16_t cs;
++  grub_uint16_t lfarlc;
++  grub_uint16_t ovno;
++  grub_uint16_t res0[4];
++  grub_uint16_t oemid;
++  grub_uint16_t oeminfo;
++  grub_uint16_t res1[10];
++  grub_uint32_t lfanew;
++};
+ 
+ /* According to the spec, the minimal alignment is 512 bytes...
+    But some examples (such as EFI drivers in the Intel
+@@ -280,7 +303,8 @@ struct grub_pe32_section_table
+ 
+ 
+ 
+-#define GRUB_PE32_SIGNATURE_SIZE 4
++#define GRUB_PE32_SIGNATURE_SIZE		4
++#define GRUB_PE32_SIGNATURE			"PE\0\0"
+ 
+ struct grub_pe32_header
+ {
diff --git a/SOURCES/0253-EFI-allocate-kernel-in-EFI_RUNTIME_SERVICES_CODE-ins.patch b/SOURCES/0253-EFI-allocate-kernel-in-EFI_RUNTIME_SERVICES_CODE-ins.patch
new file mode 100644
index 0000000..c6688cd
--- /dev/null
+++ b/SOURCES/0253-EFI-allocate-kernel-in-EFI_RUNTIME_SERVICES_CODE-ins.patch
@@ -0,0 +1,85 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Wed, 9 Feb 2022 16:08:20 -0500
+Subject: [PATCH] EFI: allocate kernel in EFI_RUNTIME_SERVICES_CODE instead of
+ EFI_LOADER_DATA.
+
+On some of the firmwares with more security mitigations, EFI_LOADER_DATA
+doesn't get you executable memory, and we take a fault and reboot when
+we enter kernel.
+
+This patch correctly allocates the kernel code as EFI_RUNTIME_SERVICES_CODE
+rather than EFI_LOADER_DATA.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+[rharwood: use kernel_size]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/loader/i386/efi/linux.c | 19 +++++++++++++------
+ 1 file changed, 13 insertions(+), 6 deletions(-)
+
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index 9e5c11ac69..92b2fb5091 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -86,7 +86,9 @@ kernel_free(void *addr, grub_efi_uintn_t size)
+ }
+ 
+ static void *
+-kernel_alloc(grub_efi_uintn_t size, const char * const errmsg)
++kernel_alloc(grub_efi_uintn_t size,
++	     grub_efi_memory_type_t memtype,
++	     const char * const errmsg)
+ {
+   void *addr = 0;
+   unsigned int i;
+@@ -112,7 +114,7 @@ kernel_alloc(grub_efi_uintn_t size, const char * const errmsg)
+       prev_max = max;
+       addr = grub_efi_allocate_pages_real (max, pages,
+ 					   max_addresses[i].alloc_type,
+-					   GRUB_EFI_LOADER_DATA);
++					   memtype);
+       if (addr)
+ 	grub_dprintf ("linux", "Allocated at %p\n", addr);
+     }
+@@ -242,7 +244,8 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[])
+ 	}
+     }
+ 
+-  initrd_mem = kernel_alloc(size, N_("can't allocate initrd"));
++  initrd_mem = kernel_alloc(size, GRUB_EFI_RUNTIME_SERVICES_DATA,
++			    N_("can't allocate initrd"));
+   if (initrd_mem == NULL)
+     goto fail;
+   grub_dprintf ("linux", "initrd_mem = %p\n", initrd_mem);
+@@ -393,7 +396,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+     }
+ #endif
+ 
+-  params = kernel_alloc (sizeof(*params), "cannot allocate kernel parameters");
++  params = kernel_alloc (sizeof(*params), GRUB_EFI_RUNTIME_SERVICES_DATA,
++			 "cannot allocate kernel parameters");
+   if (!params)
+     goto fail;
+   grub_dprintf ("linux", "params = %p\n", params);
+@@ -415,7 +419,9 @@ 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");
+-  cmdline = kernel_alloc (lh->cmdline_size + 1, N_("can't allocate cmdline"));
++  cmdline = kernel_alloc (lh->cmdline_size + 1,
++			  GRUB_EFI_RUNTIME_SERVICES_DATA,
++			  N_("can't allocate cmdline"));
+   if (!cmdline)
+     goto fail;
+   grub_dprintf ("linux", "cmdline = %p\n", cmdline);
+@@ -461,7 +467,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   max_addresses[1].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
+   max_addresses[2].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
+   kernel_size = lh->init_size;
+-  kernel_mem = kernel_alloc (kernel_size, N_("can't allocate kernel"));
++  kernel_mem = kernel_alloc (kernel_size, GRUB_EFI_RUNTIME_SERVICES_CODE,
++			     N_("can't allocate kernel"));
+   restore_addresses();
+   if (!kernel_mem)
+     goto fail;
diff --git a/SOURCES/0254-modules-load-module-sections-at-page-aligned-address.patch b/SOURCES/0254-modules-load-module-sections-at-page-aligned-address.patch
new file mode 100644
index 0000000..eb171f5
--- /dev/null
+++ b/SOURCES/0254-modules-load-module-sections-at-page-aligned-address.patch
@@ -0,0 +1,378 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 21 Mar 2022 17:45:40 -0400
+Subject: [PATCH] modules: load module sections at page-aligned addresses
+
+Currently we load module sections at whatever alignment gcc+ld happened
+to dump into the ELF section header, which is often pretty useless.  For
+example, by default time.mod has these sections on a current x86_64
+build:
+
+$ eu-readelf -a grub-core/time.mod |& grep ^Section -A13
+Section Headers:
+[Nr] Name            Type         Addr  Off      Size     ES Flags Lk Inf Al
+[ 0]                 NULL         0     00000000 00000000  0        0   0  0
+[ 1] .text           PROGBITS     0     00000040 0000015e  0 AX     0   0  1
+[ 2] .rela.text      RELA         0     00000458 000001e0 24 I      8   1  8
+[ 3] .rodata.str1.1  PROGBITS     0     0000019e 000000a1  1 AMS    0   0  1
+[ 4] .module_license PROGBITS     0     00000240 0000000f  0 A      0   0  8
+[ 5] .data           PROGBITS     0     0000024f 00000000  0 WA     0   0  1
+[ 6] .bss            NOBITS       0     00000250 00000008  0 WA     0   0  8
+[ 7] .modname        PROGBITS     0     00000250 00000005  0        0   0  1
+[ 8] .symtab         SYMTAB       0     00000258 00000150 24        9   6  8
+[ 9] .strtab         STRTAB       0     000003a8 000000ab  0        0   0  1
+[10] .shstrtab       STRTAB       0     00000638 00000059  0        0   0  1
+
+With NX protections being page based, loading sections with either a 1
+or 8 *byte* alignment does absolutely nothing to help us out.
+
+This patch switches most EFI platforms to load module sections at 4kB
+page-aligned addresses.  To do so, it adds an new per-arch function,
+grub_arch_dl_min_alignment(), which returns the alignment needed for
+dynamically loaded sections (in bytes).  Currently it sets it to 4096
+when GRUB_MACHINE_EFI is true on x86_64, i386, arm, arm64, and emu, and
+1-byte alignment on everything else.
+
+It then changes the allocation size computation and the loader code in
+grub_dl_load_segments() to align the locations and sizes up to these
+boundaries, and fills any added padding with zeros.
+
+All of this happens before relocations are applied, so the relocations
+factor that in with no change.
+
+As an aside, initially Daniel Kiper and I thought that it might be a
+better idea to split the modules up into top-level sections as
+.text.modules, .rodata.modules, .data.modules, etc., so that their page
+permissions would get set by the loader that's loading grub itself.
+This turns out to have two significant downsides: 1) either in mkimage
+or in grub_dl_relocate_symbols(), you wind up having to dynamically
+process the relocations to accommodate the moved module sections, and 2)
+you then need to change the permissions on the modules and change them
+back while relocating them in grub_dl_relocate_symbols(), which means
+that any loader that /does/ honor the section flags but does /not/
+generally support NX with the memory attributes API will cause grub to
+fail.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/kern/arm/dl.c     | 13 +++++++++++++
+ grub-core/kern/arm64/dl.c   | 13 +++++++++++++
+ grub-core/kern/dl.c         | 29 +++++++++++++++++++++--------
+ grub-core/kern/emu/full.c   | 13 +++++++++++++
+ grub-core/kern/i386/dl.c    | 13 +++++++++++++
+ grub-core/kern/ia64/dl.c    |  9 +++++++++
+ grub-core/kern/mips/dl.c    |  8 ++++++++
+ grub-core/kern/powerpc/dl.c |  9 +++++++++
+ grub-core/kern/riscv/dl.c   | 13 +++++++++++++
+ grub-core/kern/sparc64/dl.c |  9 +++++++++
+ grub-core/kern/x86_64/dl.c  | 13 +++++++++++++
+ include/grub/dl.h           |  2 ++
+ docs/grub-dev.texi          |  6 +++---
+ 13 files changed, 139 insertions(+), 11 deletions(-)
+
+diff --git a/grub-core/kern/arm/dl.c b/grub-core/kern/arm/dl.c
+index eab9d17ff2..9260737936 100644
+--- a/grub-core/kern/arm/dl.c
++++ b/grub-core/kern/arm/dl.c
+@@ -278,3 +278,16 @@ grub_arch_dl_check_header (void *ehdr)
+ 
+   return GRUB_ERR_NONE;
+ }
++
++/*
++ * Tell the loader what our minimum section alignment is.
++ */
++grub_size_t
++grub_arch_dl_min_alignment (void)
++{
++#ifdef GRUB_MACHINE_EFI
++  return 4096;
++#else
++  return 1;
++#endif
++}
+diff --git a/grub-core/kern/arm64/dl.c b/grub-core/kern/arm64/dl.c
+index 512e5a80b0..0d4a26857f 100644
+--- a/grub-core/kern/arm64/dl.c
++++ b/grub-core/kern/arm64/dl.c
+@@ -196,3 +196,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
+ 
+   return GRUB_ERR_NONE;
+ }
++
++/*
++ * Tell the loader what our minimum section alignment is.
++ */
++grub_size_t
++grub_arch_dl_min_alignment (void)
++{
++#ifdef GRUB_MACHINE_EFI
++  return 4096;
++#else
++  return 1;
++#endif
++}
+diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
+index aef8af8aa7..8c7aacef39 100644
+--- a/grub-core/kern/dl.c
++++ b/grub-core/kern/dl.c
+@@ -277,7 +277,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
+ {
+   unsigned i;
+   const Elf_Shdr *s;
+-  grub_size_t tsize = 0, talign = 1;
++  grub_size_t tsize = 0, talign = 1, arch_addralign = 1;
+ #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
+   grub_size_t tramp;
+   grub_size_t got;
+@@ -285,16 +285,24 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
+ #endif
+   char *ptr;
+ 
++  arch_addralign = grub_arch_dl_min_alignment ();
++
+   for (i = 0, s = (const Elf_Shdr *)((const char *) e + e->e_shoff);
+        i < e->e_shnum;
+        i++, s = (const Elf_Shdr *)((const char *) s + e->e_shentsize))
+     {
++      grub_size_t sh_addralign;
++      grub_size_t sh_size;
++
+       if (s->sh_size == 0 || !(s->sh_flags & SHF_ALLOC))
+ 	continue;
+ 
+-      tsize = ALIGN_UP (tsize, s->sh_addralign) + s->sh_size;
+-      if (talign < s->sh_addralign)
+-	talign = s->sh_addralign;
++      sh_addralign = ALIGN_UP(s->sh_addralign, arch_addralign);
++      sh_size = ALIGN_UP(s->sh_size, sh_addralign);
++
++      tsize = ALIGN_UP (tsize, sh_addralign) + sh_size;
++      if (talign < sh_addralign)
++	talign = sh_addralign;
+     }
+ 
+ #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
+@@ -323,6 +331,9 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
+        i < e->e_shnum;
+        i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
+     {
++      grub_size_t sh_addralign = ALIGN_UP(s->sh_addralign, arch_addralign);
++      grub_size_t sh_size = ALIGN_UP(s->sh_size, sh_addralign);
++
+       if (s->sh_flags & SHF_ALLOC)
+ 	{
+ 	  grub_dl_segment_t seg;
+@@ -335,17 +346,19 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
+ 	    {
+ 	      void *addr;
+ 
+-	      ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, s->sh_addralign);
++	      ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, sh_addralign);
+ 	      addr = ptr;
+-	      ptr += s->sh_size;
++	      ptr += sh_size;
+ 
+ 	      switch (s->sh_type)
+ 		{
+ 		case SHT_PROGBITS:
+ 		  grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size);
++		  grub_memset ((char *)addr + s->sh_size, 0,
++			       sh_size - s->sh_size);
+ 		  break;
+ 		case SHT_NOBITS:
+-		  grub_memset (addr, 0, s->sh_size);
++		  grub_memset (addr, 0, sh_size);
+ 		  break;
+ 		}
+ 
+@@ -354,7 +367,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
+ 	  else
+ 	    seg->addr = 0;
+ 
+-	  seg->size = s->sh_size;
++	  seg->size = sh_size;
+ 	  seg->section = i;
+ 	  seg->next = mod->segment;
+ 	  mod->segment = seg;
+diff --git a/grub-core/kern/emu/full.c b/grub-core/kern/emu/full.c
+index e8d63b1f5f..1de1c28eb0 100644
+--- a/grub-core/kern/emu/full.c
++++ b/grub-core/kern/emu/full.c
+@@ -67,3 +67,16 @@ grub_arch_dl_init_linker (void)
+ }
+ #endif
+ 
++
++/*
++ * Tell the loader what our minimum section alignment is.
++ */
++grub_size_t
++grub_arch_dl_min_alignment (void)
++{
++#ifdef GRUB_MACHINE_EFI
++  return 4096;
++#else
++  return 1;
++#endif
++}
+diff --git a/grub-core/kern/i386/dl.c b/grub-core/kern/i386/dl.c
+index 1346da5cc9..d6b4681fc9 100644
+--- a/grub-core/kern/i386/dl.c
++++ b/grub-core/kern/i386/dl.c
+@@ -79,3 +79,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
+ 
+   return GRUB_ERR_NONE;
+ }
++
++/*
++ * Tell the loader what our minimum section alignment is.
++ */
++grub_size_t
++grub_arch_dl_min_alignment (void)
++{
++#ifdef GRUB_MACHINE_EFI
++  return 4096;
++#else
++  return 1;
++#endif
++}
+diff --git a/grub-core/kern/ia64/dl.c b/grub-core/kern/ia64/dl.c
+index db59300fea..92d82c5750 100644
+--- a/grub-core/kern/ia64/dl.c
++++ b/grub-core/kern/ia64/dl.c
+@@ -148,3 +148,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
+     }
+   return GRUB_ERR_NONE;
+ }
++
++/*
++ * Tell the loader what our minimum section alignment is.
++ */
++grub_size_t
++grub_arch_dl_min_alignment (void)
++{
++  return 1;
++}
+diff --git a/grub-core/kern/mips/dl.c b/grub-core/kern/mips/dl.c
+index 5d7d299c74..6d83bd71e9 100644
+--- a/grub-core/kern/mips/dl.c
++++ b/grub-core/kern/mips/dl.c
+@@ -272,3 +272,11 @@ grub_arch_dl_init_linker (void)
+   grub_dl_register_symbol ("_gp_disp", &_gp_disp_dummy, 0, 0);
+ }
+ 
++/*
++ * Tell the loader what our minimum section alignment is.
++ */
++grub_size_t
++grub_arch_dl_min_alignment (void)
++{
++  return 1;
++}
+diff --git a/grub-core/kern/powerpc/dl.c b/grub-core/kern/powerpc/dl.c
+index cdd61b305f..5d9ba2e158 100644
+--- a/grub-core/kern/powerpc/dl.c
++++ b/grub-core/kern/powerpc/dl.c
+@@ -167,3 +167,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
+ 
+   return GRUB_ERR_NONE;
+ }
++
++/*
++ * Tell the loader what our minimum section alignment is.
++ */
++grub_size_t
++grub_arch_dl_min_alignment (void)
++{
++  return 1;
++}
+diff --git a/grub-core/kern/riscv/dl.c b/grub-core/kern/riscv/dl.c
+index f26b12aaa4..aa18f9e990 100644
+--- a/grub-core/kern/riscv/dl.c
++++ b/grub-core/kern/riscv/dl.c
+@@ -343,3 +343,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
+ 
+   return GRUB_ERR_NONE;
+ }
++
++/*
++ * Tell the loader what our minimum section alignment is.
++ */
++grub_size_t
++grub_arch_dl_min_alignment (void)
++{
++#ifdef GRUB_MACHINE_EFI
++  return 4096;
++#else
++  return 1;
++#endif
++}
+diff --git a/grub-core/kern/sparc64/dl.c b/grub-core/kern/sparc64/dl.c
+index f3d960186b..f054f08241 100644
+--- a/grub-core/kern/sparc64/dl.c
++++ b/grub-core/kern/sparc64/dl.c
+@@ -189,3 +189,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
+ 
+   return GRUB_ERR_NONE;
+ }
++
++/*
++ * Tell the loader what our minimum section alignment is.
++ */
++grub_size_t
++grub_arch_dl_min_alignment (void)
++{
++  return 1;
++}
+diff --git a/grub-core/kern/x86_64/dl.c b/grub-core/kern/x86_64/dl.c
+index e5a8bdcf4f..a105dc50ce 100644
+--- a/grub-core/kern/x86_64/dl.c
++++ b/grub-core/kern/x86_64/dl.c
+@@ -119,3 +119,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
+ 
+   return GRUB_ERR_NONE;
+ }
++
++/*
++ * Tell the loader what our minimum section alignment is.
++ */
++grub_size_t
++grub_arch_dl_min_alignment (void)
++{
++#ifdef GRUB_MACHINE_EFI
++  return 4096;
++#else
++  return 1;
++#endif
++}
+diff --git a/include/grub/dl.h b/include/grub/dl.h
+index 618ae6f474..f36ed5cb17 100644
+--- a/include/grub/dl.h
++++ b/include/grub/dl.h
+@@ -280,6 +280,8 @@ grub_err_t grub_arch_dl_check_header (void *ehdr);
+ grub_err_t
+ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
+ 			       Elf_Shdr *s, grub_dl_segment_t seg);
++grub_size_t
++grub_arch_dl_min_alignment (void);
+ #endif
+ 
+ #if defined (_mips)
+diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
+index 19f708ee66..7b2455a8fe 100644
+--- a/docs/grub-dev.texi
++++ b/docs/grub-dev.texi
+@@ -755,9 +755,9 @@ declare startup asm file ($cpu_$platform_startup) as well as any other files
+ (e.g. init.c and callwrap.S) (e.g. $cpu_$platform = kern/$cpu/$platform/init.c).
+ At this stage you will also need to add dummy dl.c and cache.S with functions
+ grub_err_t grub_arch_dl_check_header (void *ehdr), grub_err_t
+-grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) (dl.c) and
+-void grub_arch_sync_caches (void *address, grub_size_t len) (cache.S). They
+-won't be used for now.
++grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) (dl.c), grub_uint32_t
++grub_arch_dl_min_alignment (void), and void grub_arch_sync_caches (void
++*address, grub_size_t len) (cache.S). They won't be used for now.
+ 
+ You will need to create directory include/$cpu/$platform and a file
+ include/$cpu/types.h. The later folowing this template:
diff --git a/SOURCES/0255-nx-add-memory-attribute-get-set-API.patch b/SOURCES/0255-nx-add-memory-attribute-get-set-API.patch
new file mode 100644
index 0000000..91c9d2f
--- /dev/null
+++ b/SOURCES/0255-nx-add-memory-attribute-get-set-API.patch
@@ -0,0 +1,317 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 22 Mar 2022 10:56:21 -0400
+Subject: [PATCH] nx: add memory attribute get/set API
+
+For NX, we need to set the page access permission attributes for write
+and execute permissions.
+
+This patch adds two new primitives, grub_set_mem_attrs() and
+grub_clear_mem_attrs(), and associated constant definitions, to be used
+for that purpose.
+
+For most platforms, it adds a dummy implementation that returns
+GRUB_ERR_NONE.  On EFI platforms, it adds a common helper function,
+grub_efi_status_to_err(), which translates EFI error codes to grub error
+codes, adds headers for the EFI Memory Attribute Protocol (still pending
+standardization), and an implementation of the grub nx primitives using
+it.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+[rharwood: add pjones's none/nyi fixup]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/kern/efi/efi.c |  36 +++++++++++++
+ grub-core/kern/efi/mm.c  | 131 +++++++++++++++++++++++++++++++++++++++++++++++
+ include/grub/efi/api.h   |  25 +++++++++
+ include/grub/efi/efi.h   |   2 +
+ include/grub/mm.h        |  32 ++++++++++++
+ 5 files changed, 226 insertions(+)
+
+diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
+index 7fcca69c17..4ac2b2754e 100644
+--- a/grub-core/kern/efi/efi.c
++++ b/grub-core/kern/efi/efi.c
+@@ -1096,3 +1096,39 @@ grub_efi_compare_device_paths (const grub_efi_device_path_t *dp1,
+ 
+   return 0;
+ }
++
++grub_err_t
++grub_efi_status_to_err (grub_efi_status_t status)
++{
++  grub_err_t err;
++  switch (status)
++    {
++    case GRUB_EFI_SUCCESS:
++      err = GRUB_ERR_NONE;
++      break;
++    case GRUB_EFI_INVALID_PARAMETER:
++    default:
++      err = GRUB_ERR_BAD_ARGUMENT;
++      break;
++    case GRUB_EFI_OUT_OF_RESOURCES:
++      err = GRUB_ERR_OUT_OF_MEMORY;
++      break;
++    case GRUB_EFI_DEVICE_ERROR:
++      err = GRUB_ERR_IO;
++      break;
++    case GRUB_EFI_WRITE_PROTECTED:
++      err = GRUB_ERR_WRITE_ERROR;
++      break;
++    case GRUB_EFI_SECURITY_VIOLATION:
++      err = GRUB_ERR_ACCESS_DENIED;
++      break;
++    case GRUB_EFI_NOT_FOUND:
++      err = GRUB_ERR_FILE_NOT_FOUND;
++      break;
++    case GRUB_EFI_ABORTED:
++      err = GRUB_ERR_WAIT;
++      break;
++    }
++
++  return err;
++}
+diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
+index e84961d078..2c33758ed7 100644
+--- a/grub-core/kern/efi/mm.c
++++ b/grub-core/kern/efi/mm.c
+@@ -738,3 +738,134 @@ grub_efi_get_ram_base(grub_addr_t *base_addr)
+   return GRUB_ERR_NONE;
+ }
+ #endif
++
++static inline grub_uint64_t
++grub_mem_attrs_to_uefi_mem_attrs (grub_uint64_t attrs)
++{
++  grub_uint64_t ret = GRUB_EFI_MEMORY_RP |
++		      GRUB_EFI_MEMORY_RO |
++		      GRUB_EFI_MEMORY_XP;
++
++  if (attrs & GRUB_MEM_ATTR_R)
++    ret &= ~GRUB_EFI_MEMORY_RP;
++
++  if (attrs & GRUB_MEM_ATTR_W)
++    ret &= ~GRUB_EFI_MEMORY_RO;
++
++  if (attrs & GRUB_MEM_ATTR_X)
++    ret &= ~GRUB_EFI_MEMORY_XP;
++
++  return ret;
++}
++
++static inline grub_uint64_t
++uefi_mem_attrs_to_grub_mem_attrs (grub_uint64_t attrs)
++{
++  grub_uint64_t ret = GRUB_MEM_ATTR_R |
++		      GRUB_MEM_ATTR_W |
++		      GRUB_MEM_ATTR_X;
++
++  if (attrs & GRUB_EFI_MEMORY_RP)
++    ret &= ~GRUB_MEM_ATTR_R;
++
++  if (attrs & GRUB_EFI_MEMORY_RO)
++    ret &= ~GRUB_MEM_ATTR_W;
++
++  if (attrs & GRUB_EFI_MEMORY_XP)
++    ret &= ~GRUB_MEM_ATTR_X;
++
++  return ret;
++}
++
++grub_err_t
++grub_get_mem_attrs (grub_addr_t addr, grub_size_t size, grub_uint64_t *attrs)
++{
++  grub_efi_memory_attribute_protocol_t *proto;
++  grub_efi_physical_address_t physaddr = addr;
++  grub_efi_guid_t protocol_guid = GRUB_EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID;
++  grub_efi_status_t efi_status;
++
++  proto = grub_efi_locate_protocol (&protocol_guid, 0);
++  if (!proto)
++    return GRUB_ERR_NOT_IMPLEMENTED_YET;
++
++  if (physaddr & 0xfff || size & 0xfff || size == 0 || attrs == NULL)
++    {
++      grub_dprintf ("nx", "%s called on 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" and attrs %p\n",
++		    __func__, physaddr, physaddr+size-1, attrs);
++      return 0;
++    }
++
++  efi_status = efi_call_4(proto->get_memory_attributes,
++			  proto, physaddr, size, attrs);
++  *attrs = uefi_mem_attrs_to_grub_mem_attrs (*attrs);
++
++  return grub_efi_status_to_err (efi_status);
++}
++
++grub_err_t
++grub_update_mem_attrs (grub_addr_t addr, grub_size_t size,
++		       grub_uint64_t set_attrs, grub_uint64_t clear_attrs)
++{
++  grub_efi_memory_attribute_protocol_t *proto;
++  grub_efi_physical_address_t physaddr = addr;
++  grub_efi_guid_t protocol_guid = GRUB_EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID;
++  grub_efi_status_t efi_status = GRUB_EFI_SUCCESS;
++  grub_uint64_t before = 0, after = 0, uefi_set_attrs, uefi_clear_attrs;
++  grub_err_t err;
++
++  proto = grub_efi_locate_protocol (&protocol_guid, 0);
++  if (!proto)
++    return GRUB_ERR_NONE;
++
++  err = grub_get_mem_attrs (addr, size, &before);
++  if (err)
++    grub_dprintf ("nx", "grub_get_mem_attrs(0x%"PRIxGRUB_ADDR", %"PRIuGRUB_SIZE", %p) -> 0x%x\n",
++		  addr, size, &before, err);
++
++  if (physaddr & 0xfff || size & 0xfff || size == 0)
++    {
++      grub_dprintf ("nx", "%s called on 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" +%s%s%s -%s%s%s\n",
++		    __func__, physaddr, physaddr + size - 1,
++		    (set_attrs & GRUB_MEM_ATTR_R) ? "r" : "",
++		    (set_attrs & GRUB_MEM_ATTR_W) ? "w" : "",
++		    (set_attrs & GRUB_MEM_ATTR_X) ? "x" : "",
++		    (clear_attrs & GRUB_MEM_ATTR_R) ? "r" : "",
++		    (clear_attrs & GRUB_MEM_ATTR_W) ? "w" : "",
++		    (clear_attrs & GRUB_MEM_ATTR_X) ? "x" : "");
++      return 0;
++    }
++
++  uefi_set_attrs = grub_mem_attrs_to_uefi_mem_attrs (set_attrs);
++  grub_dprintf ("nx", "translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, uefi_set_attrs);
++  uefi_clear_attrs = grub_mem_attrs_to_uefi_mem_attrs (clear_attrs);
++  grub_dprintf ("nx", "translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, uefi_clear_attrs);
++  if (uefi_set_attrs)
++    efi_status = efi_call_4(proto->set_memory_attributes,
++			    proto, physaddr, size, uefi_set_attrs);
++  if (efi_status == GRUB_EFI_SUCCESS && uefi_clear_attrs)
++    efi_status = efi_call_4(proto->clear_memory_attributes,
++			    proto, physaddr, size, uefi_clear_attrs);
++
++  err = grub_get_mem_attrs (addr, size, &after);
++  if (err)
++    grub_dprintf ("nx", "grub_get_mem_attrs(0x%"PRIxGRUB_ADDR", %"PRIuGRUB_SIZE", %p) -> 0x%x\n",
++		  addr, size, &after, err);
++
++  grub_dprintf ("nx", "set +%s%s%s -%s%s%s on 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" before:%c%c%c after:%c%c%c\n",
++		(set_attrs & GRUB_MEM_ATTR_R) ? "r" : "",
++		(set_attrs & GRUB_MEM_ATTR_W) ? "w" : "",
++		(set_attrs & GRUB_MEM_ATTR_X) ? "x" : "",
++		(clear_attrs & GRUB_MEM_ATTR_R) ? "r" : "",
++		(clear_attrs & GRUB_MEM_ATTR_W) ? "w" : "",
++		(clear_attrs & GRUB_MEM_ATTR_X) ? "x" : "",
++		addr, addr + size - 1,
++		(before & GRUB_MEM_ATTR_R) ? 'r' : '-',
++		(before & GRUB_MEM_ATTR_W) ? 'w' : '-',
++		(before & GRUB_MEM_ATTR_X) ? 'x' : '-',
++		(after & GRUB_MEM_ATTR_R) ? 'r' : '-',
++		(after & GRUB_MEM_ATTR_W) ? 'w' : '-',
++		(after & GRUB_MEM_ATTR_X) ? 'x' : '-');
++
++  return grub_efi_status_to_err (efi_status);
++}
+diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
+index f431f49973..464842ba37 100644
+--- a/include/grub/efi/api.h
++++ b/include/grub/efi/api.h
+@@ -363,6 +363,11 @@
+       { 0x89, 0x29, 0x48, 0xbc, 0xd9, 0x0a, 0xd3, 0x1a } \
+   }
+ 
++#define GRUB_EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID \
++  { 0xf4560cf6, 0x40ec, 0x4b4a, \
++    { 0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89 } \
++  }
++
+ struct grub_efi_sal_system_table
+ {
+   grub_uint32_t signature;
+@@ -2102,6 +2107,26 @@ struct grub_efi_ip6_config_manual_address {
+ };
+ typedef struct grub_efi_ip6_config_manual_address grub_efi_ip6_config_manual_address_t;
+ 
++struct grub_efi_memory_attribute_protocol
++{
++  grub_efi_status_t (*get_memory_attributes) (
++			    struct grub_efi_memory_attribute_protocol *this,
++			    grub_efi_physical_address_t base_address,
++			    grub_efi_uint64_t length,
++			    grub_efi_uint64_t *attributes);
++  grub_efi_status_t (*set_memory_attributes) (
++			    struct grub_efi_memory_attribute_protocol *this,
++			    grub_efi_physical_address_t base_address,
++			    grub_efi_uint64_t length,
++			    grub_efi_uint64_t attributes);
++  grub_efi_status_t (*clear_memory_attributes) (
++			    struct grub_efi_memory_attribute_protocol *this,
++			    grub_efi_physical_address_t base_address,
++			    grub_efi_uint64_t length,
++			    grub_efi_uint64_t attributes);
++};
++typedef struct grub_efi_memory_attribute_protocol grub_efi_memory_attribute_protocol_t;
++
+ #if (GRUB_TARGET_SIZEOF_VOID_P == 4) || defined (__ia64__) \
+   || defined (__aarch64__) || defined (__MINGW64__) || defined (__CYGWIN__) \
+   || defined(__riscv)
+diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
+index ec52083c49..34825c4adc 100644
+--- a/include/grub/efi/efi.h
++++ b/include/grub/efi/efi.h
+@@ -164,4 +164,6 @@ struct grub_net_card;
+ grub_efi_handle_t
+ grub_efinet_get_device_handle (struct grub_net_card *card);
+ 
++grub_err_t EXPORT_FUNC(grub_efi_status_to_err) (grub_efi_status_t status);
++
+ #endif /* ! GRUB_EFI_EFI_HEADER */
+diff --git a/include/grub/mm.h b/include/grub/mm.h
+index 9c38dd3ca5..d81623d226 100644
+--- a/include/grub/mm.h
++++ b/include/grub/mm.h
+@@ -22,6 +22,7 @@
+ 
+ #include <grub/types.h>
+ #include <grub/symbol.h>
++#include <grub/err.h>
+ #include <config.h>
+ 
+ #ifndef NULL
+@@ -38,6 +39,37 @@ void *EXPORT_FUNC(grub_realloc) (void *ptr, grub_size_t size);
+ void *EXPORT_FUNC(grub_memalign) (grub_size_t align, grub_size_t size);
+ #endif
+ 
++#define GRUB_MEM_ATTR_R	0x0000000000000004LLU
++#define GRUB_MEM_ATTR_W	0x0000000000000002LLU
++#define GRUB_MEM_ATTR_X	0x0000000000000001LLU
++
++#ifdef GRUB_MACHINE_EFI
++grub_err_t EXPORT_FUNC(grub_get_mem_attrs) (grub_addr_t addr,
++					    grub_size_t size,
++					    grub_uint64_t *attrs);
++grub_err_t EXPORT_FUNC(grub_update_mem_attrs) (grub_addr_t addr,
++					       grub_size_t size,
++					       grub_uint64_t set_attrs,
++					       grub_uint64_t clear_attrs);
++#else /* !GRUB_MACHINE_EFI */
++static inline grub_err_t
++grub_get_mem_attrs (grub_addr_t addr __attribute__((__unused__)),
++		    grub_size_t size __attribute__((__unused__)),
++		    grub_uint64_t *attrs __attribute__((__unused__)))
++{
++  return GRUB_ERR_NONE;
++}
++
++static inline grub_err_t
++grub_update_mem_attrs (grub_addr_t addr __attribute__((__unused__)),
++		       grub_size_t size __attribute__((__unused__)),
++		       grub_uint64_t set_attrs __attribute__((__unused__)),
++		       grub_uint64_t clear_attrs __attribute__((__unused__)))
++{
++  return GRUB_ERR_NONE;
++}
++#endif /* GRUB_MACHINE_EFI */
++
+ void grub_mm_check_real (const char *file, int line);
+ #define grub_mm_check() grub_mm_check_real (GRUB_FILE, __LINE__);
+ 
diff --git a/SOURCES/0256-nx-set-page-permissions-for-loaded-modules.patch b/SOURCES/0256-nx-set-page-permissions-for-loaded-modules.patch
new file mode 100644
index 0000000..9e0aebb
--- /dev/null
+++ b/SOURCES/0256-nx-set-page-permissions-for-loaded-modules.patch
@@ -0,0 +1,263 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 21 Mar 2022 17:46:35 -0400
+Subject: [PATCH] nx: set page permissions for loaded modules.
+
+For NX, we need to set write and executable permissions on the sections
+of grub modules when we load them.
+
+On sections with SHF_ALLOC set, which is typically everything except
+.modname and the symbol and string tables, this patch clears the Read
+Only flag on sections that have the ELF flag SHF_WRITE set, and clears
+the No eXecute flag on sections with SHF_EXECINSTR set.  In all other
+cases it sets both flags.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+[rharwood: arm tgptr -> tgaddr]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/kern/dl.c | 120 +++++++++++++++++++++++++++++++++++++++-------------
+ include/grub/dl.h   |  44 +++++++++++++++++++
+ 2 files changed, 134 insertions(+), 30 deletions(-)
+
+diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
+index 8c7aacef39..d5de80186f 100644
+--- a/grub-core/kern/dl.c
++++ b/grub-core/kern/dl.c
+@@ -285,6 +285,8 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
+ #endif
+   char *ptr;
+ 
++  grub_dprintf ("modules", "loading segments for \"%s\"\n", mod->name);
++
+   arch_addralign = grub_arch_dl_min_alignment ();
+ 
+   for (i = 0, s = (const Elf_Shdr *)((const char *) e + e->e_shoff);
+@@ -384,6 +386,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
+   ptr += got;
+ #endif
+ 
++  grub_dprintf ("modules", "done loading segments for \"%s\"\n", mod->name);
+   return GRUB_ERR_NONE;
+ }
+ 
+@@ -517,23 +520,6 @@ 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.
+@@ -662,6 +648,7 @@ grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
+   Elf_Shdr *s;
+   unsigned i;
+ 
++  grub_dprintf ("modules", "relocating 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))
+@@ -670,24 +657,95 @@ grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
+ 	grub_dl_segment_t seg;
+ 	grub_err_t err;
+ 
+-	/* Find the target segment.  */
+-	for (seg = mod->segment; seg; seg = seg->next)
+-	  if (seg->section == s->sh_info)
+-	    break;
++	seg = grub_dl_find_segment(mod, s->sh_info);
++        if (!seg)
++	  continue;
+ 
+-	if (seg)
+-	  {
+-	    if (!mod->symtab)
+-	      return grub_error (GRUB_ERR_BAD_MODULE, "relocation without symbol table");
++	if (!mod->symtab)
++	  return grub_error (GRUB_ERR_BAD_MODULE, "relocation without symbol table");
+ 
+-	    err = grub_arch_dl_relocate_symbols (mod, ehdr, s, seg);
+-	    if (err)
+-	      return err;
+-	  }
++	err = grub_arch_dl_relocate_symbols (mod, ehdr, s, seg);
++	if (err)
++	  return err;
+       }
+ 
++  grub_dprintf ("modules", "done relocating symbols for \"%s\"\n", mod->name);
+   return GRUB_ERR_NONE;
+ }
++
++static grub_err_t
++grub_dl_set_mem_attrs (grub_dl_t mod, void *ehdr)
++{
++  unsigned i;
++  const Elf_Shdr *s;
++  const Elf_Ehdr *e = ehdr;
++#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
++  grub_size_t arch_addralign = grub_arch_dl_min_alignment ();
++  grub_addr_t tgaddr;
++  grub_uint64_t tgsz;
++#endif
++
++  grub_dprintf ("modules", "updating memory attributes for \"%s\"\n",
++		mod->name);
++  for (i = 0, s = (const Elf_Shdr *)((const char *) e + e->e_shoff);
++       i < e->e_shnum;
++       i++, s = (const Elf_Shdr *)((const char *) s + e->e_shentsize))
++    {
++      grub_dl_segment_t seg;
++      grub_uint64_t set_attrs = GRUB_MEM_ATTR_R;
++      grub_uint64_t clear_attrs = GRUB_MEM_ATTR_W|GRUB_MEM_ATTR_X;
++
++      seg = grub_dl_find_segment(mod, i);
++      if (!seg)
++	continue;
++
++      if (seg->size == 0 || !(s->sh_flags & SHF_ALLOC))
++	continue;
++
++      if (s->sh_flags & SHF_WRITE)
++	{
++	  set_attrs |= GRUB_MEM_ATTR_W;
++	  clear_attrs &= ~GRUB_MEM_ATTR_W;
++	}
++
++      if (s->sh_flags & SHF_EXECINSTR)
++	{
++	  set_attrs |= GRUB_MEM_ATTR_X;
++	  clear_attrs &= ~GRUB_MEM_ATTR_X;
++	}
++
++      grub_dprintf ("modules", "setting memory attrs for section \"%s\" to -%s%s%s+%s%s%s\n",
++		    grub_dl_get_section_name(e, s),
++		    (clear_attrs & GRUB_MEM_ATTR_R) ? "r" : "",
++		    (clear_attrs & GRUB_MEM_ATTR_W) ? "w" : "",
++		    (clear_attrs & GRUB_MEM_ATTR_X) ? "x" : "",
++		    (set_attrs & GRUB_MEM_ATTR_R) ? "r" : "",
++		    (set_attrs & GRUB_MEM_ATTR_W) ? "w" : "",
++		    (set_attrs & GRUB_MEM_ATTR_X) ? "x" : "");
++      grub_update_mem_attrs ((grub_addr_t)(seg->addr), seg->size, set_attrs, clear_attrs);
++    }
++
++#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
++  tgaddr = grub_min((grub_addr_t)mod->tramp, (grub_addr_t)mod->got);
++  tgsz = grub_max((grub_addr_t)mod->trampptr, (grub_addr_t)mod->gotptr) - tgaddr;
++
++  if (tgsz)
++    {
++      tgsz = ALIGN_UP(tgsz, arch_addralign);
++
++      grub_dprintf ("modules", "updating attributes for GOT and trampolines\n",
++		    mod->name);
++      grub_update_mem_attrs (tgaddr, tgsz, GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_X,
++			     GRUB_MEM_ATTR_W);
++    }
++#endif
++
++  grub_dprintf ("modules", "done updating module memory attributes for \"%s\"\n",
++		mod->name);
++
++  return GRUB_ERR_NONE;
++}
++
+ static void
+ grub_dl_print_gdb_info (grub_dl_t mod, Elf_Ehdr *e)
+ {
+@@ -753,6 +811,7 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size)
+   mod->ref_count = 1;
+ 
+   grub_dprintf ("modules", "relocating to %p\n", mod);
++
+   /* Me, Vladimir Serbinenko, hereby I add this module check as per new
+      GNU module policy. Note that this license check is informative only.
+      Modules have to be licensed under GPLv3 or GPLv3+ (optionally
+@@ -766,7 +825,8 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size)
+       || grub_dl_resolve_dependencies (mod, e)
+       || grub_dl_load_segments (mod, e)
+       || grub_dl_resolve_symbols (mod, e)
+-      || grub_dl_relocate_symbols (mod, e))
++      || grub_dl_relocate_symbols (mod, e)
++      || grub_dl_set_mem_attrs (mod, e))
+     {
+       mod->fini = 0;
+       grub_dl_unload (mod);
+diff --git a/include/grub/dl.h b/include/grub/dl.h
+index f36ed5cb17..45ac8e339f 100644
+--- a/include/grub/dl.h
++++ b/include/grub/dl.h
+@@ -27,6 +27,7 @@
+ #include <grub/elf.h>
+ #include <grub/list.h>
+ #include <grub/misc.h>
++#include <grub/mm.h>
+ #endif
+ 
+ /*
+@@ -268,6 +269,49 @@ grub_dl_is_persistent (grub_dl_t mod)
+   return mod->persistent;
+ }
+ 
++static inline const char *
++grub_dl_get_section_name (const Elf_Ehdr *e, const Elf_Shdr *s)
++{
++  Elf_Shdr *str_s;
++  const char *str;
++
++  str_s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
++  str = (char *) e + str_s->sh_offset;
++
++  return str + s->sh_name;
++}
++
++static inline 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;
++}
++
++/* Return the segment for a section of index N */
++static inline grub_dl_segment_t
++grub_dl_find_segment (grub_dl_t mod, unsigned n)
++{
++  grub_dl_segment_t seg;
++
++  for (seg = mod->segment; seg; seg = seg->next)
++    if (seg->section == n)
++      return seg;
++
++  return NULL;
++}
++
+ #endif
+ 
+ void * EXPORT_FUNC(grub_resolve_symbol) (const char *name);
diff --git a/SOURCES/0257-nx-set-attrs-in-our-kernel-loaders.patch b/SOURCES/0257-nx-set-attrs-in-our-kernel-loaders.patch
new file mode 100644
index 0000000..514df1f
--- /dev/null
+++ b/SOURCES/0257-nx-set-attrs-in-our-kernel-loaders.patch
@@ -0,0 +1,565 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 22 Mar 2022 10:57:07 -0400
+Subject: [PATCH] nx: set attrs in our kernel loaders
+
+For NX, our kernel loaders need to set write and execute page
+permissions on allocated pages and the stack.
+
+This patch adds those calls.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+[rharwood: fix stack_attrs undefined, fix aarch64 callsites]
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/kern/efi/mm.c            |  77 +++++++++++++++++
+ grub-core/loader/arm64/linux.c     |  16 +++-
+ grub-core/loader/arm64/xen_boot.c  |   4 +-
+ grub-core/loader/efi/chainloader.c |  11 +++
+ grub-core/loader/efi/linux.c       | 164 ++++++++++++++++++++++++++++++++++++-
+ grub-core/loader/i386/efi/linux.c  |  26 +++++-
+ grub-core/loader/i386/linux.c      |   5 ++
+ include/grub/efi/efi.h             |   6 +-
+ include/grub/efi/linux.h           |  16 +++-
+ include/grub/efi/pe32.h            |   2 +
+ 10 files changed, 312 insertions(+), 15 deletions(-)
+
+diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
+index 2c33758ed7..e460b072e6 100644
+--- a/grub-core/kern/efi/mm.c
++++ b/grub-core/kern/efi/mm.c
+@@ -610,6 +610,81 @@ print_memory_map (grub_efi_memory_descriptor_t *memory_map,
+ }
+ #endif
+ 
++grub_addr_t grub_stack_addr = (grub_addr_t)-1ll;
++grub_size_t grub_stack_size = 0;
++
++static void
++grub_nx_init (void)
++{
++  grub_uint64_t attrs, stack_attrs;
++  grub_err_t err;
++  grub_addr_t stack_current, stack_end;
++  const grub_uint64_t page_size = 4096;
++  const grub_uint64_t page_mask = ~(page_size - 1);
++
++  /*
++   * These are to confirm that the flags are working as expected when
++   * debugging.
++   */
++  attrs = 0;
++  stack_current = (grub_addr_t)grub_nx_init & page_mask;
++  err = grub_get_mem_attrs (stack_current, page_size, &attrs);
++  if (err)
++    {
++      grub_dprintf ("nx",
++		    "grub_get_mem_attrs(0x%"PRIxGRUB_UINT64_T", ...) -> 0x%x\n",
++		    stack_current, err);
++      grub_error_pop ();
++    }
++  else
++    grub_dprintf ("nx", "page attrs for grub_nx_init (%p) are %c%c%c\n",
++		  grub_dl_load_core,
++		  (attrs & GRUB_MEM_ATTR_R) ? 'r' : '-',
++		  (attrs & GRUB_MEM_ATTR_R) ? 'w' : '-',
++		  (attrs & GRUB_MEM_ATTR_R) ? 'x' : '-');
++
++  stack_current = (grub_addr_t)&stack_current & page_mask;
++  err = grub_get_mem_attrs (stack_current, page_size, &stack_attrs);
++  if (err)
++    {
++      grub_dprintf ("nx",
++		    "grub_get_mem_attrs(0x%"PRIxGRUB_UINT64_T", ...) -> 0x%x\n",
++		    stack_current, err);
++      grub_error_pop ();
++    }
++  else
++    {
++      attrs = stack_attrs;
++      grub_dprintf ("nx", "page attrs for stack (%p) are %c%c%c\n",
++                    &attrs,
++                    (attrs & GRUB_MEM_ATTR_R) ? 'r' : '-',
++                    (attrs & GRUB_MEM_ATTR_R) ? 'w' : '-',
++                    (attrs & GRUB_MEM_ATTR_R) ? 'x' : '-');
++    }
++  for (stack_end = stack_current + page_size ;
++       !(attrs & GRUB_MEM_ATTR_R);
++       stack_end += page_size)
++    {
++      err = grub_get_mem_attrs (stack_current, page_size, &attrs);
++      if (err)
++	{
++	  grub_dprintf ("nx",
++			"grub_get_mem_attrs(0x%"PRIxGRUB_UINT64_T", ...) -> 0x%x\n",
++			stack_current, err);
++	  grub_error_pop ();
++	  break;
++	}
++    }
++  if (stack_end > stack_current)
++    {
++      grub_stack_addr = stack_current;
++      grub_stack_size = stack_end - stack_current;
++      grub_dprintf ("nx",
++		    "detected stack from 0x%"PRIxGRUB_ADDR" to 0x%"PRIxGRUB_ADDR"\n",
++		    grub_stack_addr, grub_stack_addr + grub_stack_size - 1);
++    }
++}
++
+ void
+ grub_efi_mm_init (void)
+ {
+@@ -623,6 +698,8 @@ grub_efi_mm_init (void)
+   grub_efi_uint64_t required_pages;
+   int mm_status;
+ 
++  grub_nx_init ();
++
+   /* Prepare a memory region to store two memory maps.  */
+   memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
+   if (! memory_map)
+diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
+index cc67f43906..de85583487 100644
+--- a/grub-core/loader/arm64/linux.c
++++ b/grub-core/loader/arm64/linux.c
+@@ -172,7 +172,8 @@ free_params (void)
+ }
+ 
+ grub_err_t
+-grub_arch_efi_linux_boot_image (grub_addr_t addr, char *args)
++grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args,
++				int nx_supported)
+ {
+   grub_err_t retval;
+ 
+@@ -182,7 +183,8 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, char *args)
+ 
+   grub_dprintf ("linux", "linux command line: '%s'\n", args);
+ 
+-  retval = grub_efi_linux_boot ((char *)addr, handover_offset, (void *)addr);
++  retval = grub_efi_linux_boot (addr, size, handover_offset,
++				(void *)addr, nx_supported);
+ 
+   /* Never reached... */
+   free_params();
+@@ -192,7 +194,10 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, char *args)
+ static grub_err_t
+ grub_linux_boot (void)
+ {
+-  return (grub_arch_efi_linux_boot_image((grub_addr_t)kernel_addr, linux_args));
++  return grub_arch_efi_linux_boot_image((grub_addr_t)kernel_addr,
++					(grub_size_t)kernel_size,
++					linux_args,
++					0);
+ }
+ 
+ static grub_err_t
+@@ -340,6 +345,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   grub_off_t filelen;
+   grub_uint32_t align;
+   void *kernel = NULL;
++  int nx_supported = 1;
+ 
+   grub_dl_ref (my_mod);
+ 
+@@ -376,6 +382,10 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   grub_dprintf ("linux", "kernel entry offset : %d\n", handover_offset);
+   grub_dprintf ("linux", "kernel alignment    : 0x%x\n", align);
+ 
++  err = grub_efi_check_nx_image_support((grub_addr_t)kernel, filelen, &nx_supported);
++  if (err != GRUB_ERR_NONE)
++    goto fail;
++
+   grub_loader_unset();
+ 
+   kernel_alloc_pages = GRUB_EFI_BYTES_TO_PAGES (kernel_size + align - 1);
+diff --git a/grub-core/loader/arm64/xen_boot.c b/grub-core/loader/arm64/xen_boot.c
+index d9b7a9ba40..6e7e920416 100644
+--- a/grub-core/loader/arm64/xen_boot.c
++++ b/grub-core/loader/arm64/xen_boot.c
+@@ -266,7 +266,9 @@ xen_boot (void)
+     return err;
+ 
+   return grub_arch_efi_linux_boot_image (xen_hypervisor->start,
+-					  xen_hypervisor->cmdline);
++                                         xen_hypervisor->size,
++                                         xen_hypervisor->cmdline,
++                                         0);
+ }
+ 
+ static void
+diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
+index fb874f1855..dd31ac9bb3 100644
+--- a/grub-core/loader/efi/chainloader.c
++++ b/grub-core/loader/efi/chainloader.c
+@@ -1070,6 +1070,17 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+       goto fail;
+     }
+ 
++  /*
++   * The OS kernel is going to set its own permissions when it takes over
++   * paging a few million instructions from now, and load_image() will set up
++   * anything that's needed based on the section headers, so there's no point
++   * in doing anything but clearing the protection bits here.
++   */
++  grub_dprintf("nx", "setting attributes for %p (%lu bytes) to %llx\n",
++	       (void *)(grub_addr_t)address, fsize, 0llu);
++  grub_update_mem_attrs (address, fsize,
++			 GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_W|GRUB_MEM_ATTR_X, 0);
++
+ #if defined (__i386__) || defined (__x86_64__)
+   if (fsize >= (grub_ssize_t) sizeof (struct grub_macho_fat_header))
+     {
+diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c
+index 9265cf4200..277f352e0c 100644
+--- a/grub-core/loader/efi/linux.c
++++ b/grub-core/loader/efi/linux.c
+@@ -26,16 +26,127 @@
+ 
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wcast-align"
++#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
++
++grub_err_t
++grub_efi_check_nx_image_support (grub_addr_t kernel_addr,
++				 grub_size_t kernel_size,
++				 int *nx_supported)
++{
++  struct grub_dos_header *doshdr;
++  grub_size_t sz = sizeof (*doshdr);
++
++  struct grub_pe32_header_32 *pe32;
++  struct grub_pe32_header_64 *pe64;
++
++  int image_is_compatible = 0;
++  int is_64_bit;
++
++  if (kernel_size < sz)
++    return grub_error (GRUB_ERR_BAD_OS, N_("kernel is too small"));
++
++  doshdr = (void *)kernel_addr;
++
++  if ((doshdr->magic & 0xffff) != GRUB_DOS_MAGIC)
++    return grub_error (GRUB_ERR_BAD_OS, N_("kernel DOS magic is invalid"));
++
++  sz = doshdr->lfanew + sizeof (*pe32);
++  if (kernel_size < sz)
++    return grub_error (GRUB_ERR_BAD_OS, N_("kernel is too small"));
++
++  pe32 = (struct grub_pe32_header_32 *)(kernel_addr + doshdr->lfanew);
++  pe64 = (struct grub_pe32_header_64 *)pe32;
++
++  if (grub_memcmp (pe32->signature, GRUB_PE32_SIGNATURE,
++		   GRUB_PE32_SIGNATURE_SIZE) != 0)
++    return grub_error (GRUB_ERR_BAD_OS, N_("kernel PE magic is invalid"));
++
++  switch (pe32->coff_header.machine)
++    {
++    case GRUB_PE32_MACHINE_ARMTHUMB_MIXED:
++    case GRUB_PE32_MACHINE_I386:
++    case GRUB_PE32_MACHINE_RISCV32:
++      is_64_bit = 0;
++      break;
++    case GRUB_PE32_MACHINE_ARM64:
++    case GRUB_PE32_MACHINE_IA64:
++    case GRUB_PE32_MACHINE_RISCV64:
++    case GRUB_PE32_MACHINE_X86_64:
++      is_64_bit = 1;
++      break;
++    default:
++      return grub_error (GRUB_ERR_BAD_OS, N_("PE machine type 0x%04hx unknown"),
++			 pe32->coff_header.machine);
++    }
++
++  if (is_64_bit)
++    {
++      sz = doshdr->lfanew + sizeof (*pe64);
++      if (kernel_size < sz)
++	return grub_error (GRUB_ERR_BAD_OS, N_("kernel is too small"));
++
++      if (pe64->optional_header.dll_characteristics & GRUB_PE32_NX_COMPAT)
++	image_is_compatible = 1;
++    }
++  else
++    {
++      if (pe32->optional_header.dll_characteristics & GRUB_PE32_NX_COMPAT)
++	image_is_compatible = 1;
++    }
++
++  *nx_supported = image_is_compatible;
++  return GRUB_ERR_NONE;
++}
++
++grub_err_t
++grub_efi_check_nx_required (int *nx_required)
++{
++  grub_efi_status_t status;
++  grub_efi_guid_t guid = GRUB_EFI_SHIM_LOCK_GUID;
++  grub_size_t mok_policy_sz = 0;
++  char *mok_policy = NULL;
++  grub_uint32_t mok_policy_attrs = 0;
++
++  status = grub_efi_get_variable_with_attributes ("MokPolicy", &guid,
++						  &mok_policy_sz,
++						  (void **)&mok_policy,
++						  &mok_policy_attrs);
++  if (status == GRUB_EFI_NOT_FOUND ||
++      mok_policy_sz == 0 ||
++      mok_policy == NULL)
++    {
++      *nx_required = 0;
++      return GRUB_ERR_NONE;
++    }
++
++  *nx_required = 0;
++  if (mok_policy_sz < 1 ||
++      mok_policy_attrs != (GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS |
++			   GRUB_EFI_VARIABLE_RUNTIME_ACCESS) ||
++      (mok_policy[mok_policy_sz-1] & GRUB_MOK_POLICY_NX_REQUIRED))
++    *nx_required = 1;
++
++  return GRUB_ERR_NONE;
++}
+ 
+ typedef void (*handover_func) (void *, grub_efi_system_table_t *, void *);
+ 
+ grub_err_t
+-grub_efi_linux_boot (void *kernel_addr, grub_off_t handover_offset,
+-		     void *kernel_params)
++grub_efi_linux_boot (grub_addr_t kernel_addr, grub_size_t kernel_size,
++		     grub_off_t handover_offset, void *kernel_params,
++		     int nx_supported)
+ {
+   grub_efi_loaded_image_t *loaded_image = NULL;
+   handover_func hf;
+   int offset = 0;
++  grub_uint64_t stack_set_attrs = GRUB_MEM_ATTR_R |
++				  GRUB_MEM_ATTR_W |
++				  GRUB_MEM_ATTR_X;
++  grub_uint64_t stack_clear_attrs = 0;
++  grub_uint64_t kernel_set_attrs = stack_set_attrs;
++  grub_uint64_t kernel_clear_attrs = stack_clear_attrs;
++  grub_uint64_t attrs;
++  int nx_required = 0;
+ 
+ #ifdef __x86_64__
+   offset = 512;
+@@ -48,12 +159,57 @@ grub_efi_linux_boot (void *kernel_addr, grub_off_t handover_offset,
+    */
+   loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle);
+   if (loaded_image)
+-    loaded_image->image_base = kernel_addr;
++    loaded_image->image_base = (void *)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);
++		(void *)kernel_addr, (void *)handover_offset, kernel_params);
++
++
++  if (nx_required && !nx_supported)
++    return grub_error (GRUB_ERR_BAD_OS, N_("kernel does not support NX loading required by policy"));
++
++  if (nx_supported)
++    {
++      kernel_set_attrs &= ~GRUB_MEM_ATTR_W;
++      kernel_clear_attrs |= GRUB_MEM_ATTR_W;
++      stack_set_attrs &= ~GRUB_MEM_ATTR_X;
++      stack_clear_attrs |= GRUB_MEM_ATTR_X;
++    }
++
++  grub_dprintf ("nx", "Setting attributes for 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" to r%cx\n",
++		    kernel_addr, kernel_addr + kernel_size - 1,
++		    (kernel_set_attrs & GRUB_MEM_ATTR_W) ? 'w' : '-');
++  grub_update_mem_attrs (kernel_addr, kernel_size,
++			 kernel_set_attrs, kernel_clear_attrs);
++
++  grub_get_mem_attrs (kernel_addr, 4096, &attrs);
++  grub_dprintf ("nx", "permissions for 0x%"PRIxGRUB_ADDR" are %s%s%s\n",
++		(grub_addr_t)kernel_addr,
++		(attrs & GRUB_MEM_ATTR_R) ? "r" : "-",
++		(attrs & GRUB_MEM_ATTR_W) ? "w" : "-",
++		(attrs & GRUB_MEM_ATTR_X) ? "x" : "-");
++  if (grub_stack_addr != (grub_addr_t)-1ll)
++    {
++      grub_dprintf ("nx", "Setting attributes for stack at 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" to rw%c\n",
++		    grub_stack_addr, grub_stack_addr + grub_stack_size - 1,
++		    (stack_set_attrs & GRUB_MEM_ATTR_X) ? 'x' : '-');
++      grub_update_mem_attrs (grub_stack_addr, grub_stack_size,
++			     stack_set_attrs, stack_clear_attrs);
++
++      grub_get_mem_attrs (grub_stack_addr, 4096, &attrs);
++      grub_dprintf ("nx", "permissions for 0x%"PRIxGRUB_ADDR" are %s%s%s\n",
++		    grub_stack_addr,
++		    (attrs & GRUB_MEM_ATTR_R) ? "r" : "-",
++		    (attrs & GRUB_MEM_ATTR_W) ? "w" : "-",
++		    (attrs & GRUB_MEM_ATTR_X) ? "x" : "-");
++    }
++
++#if defined(__i386__) || defined(__x86_64__)
++  asm volatile ("cli");
++#endif
++
+   hf = (handover_func)((char *)kernel_addr + handover_offset + offset);
+   hf (grub_efi_image_handle, grub_efi_system_table, kernel_params);
+ 
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index 92b2fb5091..91ae274299 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -44,7 +44,7 @@ struct grub_linuxefi_context {
+   grub_uint32_t handover_offset;
+   struct linux_kernel_params *params;
+   char *cmdline;
+-
++  int nx_supported;
+   void *initrd_mem;
+ };
+ 
+@@ -110,13 +110,19 @@ kernel_alloc(grub_efi_uintn_t size,
+       pages = BYTES_TO_PAGES(size);
+       grub_dprintf ("linux", "Trying to allocate %lu pages from %p\n",
+ 		    (unsigned long)pages, (void *)(unsigned long)max);
++      size = pages * GRUB_EFI_PAGE_SIZE;
+ 
+       prev_max = max;
+       addr = grub_efi_allocate_pages_real (max, pages,
+ 					   max_addresses[i].alloc_type,
+ 					   memtype);
+       if (addr)
+-	grub_dprintf ("linux", "Allocated at %p\n", addr);
++	{
++	  grub_dprintf ("linux", "Allocated at %p\n", addr);
++	  grub_update_mem_attrs ((grub_addr_t)addr, size,
++				 GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_W,
++				 GRUB_MEM_ATTR_X);
++	}
+     }
+ 
+   while (grub_error_pop ())
+@@ -137,9 +143,11 @@ grub_linuxefi_boot (void *data)
+ 
+   asm volatile ("cli");
+ 
+-  return grub_efi_linux_boot ((char *)context->kernel_mem,
++  return grub_efi_linux_boot ((grub_addr_t)context->kernel_mem,
++			      context->kernel_size,
+ 			      context->handover_offset,
+-			      context->params);
++			      context->params,
++			      context->nx_supported);
+ }
+ 
+ static grub_err_t
+@@ -304,7 +312,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   grub_uint32_t handover_offset;
+   struct linux_kernel_params *params = 0;
+   char *cmdline = 0;
++  int nx_supported = 1;
+   struct grub_linuxefi_context *context = 0;
++  grub_err_t err;
+ 
+   grub_dl_ref (my_mod);
+ 
+@@ -334,6 +344,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+       goto fail;
+     }
+ 
++  err = grub_efi_check_nx_image_support ((grub_addr_t)kernel, filelen,
++					 &nx_supported);
++  if (err != GRUB_ERR_NONE)
++    return err;
++  grub_dprintf ("linux", "nx is%s supported by this kernel\n",
++		nx_supported ? "" : " not");
++
+   lh = (struct linux_i386_kernel_header *)kernel;
+   grub_dprintf ("linux", "original lh is at %p\n", kernel);
+ 
+@@ -498,6 +515,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   context->handover_offset = handover_offset;
+   context->params = params;
+   context->cmdline = cmdline;
++  context->nx_supported = nx_supported;
+ 
+   grub_loader_set_ex (grub_linuxefi_boot, grub_linuxefi_unload, context, 0);
+ 
+diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c
+index 4aeb0e4b9a..3c1ff64763 100644
+--- a/grub-core/loader/i386/linux.c
++++ b/grub-core/loader/i386/linux.c
+@@ -805,6 +805,11 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+       kernel_offset += len;
+     }
+ 
++  grub_dprintf("efi", "setting attributes for %p (%zu bytes) to +rw-x\n",
++	       &linux_params, sizeof (lh) + len);
++  grub_update_mem_attrs ((grub_addr_t)&linux_params, sizeof (lh) + len,
++			 GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_W, GRUB_MEM_ATTR_X);
++
+   linux_params.code32_start = prot_mode_target + lh.code32_start - GRUB_LINUX_BZIMAGE_ADDR;
+   linux_params.kernel_alignment = (1 << align);
+   linux_params.ps_mouse = linux_params.padding11 = 0;
+diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
+index 34825c4adc..449e55269f 100644
+--- a/include/grub/efi/efi.h
++++ b/include/grub/efi/efi.h
+@@ -140,12 +140,16 @@ extern void (*EXPORT_VAR(grub_efi_net_config)) (grub_efi_handle_t hnd,
+ 						char **device,
+ 						char **path);
+ 
++extern grub_addr_t EXPORT_VAR(grub_stack_addr);
++extern grub_size_t EXPORT_VAR(grub_stack_size);
++
+ #if defined(__arm__) || defined(__aarch64__) || defined(__riscv)
+ 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, char *args);
++grub_err_t grub_arch_efi_linux_boot_image(grub_addr_t addr, grub_size_t size,
++					  char *args, int nx_enabled);
+ #endif
+ 
+ grub_addr_t grub_efi_section_addr (const char *section);
+diff --git a/include/grub/efi/linux.h b/include/grub/efi/linux.h
+index 887b02fd9f..b82f71006a 100644
+--- a/include/grub/efi/linux.h
++++ b/include/grub/efi/linux.h
+@@ -22,8 +22,20 @@
+ #include <grub/err.h>
+ #include <grub/symbol.h>
+ 
++#define GRUB_MOK_POLICY_NX_REQUIRED   0x1
++
+ grub_err_t
+-EXPORT_FUNC(grub_efi_linux_boot) (void *kernel_address, grub_off_t offset,
+-				  void *kernel_param);
++EXPORT_FUNC(grub_efi_linux_boot) (grub_addr_t kernel_address,
++				  grub_size_t kernel_size,
++				  grub_off_t handover_offset,
++				  void *kernel_param, int nx_enabled);
++
++grub_err_t
++EXPORT_FUNC(grub_efi_check_nx_image_support) (grub_addr_t kernel_addr,
++					      grub_size_t kernel_size,
++					      int *nx_supported);
++
++grub_err_t
++EXPORT_FUNC(grub_efi_check_nx_required) (int *nx_required);
+ 
+ #endif /* ! GRUB_EFI_LINUX_HEADER */
+diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h
+index 2a5e1ee003..a5e623eb04 100644
+--- a/include/grub/efi/pe32.h
++++ b/include/grub/efi/pe32.h
+@@ -181,6 +181,8 @@ struct grub_pe32_optional_header
+   struct grub_pe32_data_directory reserved_entry;
+ };
+ 
++#define GRUB_PE32_NX_COMPAT 0x0100
++
+ struct grub_pe64_optional_header
+ {
+   grub_uint16_t magic;
diff --git a/SOURCES/0258-nx-set-the-nx-compatible-flag-in-EFI-grub-images.patch b/SOURCES/0258-nx-set-the-nx-compatible-flag-in-EFI-grub-images.patch
new file mode 100644
index 0000000..7da75a8
--- /dev/null
+++ b/SOURCES/0258-nx-set-the-nx-compatible-flag-in-EFI-grub-images.patch
@@ -0,0 +1,34 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 22 Mar 2022 10:57:20 -0400
+Subject: [PATCH] nx: set the nx compatible flag in EFI grub images
+
+For NX, we need the grub binary to announce that it is compatible with
+the NX feature.  This implies that when loading the executable grub
+image, several attributes are true:
+
+- the binary doesn't need an executable stack
+- the binary doesn't need sections to be both executable and writable
+- the binary knows how to use the EFI Memory Attributes protocol on code
+  it is loading.
+
+This patch adds a definition for the PE DLL Characteristics flag
+GRUB_PE32_NX_COMPAT, and changes grub-mkimage to set that flag.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ util/mkimage.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/util/mkimage.c b/util/mkimage.c
+index 8319e8dfbd..c3d33aaac8 100644
+--- a/util/mkimage.c
++++ b/util/mkimage.c
+@@ -1418,6 +1418,7 @@ grub_install_generate_image (const char *dir, const char *prefix,
+ 	    section = (struct grub_pe32_section_table *)(o64 + 1);
+ 	  }
+ 
++	PE_OHDR (o32, o64, dll_characteristics) = grub_host_to_target16 (GRUB_PE32_NX_COMPAT);
+ 	PE_OHDR (o32, o64, header_size) = grub_host_to_target32 (header_size);
+ 	PE_OHDR (o32, o64, entry_addr) = grub_host_to_target32 (layout.start_address);
+ 	PE_OHDR (o32, o64, image_base) = 0;
diff --git a/SOURCES/0259-grub-probe-document-the-behavior-of-multiple-v.patch b/SOURCES/0259-grub-probe-document-the-behavior-of-multiple-v.patch
new file mode 100644
index 0000000..4e9d7cc
--- /dev/null
+++ b/SOURCES/0259-grub-probe-document-the-behavior-of-multiple-v.patch
@@ -0,0 +1,25 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Fri, 15 Jul 2022 15:49:25 -0400
+Subject: [PATCH] grub-probe: document the behavior of multiple -v
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+(cherry picked from commit 51a55233eed08f7f12276afd6b3724b807a0b680)
+---
+ util/grub-probe.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/util/grub-probe.c b/util/grub-probe.c
+index c6fac732b4..ba867319a7 100644
+--- a/util/grub-probe.c
++++ b/util/grub-probe.c
+@@ -732,7 +732,8 @@ static struct argp_option options[] = {
+   {"device-map",  'm', N_("FILE"), 0,
+    N_("use FILE as the device map [default=%s]"), 0},
+   {"target",  't', N_("TARGET"), 0, 0, 0},
+-  {"verbose",     'v', 0,      0, N_("print verbose messages."), 0},
++  {"verbose",     'v', 0,      0,
++   N_("print verbose messages (pass twice to enable debug printing)."), 0},
+   {0, '0', 0, 0, N_("separate items in output using ASCII NUL characters"), 0},
+   { 0, 0, 0, 0, 0, 0 }
+ };
diff --git a/SOURCES/0260-grub_fs_probe-dprint-errors-from-filesystems.patch b/SOURCES/0260-grub_fs_probe-dprint-errors-from-filesystems.patch
new file mode 100644
index 0000000..1455ae4
--- /dev/null
+++ b/SOURCES/0260-grub_fs_probe-dprint-errors-from-filesystems.patch
@@ -0,0 +1,43 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Fri, 15 Jul 2022 15:39:41 -0400
+Subject: [PATCH] grub_fs_probe(): dprint errors from filesystems
+
+When filesystem detection fails, all that's currently debug-logged is a
+series of messages like:
+
+    grub-core/kern/fs.c:56:fs: Detecting ntfs...
+    grub-core/kern/fs.c:76:fs: ntfs detection failed.
+
+repeated for each filesystem.  Any messages provided to grub_error() by
+the filesystem are lost, and one has to break out gdb to figure out what
+went wrong.
+
+With this change, one instead sees:
+
+    grub-core/kern/fs.c:56:fs: Detecting fat...
+    grub-core/osdep/hostdisk.c:357:hostdisk: reusing open device
+    `/path/to/device'
+    grub-core/kern/fs.c:77:fs: error: invalid modification timestamp for /.
+    grub-core/kern/fs.c:79:fs: fat detection failed.
+
+in the debug prints.
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+(cherry picked from commit 838c79d658797d0662ee7f9e033e38ee88059e02)
+---
+ grub-core/kern/fs.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/grub-core/kern/fs.c b/grub-core/kern/fs.c
+index c698295bcb..b58e2ae1d2 100644
+--- a/grub-core/kern/fs.c
++++ b/grub-core/kern/fs.c
+@@ -74,6 +74,7 @@ grub_fs_probe (grub_device_t device)
+ 	  if (grub_errno == GRUB_ERR_NONE)
+ 	    return p;
+ 
++	  grub_dprintf ("fs", _("error: %s.\n"), grub_errmsg);
+ 	  grub_error_push ();
+ 	  grub_dprintf ("fs", "%s detection failed.\n", p->name);
+ 	  grub_error_pop ();
diff --git a/SOURCES/0261-fs-fat-don-t-error-when-mtime-is-0.patch b/SOURCES/0261-fs-fat-don-t-error-when-mtime-is-0.patch
new file mode 100644
index 0000000..f014f6c
--- /dev/null
+++ b/SOURCES/0261-fs-fat-don-t-error-when-mtime-is-0.patch
@@ -0,0 +1,64 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Fri, 15 Jul 2022 15:42:41 -0400
+Subject: [PATCH] fs/fat: don't error when mtime is 0
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+In the wild, we occasionally see valid ESPs where some file modification times
+are 0.  For instance:
+
+    ├── [Dec 31  1979]  EFI
+    │   ├── [Dec 31  1979]  BOOT
+    │   │   ├── [Dec 31  1979]  BOOTX64.EFI
+    │   │   └── [Dec 31  1979]  fbx64.efi
+    │   └── [Jun 27 02:41]  fedora
+    │       ├── [Dec 31  1979]  BOOTX64.CSV
+    │       ├── [Dec 31  1979]  fonts
+    │       ├── [Mar 14 03:35]  fw
+    │       │   ├── [Mar 14 03:35]  fwupd-359c1169-abd6-4a0d-8bce-e4d4713335c1.cap
+    │       │   ├── [Mar 14 03:34]  fwupd-9d255c4b-2d88-4861-860d-7ee52ade9463.cap
+    │       │   └── [Mar 14 03:34]  fwupd-b36438d8-9128-49d2-b280-487be02d948b.cap
+    │       ├── [Dec 31  1979]  fwupdx64.efi
+    │       ├── [May 10 10:47]  grub.cfg
+    │       ├── [Jun  3 12:38]  grub.cfg.new.new
+    │       ├── [May 10 10:41]  grub.cfg.old
+    │       ├── [Jun 27 02:41]  grubenv
+    │       ├── [Dec 31  1979]  grubx64.efi
+    │       ├── [Dec 31  1979]  mmx64.efi
+    │       ├── [Dec 31  1979]  shim.efi
+    │       ├── [Dec 31  1979]  shimx64.efi
+    │       └── [Dec 31  1979]  shimx64-fedora.efi
+    └── [Dec 31  1979]  FSCK0000.REC
+
+    5 directories, 17 files
+
+This causes grub-probe failure, which in turn causes grub-mkconfig
+failure.  They are valid filesystems that appear intact, and the Linux
+FAT stack is able to mount and manipulate them without complaint.
+
+The check for mtime of 0 has been present since
+20def1a3c3952982395cd7c3ea7e78638527962b ("fat: support file
+modification times").
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+(cherry picked from commit 0615c4887352e32d7bb7198e9ad0d695f9dc2c31)
+---
+ grub-core/fs/fat.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/grub-core/fs/fat.c b/grub-core/fs/fat.c
+index dd82e4ee35..ff6200c5be 100644
+--- a/grub-core/fs/fat.c
++++ b/grub-core/fs/fat.c
+@@ -1027,9 +1027,6 @@ grub_fat_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook,
+ 					  grub_le_to_cpu16 (ctxt.dir.w_date),
+ 					  &info.mtime);
+ #endif
+-      if (info.mtimeset == 0)
+-	grub_error (GRUB_ERR_OUT_OF_RANGE,
+-		    "invalid modification timestamp for %s", path);
+ 
+       if (hook (ctxt.filename, &info, hook_data))
+ 	break;
diff --git a/SOURCES/0262-Make-debug-file-show-which-file-filters-get-run.patch b/SOURCES/0262-Make-debug-file-show-which-file-filters-get-run.patch
new file mode 100644
index 0000000..78bc095
--- /dev/null
+++ b/SOURCES/0262-Make-debug-file-show-which-file-filters-get-run.patch
@@ -0,0 +1,45 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Fri, 29 Jul 2022 15:56:00 -0400
+Subject: [PATCH] Make debug=file show which file filters get run.
+
+If one of the file filters breaks things, it's hard to figure out where
+it has happened.
+
+This makes grub log which filter is being run, which makes it easier to
+figure out where you are in the sequence of events.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/kern/file.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c
+index db938e099d..868ce3b63e 100644
+--- a/grub-core/kern/file.c
++++ b/grub-core/kern/file.c
+@@ -30,6 +30,14 @@ void (*EXPORT_VAR (grub_grubnet_fini)) (void);
+ 
+ grub_file_filter_t grub_file_filters[GRUB_FILE_FILTER_MAX];
+ 
++static const char *filter_names[] = {
++    [GRUB_FILE_FILTER_VERIFY] = "GRUB_FILE_FILTER_VERIFY",
++    [GRUB_FILE_FILTER_GZIO] = "GRUB_FILE_FILTER_GZIO",
++    [GRUB_FILE_FILTER_XZIO] = "GRUB_FILE_FILTER_XZIO",
++    [GRUB_FILE_FILTER_LZOPIO] = "GRUB_FILE_FILTER_LZOPIO",
++    [GRUB_FILE_FILTER_MAX] = "GRUB_FILE_FILTER_MAX"
++};
++
+ /* Get the device part of the filename NAME. It is enclosed by parentheses.  */
+ char *
+ grub_file_get_device_name (const char *name)
+@@ -124,6 +132,9 @@ grub_file_open (const char *name, enum grub_file_type type)
+     if (grub_file_filters[filter])
+       {
+ 	last_file = file;
++	if (filter < GRUB_FILE_FILTER_MAX)
++	  grub_dprintf ("file", "Running %s file filter\n",
++			filter_names[filter]);
+ 	file = grub_file_filters[filter] (file, type);
+ 	if (file && file != last_file)
+ 	  {
diff --git a/SOURCES/0263-efi-use-enumerated-array-positions-for-our-allocatio.patch b/SOURCES/0263-efi-use-enumerated-array-positions-for-our-allocatio.patch
new file mode 100644
index 0000000..206e3a6
--- /dev/null
+++ b/SOURCES/0263-efi-use-enumerated-array-positions-for-our-allocatio.patch
@@ -0,0 +1,81 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 1 Aug 2022 14:06:30 -0400
+Subject: [PATCH] efi: use enumerated array positions for our allocation
+ choices
+
+In our kernel allocator on EFI systems, we currently have a growing
+amount of code that references the various allocation policies by
+position in the array, and of course maintenance of this code scales
+very poorly.
+
+This patch changes them to be enumerated, so they're easier to refer to
+farther along in the code without confusion.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/loader/i386/efi/linux.c | 31 ++++++++++++++++++++-----------
+ 1 file changed, 20 insertions(+), 11 deletions(-)
+
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index 91ae274299..8daa070132 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -60,17 +60,26 @@ struct allocation_choice {
+     grub_efi_allocate_type_t alloc_type;
+ };
+ 
+-static struct allocation_choice max_addresses[4] =
++enum {
++    KERNEL_PREF_ADDRESS,
++    KERNEL_4G_LIMIT,
++    KERNEL_NO_LIMIT,
++};
++
++static struct allocation_choice max_addresses[] =
+   {
+     /* the kernel overrides this one with pref_address and
+      * GRUB_EFI_ALLOCATE_ADDRESS */
+-    { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS },
++    [KERNEL_PREF_ADDRESS] =
++      { 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. */
++    [KERNEL_4G_LIMIT] =
++      { 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 }
++    [KERNEL_NO_LIMIT] =
++      { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS },
++    { NO_MEM, 0, 0 }
+   };
+ static struct allocation_choice saved_addresses[4];
+ 
+@@ -405,7 +414,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   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;
++      max_addresses[KERNEL_NO_LIMIT].addr = GRUB_EFI_MAX_USABLE_ADDRESS;
+     }
+   else
+     {
+@@ -478,11 +487,11 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   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[KERNEL_PREF_ADDRESS].addr = lh->pref_address;
++      max_addresses[KERNEL_PREF_ADDRESS].alloc_type = GRUB_EFI_ALLOCATE_ADDRESS;
+     }
+-  max_addresses[1].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
+-  max_addresses[2].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
++  max_addresses[KERNEL_4G_LIMIT].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
++  max_addresses[KERNEL_NO_LIMIT].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
+   kernel_size = lh->init_size;
+   kernel_mem = kernel_alloc (kernel_size, GRUB_EFI_RUNTIME_SERVICES_CODE,
+ 			     N_("can't allocate kernel"));
diff --git a/SOURCES/0264-efi-split-allocation-policy-for-kernel-vs-initrd-mem.patch b/SOURCES/0264-efi-split-allocation-policy-for-kernel-vs-initrd-mem.patch
new file mode 100644
index 0000000..d25cf30
--- /dev/null
+++ b/SOURCES/0264-efi-split-allocation-policy-for-kernel-vs-initrd-mem.patch
@@ -0,0 +1,127 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 1 Aug 2022 14:24:39 -0400
+Subject: [PATCH] efi: split allocation policy for kernel vs initrd memories.
+
+Currently in our kernel allocator, we use the same set of choices for
+all of our various kernel and initramfs allocations, though they do not
+have exactly the same constraints.
+
+This patch adds the concept of an allocation purpose, which currently
+can be KERNEL_MEM or INITRD_MEM, and updates kernel_alloc() calls
+appropriately, but does not change any current policy decision.  It
+also adds a few debug prints.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/loader/i386/efi/linux.c | 35 +++++++++++++++++++++++++++--------
+ 1 file changed, 27 insertions(+), 8 deletions(-)
+
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index 8daa070132..e6b8998e5e 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -55,7 +55,14 @@ struct grub_linuxefi_context {
+ 
+ #define BYTES_TO_PAGES(bytes)   (((bytes) + 0xfff) >> 12)
+ 
++typedef enum {
++    NO_MEM,
++    KERNEL_MEM,
++    INITRD_MEM,
++} kernel_alloc_purpose_t;
++
+ struct allocation_choice {
++    kernel_alloc_purpose_t purpose;
+     grub_efi_physical_address_t addr;
+     grub_efi_allocate_type_t alloc_type;
+ };
+@@ -64,6 +71,7 @@ enum {
+     KERNEL_PREF_ADDRESS,
+     KERNEL_4G_LIMIT,
+     KERNEL_NO_LIMIT,
++    INITRD_MAX_ADDRESS,
+ };
+ 
+ static struct allocation_choice max_addresses[] =
+@@ -71,14 +79,17 @@ static struct allocation_choice max_addresses[] =
+     /* the kernel overrides this one with pref_address and
+      * GRUB_EFI_ALLOCATE_ADDRESS */
+     [KERNEL_PREF_ADDRESS] =
+-      { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS },
++      { KERNEL_MEM, 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. */
+     [KERNEL_4G_LIMIT] =
+-      { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS },
++      { KERNEL_MEM, 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. */
+     [KERNEL_NO_LIMIT] =
+-      { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS },
++      { KERNEL_MEM, GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS },
++    /* this is for the initrd */
++    [INITRD_MAX_ADDRESS] =
++      { INITRD_MEM, GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS },
+     { NO_MEM, 0, 0 }
+   };
+ static struct allocation_choice saved_addresses[4];
+@@ -95,7 +106,8 @@ kernel_free(void *addr, grub_efi_uintn_t size)
+ }
+ 
+ static void *
+-kernel_alloc(grub_efi_uintn_t size,
++kernel_alloc(kernel_alloc_purpose_t purpose,
++	     grub_efi_uintn_t size,
+ 	     grub_efi_memory_type_t memtype,
+ 	     const char * const errmsg)
+ {
+@@ -108,6 +120,9 @@ kernel_alloc(grub_efi_uintn_t size,
+       grub_uint64_t max = max_addresses[i].addr;
+       grub_efi_uintn_t pages;
+ 
++      if (purpose != max_addresses[i].purpose)
++	continue;
++
+       /*
+        * 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
+@@ -261,7 +276,8 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[])
+ 	}
+     }
+ 
+-  initrd_mem = kernel_alloc(size, GRUB_EFI_RUNTIME_SERVICES_DATA,
++  grub_dprintf ("linux", "Trying to allocate initrd mem\n");
++  initrd_mem = kernel_alloc(INITRD_MEM, size, GRUB_EFI_RUNTIME_SERVICES_DATA,
+ 			    N_("can't allocate initrd"));
+   if (initrd_mem == NULL)
+     goto fail;
+@@ -422,7 +438,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+     }
+ #endif
+ 
+-  params = kernel_alloc (sizeof(*params), GRUB_EFI_RUNTIME_SERVICES_DATA,
++  params = kernel_alloc (KERNEL_MEM, sizeof(*params),
++			 GRUB_EFI_RUNTIME_SERVICES_DATA,
+ 			 "cannot allocate kernel parameters");
+   if (!params)
+     goto fail;
+@@ -445,7 +462,7 @@ 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");
+-  cmdline = kernel_alloc (lh->cmdline_size + 1,
++  cmdline = kernel_alloc (KERNEL_MEM, lh->cmdline_size + 1,
+ 			  GRUB_EFI_RUNTIME_SERVICES_DATA,
+ 			  N_("can't allocate cmdline"));
+   if (!cmdline)
+@@ -493,7 +510,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   max_addresses[KERNEL_4G_LIMIT].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
+   max_addresses[KERNEL_NO_LIMIT].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
+   kernel_size = lh->init_size;
+-  kernel_mem = kernel_alloc (kernel_size, GRUB_EFI_RUNTIME_SERVICES_CODE,
++  grub_dprintf ("linux", "Trying to allocate kernel mem\n");
++  kernel_mem = kernel_alloc (KERNEL_MEM, kernel_size,
++			     GRUB_EFI_RUNTIME_SERVICES_CODE,
+ 			     N_("can't allocate kernel"));
+   restore_addresses();
+   if (!kernel_mem)
diff --git a/SOURCES/0265-efi-allocate-the-initrd-within-the-bounds-expressed-.patch b/SOURCES/0265-efi-allocate-the-initrd-within-the-bounds-expressed-.patch
new file mode 100644
index 0000000..47e31e2
--- /dev/null
+++ b/SOURCES/0265-efi-allocate-the-initrd-within-the-bounds-expressed-.patch
@@ -0,0 +1,57 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 1 Aug 2022 14:07:50 -0400
+Subject: [PATCH] efi: allocate the initrd within the bounds expressed by the
+ kernel
+
+Currently on x86, only linux kernels built with CONFIG_RELOCATABLE for
+x86_64 can be loaded above 4G, but the maximum address for the initramfs
+is specified via a HdrS field.  This allows us to utilize that value,
+and unless loading the kernel above 4G, uses the value present there.
+If loading kernel above 4G is allowed, we assume loading the initramfs
+above 4G also works; in practice this has been true in the kernel code
+for quite some time.
+
+Resolves: rhbz#2112134
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/loader/i386/efi/linux.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index e6b8998e5e..d003b474ee 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -190,6 +190,8 @@ grub_linuxefi_unload (void *data)
+   cmd_initrdefi->data = 0;
+   grub_free (context);
+ 
++  max_addresses[INITRD_MAX_ADDRESS].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
++
+   return GRUB_ERR_NONE;
+ }
+ 
+@@ -426,11 +428,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+     }
+ #endif
+ 
++  max_addresses[INITRD_MAX_ADDRESS].addr = lh->initrd_addr_max;
+ #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[KERNEL_NO_LIMIT].addr = GRUB_EFI_MAX_USABLE_ADDRESS;
++      max_addresses[INITRD_MAX_ADDRESS].addr = GRUB_EFI_MAX_USABLE_ADDRESS;
+     }
+   else
+     {
+@@ -560,6 +564,8 @@ fail:
+ 
+   grub_dl_unref (my_mod);
+ 
++  max_addresses[INITRD_MAX_ADDRESS].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
++
+   if (lh)
+     kernel_free (cmdline, lh->cmdline_size + 1);
+ 
diff --git a/SOURCES/0266-efi-use-EFI_LOADER_-CODE-DATA-for-kernel-and-initrd-.patch b/SOURCES/0266-efi-use-EFI_LOADER_-CODE-DATA-for-kernel-and-initrd-.patch
new file mode 100644
index 0000000..8451dbf
--- /dev/null
+++ b/SOURCES/0266-efi-use-EFI_LOADER_-CODE-DATA-for-kernel-and-initrd-.patch
@@ -0,0 +1,61 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 1 Aug 2022 13:04:43 -0400
+Subject: [PATCH] efi: use EFI_LOADER_(CODE|DATA) for kernel and initrd
+ allocations
+
+At some point due to an erroneous kernel warning, we switched kernel and
+initramfs to being loaded in EFI_RUNTIME_SERVICES_CODE and
+EFI_RUNTIME_SERVICES_DATA memory pools.  This doesn't appear to be
+correct according to the spec, and that kernel warning has gone away.
+
+This patch puts them back in EFI_LOADER_CODE and EFI_LOADER_DATA
+allocations, respectively.
+
+Resolves: rhbz#2108456
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+---
+ grub-core/loader/i386/efi/linux.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
+index d003b474ee..ac5ef50bdb 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -279,7 +279,7 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[])
+     }
+ 
+   grub_dprintf ("linux", "Trying to allocate initrd mem\n");
+-  initrd_mem = kernel_alloc(INITRD_MEM, size, GRUB_EFI_RUNTIME_SERVICES_DATA,
++  initrd_mem = kernel_alloc(INITRD_MEM, size, GRUB_EFI_LOADER_DATA,
+ 			    N_("can't allocate initrd"));
+   if (initrd_mem == NULL)
+     goto fail;
+@@ -443,7 +443,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ #endif
+ 
+   params = kernel_alloc (KERNEL_MEM, sizeof(*params),
+-			 GRUB_EFI_RUNTIME_SERVICES_DATA,
++			 GRUB_EFI_LOADER_DATA,
+ 			 "cannot allocate kernel parameters");
+   if (!params)
+     goto fail;
+@@ -467,7 +467,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 
+   grub_dprintf ("linux", "setting up cmdline\n");
+   cmdline = kernel_alloc (KERNEL_MEM, lh->cmdline_size + 1,
+-			  GRUB_EFI_RUNTIME_SERVICES_DATA,
++			  GRUB_EFI_LOADER_DATA,
+ 			  N_("can't allocate cmdline"));
+   if (!cmdline)
+     goto fail;
+@@ -516,7 +516,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   kernel_size = lh->init_size;
+   grub_dprintf ("linux", "Trying to allocate kernel mem\n");
+   kernel_mem = kernel_alloc (KERNEL_MEM, kernel_size,
+-			     GRUB_EFI_RUNTIME_SERVICES_CODE,
++			     GRUB_EFI_LOADER_CODE,
+ 			     N_("can't allocate kernel"));
+   restore_addresses();
+   if (!kernel_mem)
diff --git a/SOURCES/0267-BLS-create-etc-kernel-cmdline-during-mkconfig.patch b/SOURCES/0267-BLS-create-etc-kernel-cmdline-during-mkconfig.patch
new file mode 100644
index 0000000..50ba0fb
--- /dev/null
+++ b/SOURCES/0267-BLS-create-etc-kernel-cmdline-during-mkconfig.patch
@@ -0,0 +1,27 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Tue, 2 Aug 2022 15:56:28 -0400
+Subject: [PATCH] BLS: create /etc/kernel/cmdline during mkconfig
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ util/grub.d/10_linux.in | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
+index 865af3d6c4..9ebff661a9 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -161,6 +161,12 @@ update_bls_cmdline()
+     local cmdline="root=${LINUX_ROOT_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}"
+     local -a files=($(get_sorted_bls))
+ 
++    if [[ ! -f /etc/kernel/cmdline ]]; then
++	# anaconda has the correct information to do this during install;
++	# afterward, grubby will take care of syncing on updates.
++	echo "$cmdline rhgb quiet" > /etc/kernel/cmdline
++    fi
++
+     for bls in "${files[@]}"; do
+         local options="${cmdline}"
+         if [ -z "${bls##*debug*}" ]; then
diff --git a/SOURCES/0268-squish-don-t-dup-rhgb-quiet-check-mtimes.patch b/SOURCES/0268-squish-don-t-dup-rhgb-quiet-check-mtimes.patch
new file mode 100644
index 0000000..67073ec
--- /dev/null
+++ b/SOURCES/0268-squish-don-t-dup-rhgb-quiet-check-mtimes.patch
@@ -0,0 +1,35 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Wed, 17 Aug 2022 10:26:07 -0400
+Subject: [PATCH] squish: don't dup rhgb quiet, check mtimes
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ util/grub.d/10_linux.in | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
+index 9ebff661a9..41c6cb1dc2 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -161,10 +161,16 @@ update_bls_cmdline()
+     local cmdline="root=${LINUX_ROOT_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}"
+     local -a files=($(get_sorted_bls))
+ 
+-    if [[ ! -f /etc/kernel/cmdline ]]; then
+-	# anaconda has the correct information to do this during install;
+-	# afterward, grubby will take care of syncing on updates.
+-	echo "$cmdline rhgb quiet" > /etc/kernel/cmdline
++    if [[ ! -f /etc/kernel/cmdline ]] ||
++	   [[ /etc/kernel/cmdline -ot /etc/default/grub ]]; then
++	# anaconda has the correct information to create this during install;
++	# afterward, grubby will take care of syncing on updates.  If the user
++	# has modified /etc/default/grub, try to cope.
++	if [[ ! "$cmdline" =~ "rhgb quiet" ]]; then
++	    # ensure these only show up once
++	    cmdline="$cmdline rhgb quiet"
++	fi
++	echo "$cmdline" > /etc/kernel/cmdline
+     fi
+ 
+     for bls in "${files[@]}"; do
diff --git a/SOURCES/0269-squish-give-up-on-rhgb-quiet.patch b/SOURCES/0269-squish-give-up-on-rhgb-quiet.patch
new file mode 100644
index 0000000..5858c91
--- /dev/null
+++ b/SOURCES/0269-squish-give-up-on-rhgb-quiet.patch
@@ -0,0 +1,25 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Wed, 17 Aug 2022 11:30:30 -0400
+Subject: [PATCH] squish: give up on rhgb quiet
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ util/grub.d/10_linux.in | 4 ----
+ 1 file changed, 4 deletions(-)
+
+diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
+index 41c6cb1dc2..5d1fa072f2 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -166,10 +166,6 @@ update_bls_cmdline()
+ 	# anaconda has the correct information to create this during install;
+ 	# afterward, grubby will take care of syncing on updates.  If the user
+ 	# has modified /etc/default/grub, try to cope.
+-	if [[ ! "$cmdline" =~ "rhgb quiet" ]]; then
+-	    # ensure these only show up once
+-	    cmdline="$cmdline rhgb quiet"
+-	fi
+ 	echo "$cmdline" > /etc/kernel/cmdline
+     fi
+ 
diff --git a/SOURCES/0270-squish-BLS-only-write-etc-kernel-cmdline-if-writable.patch b/SOURCES/0270-squish-BLS-only-write-etc-kernel-cmdline-if-writable.patch
new file mode 100644
index 0000000..d5ac923
--- /dev/null
+++ b/SOURCES/0270-squish-BLS-only-write-etc-kernel-cmdline-if-writable.patch
@@ -0,0 +1,57 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jonathan Lebon <jonathan@jlebon.com>
+Date: Wed, 17 Aug 2022 10:26:03 -0400
+Subject: [PATCH] squish: BLS: only write /etc/kernel/cmdline if writable
+
+On OSTree systems, `grub2-mkconfig` is run with `/etc` mounted read-only
+because as part of the promise of transactional updates, we want to make
+sure that we're not modifying the current deployment's state (`/etc` or
+`/var`).
+
+This conflicts with 0837dcdf1 ("BLS: create /etc/kernel/cmdline during
+mkconfig") which wants to write to `/etc/kernel/cmdline`. I'm not
+exactly sure on the background there, but based on the comment I think
+the intent is to fulfill grubby's expectation that the file exists.
+
+However, in systems like Silverblue, kernel arguments are managed by the
+rpm-ostree stack and grubby is not shipped at all.
+
+Adjust the script slightly so that we only write `/etc/kernel/cmdline`
+if the parent directory is writable.
+
+In the future, we're hoping to simplify things further on rpm-ostree
+systems by not running `grub2-mkconfig` at all since libostree already
+directly writes BLS entries. Doing that would also have avoided this,
+but ratcheting it into existing systems needs more careful thought.
+
+Signed-off-by: Jonathan Lebon <jonathan@jlebon.com>
+
+Fixes: https://github.com/fedora-silverblue/issue-tracker/issues/322
+---
+ util/grub.d/10_linux.in | 13 +++++++------
+ 1 file changed, 7 insertions(+), 6 deletions(-)
+
+diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
+index 5d1fa072f2..4795a63b4c 100644
+--- a/util/grub.d/10_linux.in
++++ b/util/grub.d/10_linux.in
+@@ -161,12 +161,13 @@ update_bls_cmdline()
+     local cmdline="root=${LINUX_ROOT_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}"
+     local -a files=($(get_sorted_bls))
+ 
+-    if [[ ! -f /etc/kernel/cmdline ]] ||
+-	   [[ /etc/kernel/cmdline -ot /etc/default/grub ]]; then
+-	# anaconda has the correct information to create this during install;
+-	# afterward, grubby will take care of syncing on updates.  If the user
+-	# has modified /etc/default/grub, try to cope.
+-	echo "$cmdline" > /etc/kernel/cmdline
++    if [ -w /etc/kernel ] &&
++           [[ ! -f /etc/kernel/cmdline ||
++                  /etc/kernel/cmdline -ot /etc/default/grub ]]; then
++        # anaconda has the correct information to create this during install;
++        # afterward, grubby will take care of syncing on updates.  If the user
++        # has modified /etc/default/grub, try to cope.
++        echo "$cmdline" > /etc/kernel/cmdline
+     fi
+ 
+     for bls in "${files[@]}"; do
diff --git a/SOURCES/0271-blscfg-Don-t-root-device-in-emu-builds.patch b/SOURCES/0271-blscfg-Don-t-root-device-in-emu-builds.patch
new file mode 100644
index 0000000..3fe8baf
--- /dev/null
+++ b/SOURCES/0271-blscfg-Don-t-root-device-in-emu-builds.patch
@@ -0,0 +1,27 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Thu, 25 Aug 2022 17:57:55 -0400
+Subject: [PATCH] blscfg: Don't root device in emu builds
+
+Otherwise, we end up looking for kernel/initrd in /boot/boot which
+doesn't work at all.  Non-emu builds need to be looking in
+($root)/boot/, which is what this is for.
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/commands/blscfg.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/commands/blscfg.c b/grub-core/commands/blscfg.c
+index e907a6a5d2..dbd0899acf 100644
+--- a/grub-core/commands/blscfg.c
++++ b/grub-core/commands/blscfg.c
+@@ -41,7 +41,7 @@ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+ #define GRUB_BLS_CONFIG_PATH "/loader/entries/"
+ #ifdef GRUB_MACHINE_EMU
+-#define GRUB_BOOT_DEVICE "/boot"
++#define GRUB_BOOT_DEVICE ""
+ #else
+ #define GRUB_BOOT_DEVICE "($root)"
+ #endif
diff --git a/SOURCES/0272-loader-arm64-linux-Remove-magic-number-header-field-.patch b/SOURCES/0272-loader-arm64-linux-Remove-magic-number-header-field-.patch
new file mode 100644
index 0000000..faaa071
--- /dev/null
+++ b/SOURCES/0272-loader-arm64-linux-Remove-magic-number-header-field-.patch
@@ -0,0 +1,43 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Ard Biesheuvel <ardb@kernel.org>
+Date: Thu, 11 Aug 2022 16:51:57 +0200
+Subject: [PATCH] loader/arm64/linux: Remove magic number header field check
+
+The "ARM\x64" magic number in the file header identifies an image as one
+that implements the bare metal boot protocol, allowing the loader to
+simply move the file to a suitably aligned address in memory, with
+sufficient headroom for the trailing .bss segment (the required memory
+size is described in the header as well).
+
+Note of this matters for GRUB, as it only supports EFI boot. EFI does
+not care about this magic number, and nor should GRUB: this prevents us
+from booting other PE linux images, such as the generic EFI zboot
+decompressor, which is a pure PE/COFF image, and does not implement the
+bare metal boot protocol.
+
+So drop the magic number check.
+
+Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+Resolves: rhbz#2125069
+Signed-off-by: Jeremy Linton <jlinton@redhat.com>
+(cherry-picked from commit 69edb31205602c29293a8c6e67363bba2a4a1e66)
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/loader/arm64/linux.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
+index de85583487..489d0c7173 100644
+--- a/grub-core/loader/arm64/linux.c
++++ b/grub-core/loader/arm64/linux.c
+@@ -55,9 +55,6 @@ static grub_addr_t initrd_end;
+ grub_err_t
+ grub_arch_efi_linux_check_image (struct linux_arch_kernel_header * lh)
+ {
+-  if (lh->magic != GRUB_LINUX_ARMXX_MAGIC_SIGNATURE)
+-    return grub_error(GRUB_ERR_BAD_OS, "invalid magic number");
+-
+   if ((lh->code0 & 0xffff) != GRUB_DOS_MAGIC)
+     return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ 		       N_("plain image kernel not supported - rebuild with CONFIG_(U)EFI_STUB enabled"));
diff --git a/SOURCES/0273-Correct-BSS-zeroing-on-aarch64.patch b/SOURCES/0273-Correct-BSS-zeroing-on-aarch64.patch
new file mode 100644
index 0000000..4f9a2b7
--- /dev/null
+++ b/SOURCES/0273-Correct-BSS-zeroing-on-aarch64.patch
@@ -0,0 +1,95 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jeremy Linton <jeremy.linton@arm.com>
+Date: Tue, 6 Sep 2022 15:33:03 -0500
+Subject: [PATCH] Correct BSS zeroing on aarch64
+
+The aarch64 loader doesn't use efi bootservices, and
+therefor it has a very minimal loader which makes a lot
+of assumptions about the kernel layout. With the ZBOOT
+changes, the layout has changed a bit and we not should
+really be parsing the PE sections to determine how much
+data to copy, otherwise the BSS won't be setup properly.
+
+This code still makes a lot of assumptions about the
+the kernel layout, so its far from ideal, but it works.
+
+Resolves: rhbz#2125069
+
+Signed-off-by: Jeremy Linton <jeremy.linton@arm.com>
+---
+ grub-core/loader/arm64/linux.c | 27 ++++++++++++++++++++++-----
+ 1 file changed, 22 insertions(+), 5 deletions(-)
+
+diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
+index 489d0c7173..419f2201df 100644
+--- a/grub-core/loader/arm64/linux.c
++++ b/grub-core/loader/arm64/linux.c
+@@ -316,10 +316,12 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+ static grub_err_t
+ parse_pe_header (void *kernel, grub_uint64_t *total_size,
+ 		 grub_uint32_t *entry_offset,
+-		 grub_uint32_t *alignment)
++		 grub_uint32_t *alignment,grub_uint32_t *code_size)
+ {
+   struct linux_arch_kernel_header *lh = kernel;
+   struct grub_armxx_linux_pe_header *pe;
++  grub_uint16_t i;
++  struct grub_pe32_section_table *sections;
+ 
+   pe = (void *)((unsigned long)kernel + lh->hdr_offset);
+ 
+@@ -329,6 +331,19 @@ parse_pe_header (void *kernel, grub_uint64_t *total_size,
+   *total_size   = pe->opt.image_size;
+   *entry_offset = pe->opt.entry_addr;
+   *alignment    = pe->opt.section_alignment;
++  *code_size    = pe->opt.section_alignment;
++
++  sections = (struct grub_pe32_section_table *) ((char *)&pe->opt +
++						 pe->coff.optional_header_size);
++  grub_dprintf ("linux", "num_sections     : %d\n",  pe->coff.num_sections );
++  for (i = 0 ; i < pe->coff.num_sections; i++)
++    {
++      grub_dprintf ("linux", "raw_size   : %lld\n",
++		    (long long) sections[i].raw_data_size);
++      grub_dprintf ("linux", "virt_size  : %lld\n",
++		    (long long) sections[i].virtual_size);
++      *code_size += sections[i].raw_data_size;
++    }
+ 
+   return GRUB_ERR_NONE;
+ }
+@@ -341,6 +356,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   grub_err_t err;
+   grub_off_t filelen;
+   grub_uint32_t align;
++  grub_uint32_t code_size;
+   void *kernel = NULL;
+   int nx_supported = 1;
+ 
+@@ -373,11 +389,12 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ 
+   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)
++  if (parse_pe_header (kernel, &kernel_size, &handover_offset, &align, &code_size) != 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_dprintf ("linux", "kernel size         : 0x%x\n", code_size);
+ 
+   err = grub_efi_check_nx_image_support((grub_addr_t)kernel, filelen, &nx_supported);
+   if (err != GRUB_ERR_NONE)
+@@ -396,9 +413,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+   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_memcpy (kernel_addr, kernel, grub_min(code_size, kernel_size));
++  if (kernel_size > code_size)
++    grub_memset ((char *)kernel_addr + code_size, 0, kernel_size - code_size);
+   grub_free(kernel);
+   kernel = NULL;
+ 
diff --git a/SOURCES/0274-linuxefi-Invalidate-i-cache-before-starting-the-kern.patch b/SOURCES/0274-linuxefi-Invalidate-i-cache-before-starting-the-kern.patch
new file mode 100644
index 0000000..eff155d
--- /dev/null
+++ b/SOURCES/0274-linuxefi-Invalidate-i-cache-before-starting-the-kern.patch
@@ -0,0 +1,39 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: dann frazier <dann.frazier@canonical.com>
+Date: Thu, 25 Aug 2022 17:08:09 -0600
+Subject: [PATCH] linuxefi: Invalidate i-cache before starting the kernel
+
+We need to flush the memory range of the code we are about to execute
+from the instruction cache before we can safely execute it. Not doing
+so appears to be the source of rare synchronous exceptions a user
+is seeing on a Cortex-A72-based platform while executing the Linux EFI
+stub. Notably they seem to correlate with an instruction on a cache
+line boundary.
+
+Signed-off-by: dann frazier <dann.frazier@canonical.com>
+---
+ grub-core/loader/efi/linux.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c
+index 277f352e0c..e413bdcc23 100644
+--- a/grub-core/loader/efi/linux.c
++++ b/grub-core/loader/efi/linux.c
+@@ -16,6 +16,7 @@
+  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ 
++#include <grub/cache.h>
+ #include <grub/err.h>
+ #include <grub/mm.h>
+ #include <grub/types.h>
+@@ -210,6 +211,9 @@ grub_efi_linux_boot (grub_addr_t kernel_addr, grub_size_t kernel_size,
+   asm volatile ("cli");
+ #endif
+ 
++  /* Invalidate the instruction cache */
++  grub_arch_sync_caches((void *)kernel_addr, kernel_size);
++
+   hf = (handover_func)((char *)kernel_addr + handover_offset + offset);
+   hf (grub_efi_image_handle, grub_efi_system_table, kernel_params);
+ 
diff --git a/SOURCES/0275-x86-efi-Fix-an-incorrect-array-size-in-kernel-alloca.patch b/SOURCES/0275-x86-efi-Fix-an-incorrect-array-size-in-kernel-alloca.patch
new file mode 100644
index 0000000..0079750
--- /dev/null
+++ b/SOURCES/0275-x86-efi-Fix-an-incorrect-array-size-in-kernel-alloca.patch
@@ -0,0 +1,36 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Tue, 11 Oct 2022 17:00:50 -0400
+Subject: [PATCH] x86-efi: Fix an incorrect array size in kernel allocation
+
+In 81a6ebf62bbe166ddc968463df2e8bd481bf697c ("efi: split allocation
+policy for kernel vs initrd memories."), I introduced a split in the
+kernel allocator to allow for different dynamic policies for the kernel
+and the initrd allocations.
+
+Unfortunately, that change increased the size of the policy data used to
+make decisions, but did not change the size of the temporary storage we
+use to back it up and restore.  This results in some of .data getting
+clobbered at runtime, and hilarity ensues.
+
+This patch makes the size of the backup storage be based on the size of
+the initial policy data.
+
+Signed-off-by: Peter Jones <pjones@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 ac5ef50bdb..9854b0defa 100644
+--- a/grub-core/loader/i386/efi/linux.c
++++ b/grub-core/loader/i386/efi/linux.c
+@@ -92,7 +92,7 @@ static struct allocation_choice max_addresses[] =
+       { INITRD_MEM, GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS },
+     { NO_MEM, 0, 0 }
+   };
+-static struct allocation_choice saved_addresses[4];
++static struct allocation_choice saved_addresses[sizeof(max_addresses) / sizeof(max_addresses[0])];
+ 
+ #define save_addresses() grub_memcpy(saved_addresses, max_addresses, sizeof(max_addresses))
+ #define restore_addresses() grub_memcpy(max_addresses, saved_addresses, sizeof(max_addresses))
diff --git a/SOURCES/0276-commands-efi-tpm-Refine-the-status-of-log-event.patch b/SOURCES/0276-commands-efi-tpm-Refine-the-status-of-log-event.patch
new file mode 100644
index 0000000..896e49b
--- /dev/null
+++ b/SOURCES/0276-commands-efi-tpm-Refine-the-status-of-log-event.patch
@@ -0,0 +1,42 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Lu Ken <ken.lu@intel.com>
+Date: Wed, 13 Jul 2022 10:06:10 +0800
+Subject: [PATCH] commands/efi/tpm: Refine the status of log event
+
+1. Use macro GRUB_ERR_NONE instead of hard code 0.
+2. Keep lowercase of the first char for the status string of log event.
+
+Signed-off-by: Lu Ken <ken.lu@intel.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 922898573e37135f5dedc16f3e15a1d1d4c53f8a)
+---
+ grub-core/commands/efi/tpm.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/grub-core/commands/efi/tpm.c b/grub-core/commands/efi/tpm.c
+index a97d85368a..7acf510499 100644
+--- a/grub-core/commands/efi/tpm.c
++++ b/grub-core/commands/efi/tpm.c
+@@ -135,17 +135,17 @@ grub_efi_log_event_status (grub_efi_status_t status)
+   switch (status)
+     {
+     case GRUB_EFI_SUCCESS:
+-      return 0;
++      return GRUB_ERR_NONE;
+     case GRUB_EFI_DEVICE_ERROR:
+-      return grub_error (GRUB_ERR_IO, N_("Command failed"));
++      return grub_error (GRUB_ERR_IO, N_("command failed"));
+     case GRUB_EFI_INVALID_PARAMETER:
+-      return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid parameter"));
++      return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid parameter"));
+     case GRUB_EFI_BUFFER_TOO_SMALL:
+-      return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Output buffer too small"));
++      return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("output buffer too small"));
+     case GRUB_EFI_NOT_FOUND:
+       return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("TPM unavailable"));
+     default:
+-      return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("Unknown TPM error"));
++      return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("unknown TPM error"));
+     }
+ }
+ 
diff --git a/SOURCES/0277-commands-efi-tpm-Use-grub_strcpy-instead-of-grub_mem.patch b/SOURCES/0277-commands-efi-tpm-Use-grub_strcpy-instead-of-grub_mem.patch
new file mode 100644
index 0000000..e04dcbc
--- /dev/null
+++ b/SOURCES/0277-commands-efi-tpm-Use-grub_strcpy-instead-of-grub_mem.patch
@@ -0,0 +1,37 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Lu Ken <ken.lu@intel.com>
+Date: Wed, 13 Jul 2022 10:06:11 +0800
+Subject: [PATCH] commands/efi/tpm: Use grub_strcpy() instead of grub_memcpy()
+
+The event description is a string, so using grub_strcpy() is cleaner than
+using grub_memcpy().
+
+Signed-off-by: Lu Ken <ken.lu@intel.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit ef8679b645a63eb9eb191bb9539d7d25a9d6ff3b)
+---
+ grub-core/commands/efi/tpm.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/commands/efi/tpm.c b/grub-core/commands/efi/tpm.c
+index 7acf510499..bb59599721 100644
+--- a/grub-core/commands/efi/tpm.c
++++ b/grub-core/commands/efi/tpm.c
+@@ -175,7 +175,7 @@ grub_tpm1_log_event (grub_efi_handle_t tpm_handle, unsigned char *buf,
+   event->PCRIndex = pcr;
+   event->EventType = EV_IPL;
+   event->EventSize = grub_strlen (description) + 1;
+-  grub_memcpy (event->Event, description, event->EventSize);
++  grub_strcpy ((char *) event->Event, description);
+ 
+   algorithm = TCG_ALG_SHA;
+   status = efi_call_7 (tpm->log_extend_event, tpm, (grub_addr_t) buf, (grub_uint64_t) size,
+@@ -212,7 +212,7 @@ grub_tpm2_log_event (grub_efi_handle_t tpm_handle, unsigned char *buf,
+   event->Header.EventType = EV_IPL;
+   event->Size =
+     sizeof (*event) - sizeof (event->Event) + grub_strlen (description) + 1;
+-  grub_memcpy (event->Event, description, grub_strlen (description) + 1);
++  grub_strcpy ((char *) event->Event, description);
+ 
+   status = efi_call_5 (tpm->hash_log_extend_event, tpm, 0, (grub_addr_t) buf,
+ 		       (grub_uint64_t) size, event);
diff --git a/SOURCES/0278-efi-tpm-Add-EFI_CC_MEASUREMENT_PROTOCOL-support.patch b/SOURCES/0278-efi-tpm-Add-EFI_CC_MEASUREMENT_PROTOCOL-support.patch
new file mode 100644
index 0000000..610c81a
--- /dev/null
+++ b/SOURCES/0278-efi-tpm-Add-EFI_CC_MEASUREMENT_PROTOCOL-support.patch
@@ -0,0 +1,258 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Lu Ken <ken.lu@intel.com>
+Date: Wed, 13 Jul 2022 10:06:12 +0800
+Subject: [PATCH] efi/tpm: Add EFI_CC_MEASUREMENT_PROTOCOL support
+
+The EFI_CC_MEASUREMENT_PROTOCOL abstracts the measurement for virtual firmware
+in confidential computing environment. It is similar to the EFI_TCG2_PROTOCOL.
+It was proposed by Intel and ARM and approved by UEFI organization.
+
+It is defined in Intel GHCI specification: https://cdrdv2.intel.com/v1/dl/getContent/726790 .
+The EDKII header file is available at https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/CcMeasurement.h .
+
+Signed-off-by: Lu Ken <ken.lu@intel.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 4c76565b6cb885b7e144dc27f3612066844e2d19)
+---
+ grub-core/commands/efi/tpm.c |  48 ++++++++++++++
+ include/grub/efi/cc.h        | 151 +++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 199 insertions(+)
+ create mode 100644 include/grub/efi/cc.h
+
+diff --git a/grub-core/commands/efi/tpm.c b/grub-core/commands/efi/tpm.c
+index bb59599721..ae09c1bf8b 100644
+--- a/grub-core/commands/efi/tpm.c
++++ b/grub-core/commands/efi/tpm.c
+@@ -22,6 +22,7 @@
+ #include <grub/i18n.h>
+ #include <grub/efi/api.h>
+ #include <grub/efi/efi.h>
++#include <grub/efi/cc.h>
+ #include <grub/efi/tpm.h>
+ #include <grub/mm.h>
+ #include <grub/tpm.h>
+@@ -31,6 +32,7 @@ typedef TCG_PCR_EVENT grub_tpm_event_t;
+ 
+ static grub_efi_guid_t tpm_guid = EFI_TPM_GUID;
+ static grub_efi_guid_t tpm2_guid = EFI_TPM2_GUID;
++static grub_efi_guid_t cc_measurement_guid = GRUB_EFI_CC_MEASUREMENT_PROTOCOL_GUID;
+ 
+ static grub_efi_handle_t *grub_tpm_handle;
+ static grub_uint8_t grub_tpm_version;
+@@ -221,6 +223,50 @@ grub_tpm2_log_event (grub_efi_handle_t tpm_handle, unsigned char *buf,
+   return grub_efi_log_event_status (status);
+ }
+ 
++static void
++grub_cc_log_event (unsigned char *buf, grub_size_t size, grub_uint8_t pcr,
++		   const char *description)
++{
++  grub_efi_cc_event_t *event;
++  grub_efi_status_t status;
++  grub_efi_cc_protocol_t *cc;
++  grub_efi_cc_mr_index_t mr;
++
++  cc = grub_efi_locate_protocol (&cc_measurement_guid, NULL);
++  if (cc == NULL)
++    return;
++
++  status = efi_call_3 (cc->map_pcr_to_mr_index, cc, pcr, &mr);
++  if (status != GRUB_EFI_SUCCESS)
++    {
++      grub_efi_log_event_status (status);
++      return;
++    }
++
++  event = grub_zalloc (sizeof (grub_efi_cc_event_t) +
++		       grub_strlen (description) + 1);
++  if (event == NULL)
++    {
++      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate CC event buffer"));
++      return;
++    }
++
++  event->Header.HeaderSize = sizeof (grub_efi_cc_event_header_t);
++  event->Header.HeaderVersion = GRUB_EFI_CC_EVENT_HEADER_VERSION;
++  event->Header.MrIndex = mr;
++  event->Header.EventType = EV_IPL;
++  event->Size = sizeof (*event) + grub_strlen (description) + 1;
++  grub_strcpy ((char *) event->Event, description);
++
++  status = efi_call_5 (cc->hash_log_extend_event, cc, 0,
++		       (grub_efi_physical_address_t)(grub_addr_t) buf,
++		       (grub_efi_uint64_t) size, event);
++  grub_free (event);
++
++  if (status != GRUB_EFI_SUCCESS)
++    grub_efi_log_event_status (status);
++}
++
+ grub_err_t
+ grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr,
+ 		    const char *description)
+@@ -228,6 +274,8 @@ grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr,
+   grub_efi_handle_t tpm_handle;
+   grub_efi_uint8_t protocol_version;
+ 
++  grub_cc_log_event(buf, size, pcr, description);
++
+   if (!grub_tpm_handle_find (&tpm_handle, &protocol_version))
+     return 0;
+ 
+diff --git a/include/grub/efi/cc.h b/include/grub/efi/cc.h
+new file mode 100644
+index 0000000000..8960306890
+--- /dev/null
++++ b/include/grub/efi/cc.h
+@@ -0,0 +1,151 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2022  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_CC_H
++#define GRUB_EFI_CC_H 1
++
++#include <grub/efi/api.h>
++#include <grub/efi/efi.h>
++#include <grub/err.h>
++
++#define GRUB_EFI_CC_MEASUREMENT_PROTOCOL_GUID \
++  { 0x96751a3d, 0x72f4, 0x41a6, \
++    { 0xa7, 0x94, 0xed, 0x5d, 0x0e, 0x67, 0xae, 0x6b } \
++  };
++
++struct grub_efi_cc_version
++{
++  grub_efi_uint8_t Major;
++  grub_efi_uint8_t Minor;
++};
++typedef struct grub_efi_cc_version grub_efi_cc_version_t;
++
++/* EFI_CC Type/SubType definition. */
++#define GRUB_EFI_CC_TYPE_NONE	0
++#define GRUB_EFI_CC_TYPE_SEV	1
++#define GRUB_EFI_CC_TYPE_TDX	2
++
++struct grub_efi_cc_type
++{
++  grub_efi_uint8_t Type;
++  grub_efi_uint8_t SubType;
++};
++typedef struct grub_efi_cc_type grub_efi_cc_type_t;
++
++typedef grub_efi_uint32_t grub_efi_cc_event_log_bitmap_t;
++typedef grub_efi_uint32_t grub_efi_cc_event_log_format_t;
++typedef grub_efi_uint32_t grub_efi_cc_event_algorithm_bitmap_t;
++typedef grub_efi_uint32_t grub_efi_cc_mr_index_t;
++
++/* Intel TDX measure register index. */
++#define GRUB_TDX_MR_INDEX_MRTD	0
++#define GRUB_TDX_MR_INDEX_RTMR0	1
++#define GRUB_TDX_MR_INDEX_RTMR1	2
++#define GRUB_TDX_MR_INDEX_RTMR2	3
++#define GRUB_TDX_MR_INDEX_RTMR3	4
++
++#define GRUB_EFI_CC_EVENT_LOG_FORMAT_TCG_2	0x00000002
++#define GRUB_EFI_CC_BOOT_HASH_ALG_SHA384	0x00000004
++#define GRUB_EFI_CC_EVENT_HEADER_VERSION	1
++
++struct grub_efi_cc_event_header
++{
++  /* Size of the event header itself (sizeof(EFI_TD_EVENT_HEADER)). */
++  grub_efi_uint32_t      HeaderSize;
++
++  /*
++   * Header version. For this version of this specification,
++   * the value shall be 1.
++   */
++  grub_efi_uint16_t      HeaderVersion;
++
++  /* Index of the MR that shall be extended. */
++  grub_efi_cc_mr_index_t MrIndex;
++
++  /* Type of the event that shall be extended (and optionally logged). */
++  grub_efi_uint32_t      EventType;
++} GRUB_PACKED;
++typedef struct grub_efi_cc_event_header grub_efi_cc_event_header_t;
++
++struct grub_efi_cc_event
++{
++  /* Total size of the event including the Size component, the header and the Event data. */
++  grub_efi_uint32_t          Size;
++  grub_efi_cc_event_header_t Header;
++  grub_efi_uint8_t           Event[0];
++} GRUB_PACKED;
++typedef struct grub_efi_cc_event grub_efi_cc_event_t;
++
++struct grub_efi_cc_boot_service_capability
++{
++  /* Allocated size of the structure. */
++  grub_efi_uint8_t                     Size;
++
++  /*
++   * Version of the grub_efi_cc_boot_service_capability_t structure itself.
++   * For this version of the protocol, the Major version shall be set to 1
++   * and the Minor version shall be set to 1.
++   */
++  grub_efi_cc_version_t                StructureVersion;
++
++  /*
++   * Version of the EFI TD protocol.
++   * For this version of the protocol, the Major version shall be set to 1
++   * and the Minor version shall be set to 1.
++   */
++  grub_efi_cc_version_t                ProtocolVersion;
++
++  /* Supported hash algorithms. */
++  grub_efi_cc_event_algorithm_bitmap_t HashAlgorithmBitmap;
++
++  /* Bitmap of supported event log formats. */
++  grub_efi_cc_event_log_bitmap_t       SupportedEventLogs;
++
++  /* Indicates the CC type. */
++  grub_efi_cc_type_t CcType;
++};
++typedef struct grub_efi_cc_boot_service_capability grub_efi_cc_boot_service_capability_t;
++
++struct grub_efi_cc_protocol
++{
++  grub_efi_status_t
++  (*get_capability) (struct grub_efi_cc_protocol *this,
++		     grub_efi_cc_boot_service_capability_t *ProtocolCapability);
++
++  grub_efi_status_t
++  (*get_event_log) (struct grub_efi_cc_protocol *this,
++		    grub_efi_cc_event_log_format_t EventLogFormat,
++		    grub_efi_physical_address_t *EventLogLocation,
++		    grub_efi_physical_address_t *EventLogLastEntry,
++		    grub_efi_boolean_t *EventLogTruncated);
++
++  grub_efi_status_t
++  (*hash_log_extend_event) (struct grub_efi_cc_protocol *this,
++			    grub_efi_uint64_t Flags,
++			    grub_efi_physical_address_t DataToHash,
++			    grub_efi_uint64_t DataToHashLen,
++			    grub_efi_cc_event_t *EfiCcEvent);
++
++  grub_efi_status_t
++  (*map_pcr_to_mr_index) (struct grub_efi_cc_protocol *this,
++			  grub_efi_uint32_t PcrIndex,
++			  grub_efi_cc_mr_index_t *MrIndex);
++};
++typedef struct grub_efi_cc_protocol grub_efi_cc_protocol_t;
++
++#endif
diff --git a/SOURCES/0279-font-Reject-glyphs-exceeds-font-max_glyph_width-or-f.patch b/SOURCES/0279-font-Reject-glyphs-exceeds-font-max_glyph_width-or-f.patch
new file mode 100644
index 0000000..e0dd347
--- /dev/null
+++ b/SOURCES/0279-font-Reject-glyphs-exceeds-font-max_glyph_width-or-f.patch
@@ -0,0 +1,31 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zhang Boyang <zhangboyang.id@gmail.com>
+Date: Wed, 3 Aug 2022 19:45:33 +0800
+Subject: [PATCH] font: Reject glyphs exceeds font->max_glyph_width or
+ font->max_glyph_height
+
+Check glyph's width and height against limits specified in font's
+metadata. Reject the glyph (and font) if such limits are exceeded.
+
+Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 5760fcfd466cc757540ea0d591bad6a08caeaa16)
+---
+ grub-core/font/font.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/font/font.c b/grub-core/font/font.c
+index d09bb38d89..2f09a4a55b 100644
+--- a/grub-core/font/font.c
++++ b/grub-core/font/font.c
+@@ -760,7 +760,9 @@ grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code)
+ 	  || read_be_uint16 (font->file, &height) != 0
+ 	  || read_be_int16 (font->file, &xoff) != 0
+ 	  || read_be_int16 (font->file, &yoff) != 0
+-	  || read_be_int16 (font->file, &dwidth) != 0)
++	  || read_be_int16 (font->file, &dwidth) != 0
++	  || width > font->max_char_width
++	  || height > font->max_char_height)
+ 	{
+ 	  remove_font (font);
+ 	  return 0;
diff --git a/SOURCES/0280-font-Fix-size-overflow-in-grub_font_get_glyph_intern.patch b/SOURCES/0280-font-Fix-size-overflow-in-grub_font_get_glyph_intern.patch
new file mode 100644
index 0000000..b5f10f6
--- /dev/null
+++ b/SOURCES/0280-font-Fix-size-overflow-in-grub_font_get_glyph_intern.patch
@@ -0,0 +1,110 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zhang Boyang <zhangboyang.id@gmail.com>
+Date: Fri, 5 Aug 2022 00:51:20 +0800
+Subject: [PATCH] font: Fix size overflow in grub_font_get_glyph_internal()
+
+The length of memory allocation and file read may overflow. This patch
+fixes the problem by using safemath macros.
+
+There is a lot of code repetition like "(x * y + 7) / 8". It is unsafe
+if overflow happens. This patch introduces grub_video_bitmap_calc_1bpp_bufsz().
+It is safe replacement for such code. It has safemath-like prototype.
+
+This patch also introduces grub_cast(value, pointer), it casts value to
+typeof(*pointer) then store the value to *pointer. It returns true when
+overflow occurs or false if there is no overflow. The semantics of arguments
+and return value are designed to be consistent with other safemath macros.
+
+Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 941d10ad6f1dcbd12fb613002249e29ba035f985)
+---
+ grub-core/font/font.c   | 17 +++++++++++++----
+ include/grub/bitmap.h   | 18 ++++++++++++++++++
+ include/grub/safemath.h |  2 ++
+ 3 files changed, 33 insertions(+), 4 deletions(-)
+
+diff --git a/grub-core/font/font.c b/grub-core/font/font.c
+index 2f09a4a55b..6a3fbebbd8 100644
+--- a/grub-core/font/font.c
++++ b/grub-core/font/font.c
+@@ -739,7 +739,8 @@ grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code)
+       grub_int16_t xoff;
+       grub_int16_t yoff;
+       grub_int16_t dwidth;
+-      int len;
++      grub_ssize_t len;
++      grub_size_t sz;
+ 
+       if (index_entry->glyph)
+ 	/* Return cached glyph.  */
+@@ -768,9 +769,17 @@ grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code)
+ 	  return 0;
+ 	}
+ 
+-      len = (width * height + 7) / 8;
+-      glyph = grub_malloc (sizeof (struct grub_font_glyph) + len);
+-      if (!glyph)
++      /* Calculate real struct size of current glyph. */
++      if (grub_video_bitmap_calc_1bpp_bufsz (width, height, &len) ||
++	  grub_add (sizeof (struct grub_font_glyph), len, &sz))
++	{
++	  remove_font (font);
++	  return 0;
++	}
++
++      /* Allocate and initialize the glyph struct. */
++      glyph = grub_malloc (sz);
++      if (glyph == NULL)
+ 	{
+ 	  remove_font (font);
+ 	  return 0;
+diff --git a/include/grub/bitmap.h b/include/grub/bitmap.h
+index 5728f8ca3a..0d9603f619 100644
+--- a/include/grub/bitmap.h
++++ b/include/grub/bitmap.h
+@@ -23,6 +23,7 @@
+ #include <grub/symbol.h>
+ #include <grub/types.h>
+ #include <grub/video.h>
++#include <grub/safemath.h>
+ 
+ struct grub_video_bitmap
+ {
+@@ -79,6 +80,23 @@ grub_video_bitmap_get_height (struct grub_video_bitmap *bitmap)
+   return bitmap->mode_info.height;
+ }
+ 
++/*
++ * Calculate and store the size of data buffer of 1bit bitmap in result.
++ * Equivalent to "*result = (width * height + 7) / 8" if no overflow occurs.
++ * Return true when overflow occurs or false if there is no overflow.
++ * This function is intentionally implemented as a macro instead of
++ * an inline function. Although a bit awkward, it preserves data types for
++ * safemath macros and reduces macro side effects as much as possible.
++ *
++ * XXX: Will report false overflow if width * height > UINT64_MAX.
++ */
++#define grub_video_bitmap_calc_1bpp_bufsz(width, height, result) \
++({ \
++  grub_uint64_t _bitmap_pixels; \
++  grub_mul ((width), (height), &_bitmap_pixels) ? 1 : \
++    grub_cast (_bitmap_pixels / GRUB_CHAR_BIT + !!(_bitmap_pixels % GRUB_CHAR_BIT), (result)); \
++})
++
+ void EXPORT_FUNC (grub_video_bitmap_get_mode_info) (struct grub_video_bitmap *bitmap,
+ 						    struct grub_video_mode_info *mode_info);
+ 
+diff --git a/include/grub/safemath.h b/include/grub/safemath.h
+index c17b89bba1..bb0f826de1 100644
+--- a/include/grub/safemath.h
++++ b/include/grub/safemath.h
+@@ -30,6 +30,8 @@
+ #define grub_sub(a, b, res)	__builtin_sub_overflow(a, b, res)
+ #define grub_mul(a, b, res)	__builtin_mul_overflow(a, b, res)
+ 
++#define grub_cast(a, res)	grub_add ((a), 0, (res))
++
+ #else
+ #error gcc 5.1 or newer or clang 3.8 or newer is required
+ #endif
diff --git a/SOURCES/0281-font-Fix-several-integer-overflows-in-grub_font_cons.patch b/SOURCES/0281-font-Fix-several-integer-overflows-in-grub_font_cons.patch
new file mode 100644
index 0000000..3c76eb4
--- /dev/null
+++ b/SOURCES/0281-font-Fix-several-integer-overflows-in-grub_font_cons.patch
@@ -0,0 +1,79 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zhang Boyang <zhangboyang.id@gmail.com>
+Date: Fri, 5 Aug 2022 01:58:27 +0800
+Subject: [PATCH] font: Fix several integer overflows in
+ grub_font_construct_glyph()
+
+This patch fixes several integer overflows in grub_font_construct_glyph().
+Glyphs of invalid size, zero or leading to an overflow, are rejected.
+The inconsistency between "glyph" and "max_glyph_size" when grub_malloc()
+returns NULL is fixed too.
+
+Fixes: CVE-2022-2601
+
+Reported-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit b1805f251b31a9d3cfae5c3572ddfa630145dbbf)
+---
+ grub-core/font/font.c | 29 +++++++++++++++++------------
+ 1 file changed, 17 insertions(+), 12 deletions(-)
+
+diff --git a/grub-core/font/font.c b/grub-core/font/font.c
+index 6a3fbebbd8..1fa181d4ca 100644
+--- a/grub-core/font/font.c
++++ b/grub-core/font/font.c
+@@ -1517,6 +1517,7 @@ grub_font_construct_glyph (grub_font_t hinted_font,
+   struct grub_video_signed_rect bounds;
+   static struct grub_font_glyph *glyph = 0;
+   static grub_size_t max_glyph_size = 0;
++  grub_size_t cur_glyph_size;
+ 
+   ensure_comb_space (glyph_id);
+ 
+@@ -1533,29 +1534,33 @@ grub_font_construct_glyph (grub_font_t hinted_font,
+   if (!glyph_id->ncomb && !glyph_id->attributes)
+     return main_glyph;
+ 
+-  if (max_glyph_size < sizeof (*glyph) + (bounds.width * bounds.height + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT)
++  if (grub_video_bitmap_calc_1bpp_bufsz (bounds.width, bounds.height, &cur_glyph_size) ||
++      grub_add (sizeof (*glyph), cur_glyph_size, &cur_glyph_size))
++    return main_glyph;
++
++  if (max_glyph_size < cur_glyph_size)
+     {
+       grub_free (glyph);
+-      max_glyph_size = (sizeof (*glyph) + (bounds.width * bounds.height + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT) * 2;
+-      if (max_glyph_size < 8)
+-	max_glyph_size = 8;
+-      glyph = grub_malloc (max_glyph_size);
++      if (grub_mul (cur_glyph_size, 2, &max_glyph_size))
++	max_glyph_size = 0;
++      glyph = max_glyph_size > 0 ? grub_malloc (max_glyph_size) : NULL;
+     }
+   if (!glyph)
+     {
++      max_glyph_size = 0;
+       grub_errno = GRUB_ERR_NONE;
+       return main_glyph;
+     }
+ 
+-  grub_memset (glyph, 0, sizeof (*glyph)
+-	       + (bounds.width * bounds.height
+-		  + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT);
++  grub_memset (glyph, 0, cur_glyph_size);
+ 
+   glyph->font = main_glyph->font;
+-  glyph->width = bounds.width;
+-  glyph->height = bounds.height;
+-  glyph->offset_x = bounds.x;
+-  glyph->offset_y = bounds.y;
++  if (bounds.width == 0 || bounds.height == 0 ||
++      grub_cast (bounds.width, &glyph->width) ||
++      grub_cast (bounds.height, &glyph->height) ||
++      grub_cast (bounds.x, &glyph->offset_x) ||
++      grub_cast (bounds.y, &glyph->offset_y))
++    return main_glyph;
+ 
+   if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR)
+     grub_font_blit_glyph_mirror (glyph, main_glyph,
diff --git a/SOURCES/0282-font-Remove-grub_font_dup_glyph.patch b/SOURCES/0282-font-Remove-grub_font_dup_glyph.patch
new file mode 100644
index 0000000..4c3db92
--- /dev/null
+++ b/SOURCES/0282-font-Remove-grub_font_dup_glyph.patch
@@ -0,0 +1,40 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zhang Boyang <zhangboyang.id@gmail.com>
+Date: Fri, 5 Aug 2022 02:13:29 +0800
+Subject: [PATCH] font: Remove grub_font_dup_glyph()
+
+Remove grub_font_dup_glyph() since nobody is using it since 2013, and
+I'm too lazy to fix the integer overflow problem in it.
+
+Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 25ad31c19c331aaa2dbd9bd2b2e2655de5766a9d)
+---
+ grub-core/font/font.c | 14 --------------
+ 1 file changed, 14 deletions(-)
+
+diff --git a/grub-core/font/font.c b/grub-core/font/font.c
+index 1fa181d4ca..a115a63b0c 100644
+--- a/grub-core/font/font.c
++++ b/grub-core/font/font.c
+@@ -1055,20 +1055,6 @@ grub_font_get_glyph_with_fallback (grub_font_t font, grub_uint32_t code)
+   return best_glyph;
+ }
+ 
+-#if 0
+-static struct grub_font_glyph *
+-grub_font_dup_glyph (struct grub_font_glyph *glyph)
+-{
+-  static struct grub_font_glyph *ret;
+-  ret = grub_malloc (sizeof (*ret) + (glyph->width * glyph->height + 7) / 8);
+-  if (!ret)
+-    return NULL;
+-  grub_memcpy (ret, glyph, sizeof (*ret)
+-	       + (glyph->width * glyph->height + 7) / 8);
+-  return ret;
+-}
+-#endif
+-
+ /* FIXME: suboptimal.  */
+ static void
+ grub_font_blit_glyph (struct grub_font_glyph *target,
diff --git a/SOURCES/0283-font-Fix-integer-overflow-in-ensure_comb_space.patch b/SOURCES/0283-font-Fix-integer-overflow-in-ensure_comb_space.patch
new file mode 100644
index 0000000..6b73038
--- /dev/null
+++ b/SOURCES/0283-font-Fix-integer-overflow-in-ensure_comb_space.patch
@@ -0,0 +1,46 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zhang Boyang <zhangboyang.id@gmail.com>
+Date: Fri, 5 Aug 2022 02:27:05 +0800
+Subject: [PATCH] font: Fix integer overflow in ensure_comb_space()
+
+In fact it can't overflow at all because glyph_id->ncomb is only 8-bit
+wide. But let's keep safe if somebody changes the width of glyph_id->ncomb
+in the future. This patch also fixes the inconsistency between
+render_max_comb_glyphs and render_combining_glyphs when grub_malloc()
+returns NULL.
+
+Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit b2740b7e4a03bb8331d48b54b119afea76bb9d5f)
+---
+ grub-core/font/font.c | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/grub-core/font/font.c b/grub-core/font/font.c
+index a115a63b0c..d0e6340404 100644
+--- a/grub-core/font/font.c
++++ b/grub-core/font/font.c
+@@ -1468,14 +1468,18 @@ ensure_comb_space (const struct grub_unicode_glyph *glyph_id)
+   if (glyph_id->ncomb <= render_max_comb_glyphs)
+     return;
+ 
+-  render_max_comb_glyphs = 2 * glyph_id->ncomb;
+-  if (render_max_comb_glyphs < 8)
++  if (grub_mul (glyph_id->ncomb, 2, &render_max_comb_glyphs))
++    render_max_comb_glyphs = 0;
++  if (render_max_comb_glyphs > 0 && render_max_comb_glyphs < 8)
+     render_max_comb_glyphs = 8;
+   grub_free (render_combining_glyphs);
+-  render_combining_glyphs = grub_malloc (render_max_comb_glyphs
+-					 * sizeof (render_combining_glyphs[0]));
++  render_combining_glyphs = (render_max_comb_glyphs > 0) ?
++    grub_calloc (render_max_comb_glyphs, sizeof (render_combining_glyphs[0])) : NULL;
+   if (!render_combining_glyphs)
+-    grub_errno = 0;
++    {
++      render_max_comb_glyphs = 0;
++      grub_errno = GRUB_ERR_NONE;
++    }
+ }
+ 
+ int
diff --git a/SOURCES/0284-font-Fix-integer-overflow-in-BMP-index.patch b/SOURCES/0284-font-Fix-integer-overflow-in-BMP-index.patch
new file mode 100644
index 0000000..5e10699
--- /dev/null
+++ b/SOURCES/0284-font-Fix-integer-overflow-in-BMP-index.patch
@@ -0,0 +1,63 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zhang Boyang <zhangboyang.id@gmail.com>
+Date: Mon, 15 Aug 2022 02:04:58 +0800
+Subject: [PATCH] font: Fix integer overflow in BMP index
+
+The BMP index (font->bmp_idx) is designed as a reverse lookup table of
+char entries (font->char_index), in order to speed up lookups for BMP
+chars (i.e. code < 0x10000). The values in BMP index are the subscripts
+of the corresponding char entries, stored in grub_uint16_t, while 0xffff
+means not found.
+
+This patch fixes the problem of large subscript truncated to grub_uint16_t,
+leading BMP index to return wrong char entry or report false miss. The
+code now checks for bounds and uses BMP index as a hint, and fallbacks
+to binary-search if necessary.
+
+On the occasion add a comment about BMP index is initialized to 0xffff.
+
+Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit afda8b60ba0712abe01ae1e64c5f7a067a0e6492)
+---
+ grub-core/font/font.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/grub-core/font/font.c b/grub-core/font/font.c
+index d0e6340404..b208a28717 100644
+--- a/grub-core/font/font.c
++++ b/grub-core/font/font.c
+@@ -300,6 +300,8 @@ load_font_index (grub_file_t file, grub_uint32_t sect_length, struct
+   font->bmp_idx = grub_malloc (0x10000 * sizeof (grub_uint16_t));
+   if (!font->bmp_idx)
+     return 1;
++
++  /* Init the BMP index array to 0xffff. */
+   grub_memset (font->bmp_idx, 0xff, 0x10000 * sizeof (grub_uint16_t));
+ 
+ 
+@@ -328,7 +330,7 @@ load_font_index (grub_file_t file, grub_uint32_t sect_length, struct
+ 	  return 1;
+ 	}
+ 
+-      if (entry->code < 0x10000)
++      if (entry->code < 0x10000 && i < 0xffff)
+ 	font->bmp_idx[entry->code] = i;
+ 
+       last_code = entry->code;
+@@ -696,9 +698,12 @@ find_glyph (const grub_font_t font, grub_uint32_t code)
+   /* Use BMP index if possible.  */
+   if (code < 0x10000 && font->bmp_idx)
+     {
+-      if (font->bmp_idx[code] == 0xffff)
+-	return 0;
+-      return &table[font->bmp_idx[code]];
++      if (font->bmp_idx[code] < 0xffff)
++	return &table[font->bmp_idx[code]];
++      /*
++       * When we are here then lookup in BMP index result in miss,
++       * fallthough to binary-search.
++       */
+     }
+ 
+   /* Do a binary search in `char_index', which is ordered by code point.  */
diff --git a/SOURCES/0285-font-Fix-integer-underflow-in-binary-search-of-char-.patch b/SOURCES/0285-font-Fix-integer-underflow-in-binary-search-of-char-.patch
new file mode 100644
index 0000000..e81824b
--- /dev/null
+++ b/SOURCES/0285-font-Fix-integer-underflow-in-binary-search-of-char-.patch
@@ -0,0 +1,83 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zhang Boyang <zhangboyang.id@gmail.com>
+Date: Sun, 14 Aug 2022 18:09:38 +0800
+Subject: [PATCH] font: Fix integer underflow in binary search of char index
+
+If search target is less than all entries in font->index then "hi"
+variable is set to -1, which translates to SIZE_MAX and leads to errors.
+
+This patch fixes the problem by replacing the entire binary search code
+with the libstdc++'s std::lower_bound() implementation.
+
+Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit c140a086838e7c9af87842036f891b8393a8c4bc)
+---
+ grub-core/font/font.c | 40 ++++++++++++++++++++++------------------
+ 1 file changed, 22 insertions(+), 18 deletions(-)
+
+diff --git a/grub-core/font/font.c b/grub-core/font/font.c
+index b208a28717..193dfec045 100644
+--- a/grub-core/font/font.c
++++ b/grub-core/font/font.c
+@@ -688,12 +688,12 @@ read_be_int16 (grub_file_t file, grub_int16_t * value)
+ static inline struct char_index_entry *
+ find_glyph (const grub_font_t font, grub_uint32_t code)
+ {
+-  struct char_index_entry *table;
+-  grub_size_t lo;
+-  grub_size_t hi;
+-  grub_size_t mid;
++  struct char_index_entry *table, *first, *end;
++  grub_size_t len;
+ 
+   table = font->char_index;
++  if (table == NULL)
++    return NULL;
+ 
+   /* Use BMP index if possible.  */
+   if (code < 0x10000 && font->bmp_idx)
+@@ -706,25 +706,29 @@ find_glyph (const grub_font_t font, grub_uint32_t code)
+        */
+     }
+ 
+-  /* Do a binary search in `char_index', which is ordered by code point.  */
+-  lo = 0;
+-  hi = font->num_chars - 1;
++  /*
++   * Do a binary search in char_index which is ordered by code point.
++   * The code below is the same as libstdc++'s std::lower_bound().
++   */
++  first = table;
++  len = font->num_chars;
++  end = first + len;
+ 
+-  if (!table)
+-    return 0;
+-
+-  while (lo <= hi)
++  while (len > 0)
+     {
+-      mid = lo + (hi - lo) / 2;
+-      if (code < table[mid].code)
+-	hi = mid - 1;
+-      else if (code > table[mid].code)
+-	lo = mid + 1;
++      grub_size_t half = len >> 1;
++      struct char_index_entry *middle = first + half;
++
++      if (middle->code < code)
++	{
++	  first = middle + 1;
++	  len = len - half - 1;
++	}
+       else
+-	return &table[mid];
++	len = half;
+     }
+ 
+-  return 0;
++  return (first < end && first->code == code) ? first : NULL;
+ }
+ 
+ /* Get a glyph for the Unicode character CODE in FONT.  The glyph is loaded
diff --git a/SOURCES/0286-kern-efi-sb-Enforce-verification-of-font-files.patch b/SOURCES/0286-kern-efi-sb-Enforce-verification-of-font-files.patch
new file mode 100644
index 0000000..1a381b0
--- /dev/null
+++ b/SOURCES/0286-kern-efi-sb-Enforce-verification-of-font-files.patch
@@ -0,0 +1,52 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zhang Boyang <zhangboyang.id@gmail.com>
+Date: Sun, 14 Aug 2022 15:51:54 +0800
+Subject: [PATCH] kern/efi/sb: Enforce verification of font files
+
+As a mitigation and hardening measure enforce verification of font
+files. Then only trusted font files can be load. This will reduce the
+attack surface at cost of losing the ability of end-users to customize
+fonts if e.g. UEFI Secure Boot is enabled. Vendors can always customize
+fonts because they have ability to pack fonts into their GRUB bundles.
+
+This goal is achieved by:
+
+  * Removing GRUB_FILE_TYPE_FONT from shim lock verifier's
+    skip-verification list.
+
+  * Adding GRUB_FILE_TYPE_FONT to lockdown verifier's defer-auth list,
+    so font files must be verified by a verifier before they can be loaded.
+
+Suggested-by: Daniel Kiper <daniel.kiper@oracle.com>
+Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 630deb8c0d8b02b670ced4b7030414bcf17aa080)
+---
+ grub-core/kern/efi/sb.c   | 1 -
+ grub-core/kern/lockdown.c | 1 +
+ 2 files changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/kern/efi/sb.c b/grub-core/kern/efi/sb.c
+index 89c4bb3fd1..db42c2539f 100644
+--- a/grub-core/kern/efi/sb.c
++++ b/grub-core/kern/efi/sb.c
+@@ -145,7 +145,6 @@ shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)),
+     case GRUB_FILE_TYPE_PRINT_BLOCKLIST:
+     case GRUB_FILE_TYPE_TESTLOAD:
+     case GRUB_FILE_TYPE_GET_SIZE:
+-    case GRUB_FILE_TYPE_FONT:
+     case GRUB_FILE_TYPE_ZFS_ENCRYPTION_KEY:
+     case GRUB_FILE_TYPE_CAT:
+     case GRUB_FILE_TYPE_HEXCAT:
+diff --git a/grub-core/kern/lockdown.c b/grub-core/kern/lockdown.c
+index 0bc70fd42d..af6d493cd3 100644
+--- a/grub-core/kern/lockdown.c
++++ b/grub-core/kern/lockdown.c
+@@ -51,6 +51,7 @@ lockdown_verifier_init (grub_file_t io __attribute__ ((unused)),
+     case GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE:
+     case GRUB_FILE_TYPE_ACPI_TABLE:
+     case GRUB_FILE_TYPE_DEVICE_TREE_IMAGE:
++    case GRUB_FILE_TYPE_FONT:
+       *flags = GRUB_VERIFY_FLAGS_DEFER_AUTH;
+ 
+       /* Fall through. */
diff --git a/SOURCES/0287-fbutil-Fix-integer-overflow.patch b/SOURCES/0287-fbutil-Fix-integer-overflow.patch
new file mode 100644
index 0000000..4d32dbd
--- /dev/null
+++ b/SOURCES/0287-fbutil-Fix-integer-overflow.patch
@@ -0,0 +1,83 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zhang Boyang <zhangboyang.id@gmail.com>
+Date: Tue, 6 Sep 2022 03:03:21 +0800
+Subject: [PATCH] fbutil: Fix integer overflow
+
+Expressions like u64 = u32 * u32 are unsafe because their products are
+truncated to u32 even if left hand side is u64. This patch fixes all
+problems like that one in fbutil.
+
+To get right result not only left hand side have to be u64 but it's also
+necessary to cast at least one of the operands of all leaf operators of
+right hand side to u64, e.g. u64 = u32 * u32 + u32 * u32 should be
+u64 = (u64)u32 * u32 + (u64)u32 * u32.
+
+For 1-bit bitmaps grub_uint64_t have to be used. It's safe because any
+combination of values in (grub_uint64_t)u32 * u32 + u32 expression will
+not overflow grub_uint64_t.
+
+Other expressions like ptr + u32 * u32 + u32 * u32 are also vulnerable.
+They should be ptr + (grub_addr_t)u32 * u32 + (grub_addr_t)u32 * u32.
+
+This patch also adds a comment to grub_video_fb_get_video_ptr() which
+says it's arguments must be valid and no sanity check is performed
+(like its siblings in grub-core/video/fb/fbutil.c).
+
+Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 50a11a81bc842c58962244a2dc86bbd31a426e12)
+---
+ grub-core/video/fb/fbutil.c |  4 ++--
+ include/grub/fbutil.h       | 13 +++++++++----
+ 2 files changed, 11 insertions(+), 6 deletions(-)
+
+diff --git a/grub-core/video/fb/fbutil.c b/grub-core/video/fb/fbutil.c
+index b98bb51fe8..25ef39f47d 100644
+--- a/grub-core/video/fb/fbutil.c
++++ b/grub-core/video/fb/fbutil.c
+@@ -67,7 +67,7 @@ get_pixel (struct grub_video_fbblit_info *source,
+     case 1:
+       if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED)
+         {
+-          int bit_index = y * source->mode_info->width + x;
++          grub_uint64_t bit_index = (grub_uint64_t) y * source->mode_info->width + x;
+           grub_uint8_t *ptr = source->data + bit_index / 8;
+           int bit_pos = 7 - bit_index % 8;
+           color = (*ptr >> bit_pos) & 0x01;
+@@ -138,7 +138,7 @@ set_pixel (struct grub_video_fbblit_info *source,
+     case 1:
+       if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED)
+         {
+-          int bit_index = y * source->mode_info->width + x;
++          grub_uint64_t bit_index = (grub_uint64_t) y * source->mode_info->width + x;
+           grub_uint8_t *ptr = source->data + bit_index / 8;
+           int bit_pos = 7 - bit_index % 8;
+           *ptr = (*ptr & ~(1 << bit_pos)) | ((color & 0x01) << bit_pos);
+diff --git a/include/grub/fbutil.h b/include/grub/fbutil.h
+index 4205eb917f..78a1ab3b45 100644
+--- a/include/grub/fbutil.h
++++ b/include/grub/fbutil.h
+@@ -31,14 +31,19 @@ struct grub_video_fbblit_info
+   grub_uint8_t *data;
+ };
+ 
+-/* Don't use for 1-bit bitmaps, addressing needs to be done at the bit level
+-   and it doesn't make sense, in general, to ask for a pointer
+-   to a particular pixel's data.  */
++/*
++ * Don't use for 1-bit bitmaps, addressing needs to be done at the bit level
++ * and it doesn't make sense, in general, to ask for a pointer
++ * to a particular pixel's data.
++ *
++ * This function assumes that bounds checking has been done in previous phase
++ * and they are opted out in here.
++ */
+ static inline void *
+ grub_video_fb_get_video_ptr (struct grub_video_fbblit_info *source,
+               unsigned int x, unsigned int y)
+ {
+-  return source->data + y * source->mode_info->pitch + x * source->mode_info->bytes_per_pixel;
++  return source->data + (grub_addr_t) y * source->mode_info->pitch + (grub_addr_t) x * source->mode_info->bytes_per_pixel;
+ }
+ 
+ /* Advance pointer by VAL bytes. If there is no unaligned access available,
diff --git a/SOURCES/0288-font-Fix-an-integer-underflow-in-blit_comb.patch b/SOURCES/0288-font-Fix-an-integer-underflow-in-blit_comb.patch
new file mode 100644
index 0000000..72f4308
--- /dev/null
+++ b/SOURCES/0288-font-Fix-an-integer-underflow-in-blit_comb.patch
@@ -0,0 +1,89 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zhang Boyang <zhangboyang.id@gmail.com>
+Date: Mon, 24 Oct 2022 08:05:35 +0800
+Subject: [PATCH] font: Fix an integer underflow in blit_comb()
+
+The expression (ctx.bounds.height - combining_glyphs[i]->height) / 2 may
+evaluate to a very big invalid value even if both ctx.bounds.height and
+combining_glyphs[i]->height are small integers. For example, if
+ctx.bounds.height is 10 and combining_glyphs[i]->height is 12, this
+expression evaluates to 2147483647 (expected -1). This is because
+coordinates are allowed to be negative but ctx.bounds.height is an
+unsigned int. So, the subtraction operates on unsigned ints and
+underflows to a very big value. The division makes things even worse.
+The quotient is still an invalid value even if converted back to int.
+
+This patch fixes the problem by casting ctx.bounds.height to int. As
+a result the subtraction will operate on int and grub_uint16_t which
+will be promoted to an int. So, the underflow will no longer happen. Other
+uses of ctx.bounds.height (and ctx.bounds.width) are also casted to int,
+to ensure coordinates are always calculated on signed integers.
+
+Fixes: CVE-2022-3775
+
+Reported-by: Daniel Axtens <dja@axtens.net>
+Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 6d2668dea3774ed74c4cd1eadd146f1b846bc3d4)
+---
+ grub-core/font/font.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/grub-core/font/font.c b/grub-core/font/font.c
+index 193dfec045..12a5f0d08c 100644
+--- a/grub-core/font/font.c
++++ b/grub-core/font/font.c
+@@ -1203,12 +1203,12 @@ blit_comb (const struct grub_unicode_glyph *glyph_id,
+   ctx.bounds.height = main_glyph->height;
+ 
+   above_rightx = main_glyph->offset_x + main_glyph->width;
+-  above_righty = ctx.bounds.y + ctx.bounds.height;
++  above_righty = ctx.bounds.y + (int) ctx.bounds.height;
+ 
+   above_leftx = main_glyph->offset_x;
+-  above_lefty = ctx.bounds.y + ctx.bounds.height;
++  above_lefty = ctx.bounds.y + (int) ctx.bounds.height;
+ 
+-  below_rightx = ctx.bounds.x + ctx.bounds.width;
++  below_rightx = ctx.bounds.x + (int) ctx.bounds.width;
+   below_righty = ctx.bounds.y;
+ 
+   comb = grub_unicode_get_comb (glyph_id);
+@@ -1221,7 +1221,7 @@ blit_comb (const struct grub_unicode_glyph *glyph_id,
+ 
+       if (!combining_glyphs[i])
+ 	continue;
+-      targetx = (ctx.bounds.width - combining_glyphs[i]->width) / 2 + ctx.bounds.x;
++      targetx = ((int) ctx.bounds.width - combining_glyphs[i]->width) / 2 + ctx.bounds.x;
+       /* CGJ is to avoid diacritics reordering. */
+       if (comb[i].code
+ 	  == GRUB_UNICODE_COMBINING_GRAPHEME_JOINER)
+@@ -1231,8 +1231,8 @@ blit_comb (const struct grub_unicode_glyph *glyph_id,
+ 	case GRUB_UNICODE_COMB_OVERLAY:
+ 	  do_blit (combining_glyphs[i],
+ 		   targetx,
+-		   (ctx.bounds.height - combining_glyphs[i]->height) / 2
+-		   - (ctx.bounds.height + ctx.bounds.y), &ctx);
++		   ((int) ctx.bounds.height - combining_glyphs[i]->height) / 2
++		   - ((int) ctx.bounds.height + ctx.bounds.y), &ctx);
+ 	  if (min_devwidth < combining_glyphs[i]->width)
+ 	    min_devwidth = combining_glyphs[i]->width;
+ 	  break;
+@@ -1305,7 +1305,7 @@ blit_comb (const struct grub_unicode_glyph *glyph_id,
+ 	  /* Fallthrough.  */
+ 	case GRUB_UNICODE_STACK_ATTACHED_ABOVE:
+ 	  do_blit (combining_glyphs[i], targetx,
+-		   -(ctx.bounds.height + ctx.bounds.y + space
++		   -((int) ctx.bounds.height + ctx.bounds.y + space
+ 		     + combining_glyphs[i]->height), &ctx);
+ 	  if (min_devwidth < combining_glyphs[i]->width)
+ 	    min_devwidth = combining_glyphs[i]->width;
+@@ -1313,7 +1313,7 @@ blit_comb (const struct grub_unicode_glyph *glyph_id,
+ 
+ 	case GRUB_UNICODE_COMB_HEBREW_DAGESH:
+ 	  do_blit (combining_glyphs[i], targetx,
+-		   -(ctx.bounds.height / 2 + ctx.bounds.y
++		   -((int) ctx.bounds.height / 2 + ctx.bounds.y
+ 		     + combining_glyphs[i]->height / 2), &ctx);
+ 	  if (min_devwidth < combining_glyphs[i]->width)
+ 	    min_devwidth = combining_glyphs[i]->width;
diff --git a/SOURCES/0289-font-Harden-grub_font_blit_glyph-and-grub_font_blit_.patch b/SOURCES/0289-font-Harden-grub_font_blit_glyph-and-grub_font_blit_.patch
new file mode 100644
index 0000000..5207c12
--- /dev/null
+++ b/SOURCES/0289-font-Harden-grub_font_blit_glyph-and-grub_font_blit_.patch
@@ -0,0 +1,73 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zhang Boyang <zhangboyang.id@gmail.com>
+Date: Mon, 24 Oct 2022 07:15:41 +0800
+Subject: [PATCH] font: Harden grub_font_blit_glyph() and
+ grub_font_blit_glyph_mirror()
+
+As a mitigation and hardening measure add sanity checks to
+grub_font_blit_glyph() and grub_font_blit_glyph_mirror(). This patch
+makes these two functions do nothing if target blitting area isn't fully
+contained in target bitmap. Therefore, if complex calculations in caller
+overflows and malicious coordinates are given, we are still safe because
+any coordinates which result in out-of-bound-write are rejected. However,
+this patch only checks for invalid coordinates, and doesn't provide any
+protection against invalid source glyph or destination glyph, e.g.
+mismatch between glyph size and buffer size.
+
+This hardening measure is designed to mitigate possible overflows in
+blit_comb(). If overflow occurs, it may return invalid bounding box
+during dry run and call grub_font_blit_glyph() with malicious
+coordinates during actual blitting. However, we are still safe because
+the scratch glyph itself is valid, although its size makes no sense, and
+any invalid coordinates are rejected.
+
+It would be better to call grub_fatal() if illegal parameter is detected.
+However, doing this may end up in a dangerous recursion because grub_fatal()
+would print messages to the screen and we are in the progress of drawing
+characters on the screen.
+
+Reported-by: Daniel Axtens <dja@axtens.net>
+Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit fcd7aa0c278f7cf3fb9f93f1a3966e1792339eb6)
+---
+ grub-core/font/font.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/grub-core/font/font.c b/grub-core/font/font.c
+index 12a5f0d08c..29fbb94294 100644
+--- a/grub-core/font/font.c
++++ b/grub-core/font/font.c
+@@ -1069,8 +1069,15 @@ static void
+ grub_font_blit_glyph (struct grub_font_glyph *target,
+ 		      struct grub_font_glyph *src, unsigned dx, unsigned dy)
+ {
++  grub_uint16_t max_x, max_y;
+   unsigned src_bit, tgt_bit, src_byte, tgt_byte;
+   unsigned i, j;
++
++  /* Harden against out-of-bound writes. */
++  if ((grub_add (dx, src->width, &max_x) || max_x > target->width) ||
++      (grub_add (dy, src->height, &max_y) || max_y > target->height))
++    return;
++
+   for (i = 0; i < src->height; i++)
+     {
+       src_bit = (src->width * i) % 8;
+@@ -1102,9 +1109,16 @@ grub_font_blit_glyph_mirror (struct grub_font_glyph *target,
+ 			     struct grub_font_glyph *src,
+ 			     unsigned dx, unsigned dy)
+ {
++  grub_uint16_t max_x, max_y;
+   unsigned tgt_bit, src_byte, tgt_byte;
+   signed src_bit;
+   unsigned i, j;
++
++  /* Harden against out-of-bound writes. */
++  if ((grub_add (dx, src->width, &max_x) || max_x > target->width) ||
++      (grub_add (dy, src->height, &max_y) || max_y > target->height))
++    return;
++
+   for (i = 0; i < src->height; i++)
+     {
+       src_bit = (src->width * i + src->width - 1) % 8;
diff --git a/SOURCES/0290-font-Assign-null_font-to-glyphs-in-ascii_font_glyph.patch b/SOURCES/0290-font-Assign-null_font-to-glyphs-in-ascii_font_glyph.patch
new file mode 100644
index 0000000..c2bcc18
--- /dev/null
+++ b/SOURCES/0290-font-Assign-null_font-to-glyphs-in-ascii_font_glyph.patch
@@ -0,0 +1,34 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zhang Boyang <zhangboyang.id@gmail.com>
+Date: Fri, 28 Oct 2022 17:29:16 +0800
+Subject: [PATCH] font: Assign null_font to glyphs in ascii_font_glyph[]
+
+The calculations in blit_comb() need information from glyph's font, e.g.
+grub_font_get_xheight(main_glyph->font). However, main_glyph->font is
+NULL if main_glyph comes from ascii_font_glyph[]. Therefore
+grub_font_get_*() crashes because of NULL pointer.
+
+There is already a solution, the null_font. So, assign it to those glyphs
+in ascii_font_glyph[].
+
+Reported-by: Daniel Axtens <dja@axtens.net>
+Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit dd539d695482069d28b40f2d3821f710cdcf6ee6)
+---
+ grub-core/font/font.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/font/font.c b/grub-core/font/font.c
+index 29fbb94294..e6616e610c 100644
+--- a/grub-core/font/font.c
++++ b/grub-core/font/font.c
+@@ -137,7 +137,7 @@ ascii_glyph_lookup (grub_uint32_t code)
+ 	  ascii_font_glyph[current]->offset_x = 0;
+ 	  ascii_font_glyph[current]->offset_y = -2;
+ 	  ascii_font_glyph[current]->device_width = 8;
+-	  ascii_font_glyph[current]->font = NULL;
++	  ascii_font_glyph[current]->font = &null_font;
+ 
+ 	  grub_memcpy (ascii_font_glyph[current]->bitmap,
+ 		       &ascii_bitmaps[current * ASCII_BITMAP_SIZE],
diff --git a/SOURCES/0291-normal-charset-Fix-an-integer-overflow-in-grub_unico.patch b/SOURCES/0291-normal-charset-Fix-an-integer-overflow-in-grub_unico.patch
new file mode 100644
index 0000000..ec2184f
--- /dev/null
+++ b/SOURCES/0291-normal-charset-Fix-an-integer-overflow-in-grub_unico.patch
@@ -0,0 +1,53 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zhang Boyang <zhangboyang.id@gmail.com>
+Date: Fri, 28 Oct 2022 21:31:39 +0800
+Subject: [PATCH] normal/charset: Fix an integer overflow in
+ grub_unicode_aglomerate_comb()
+
+The out->ncomb is a bit-field of 8 bits. So, the max possible value is 255.
+However, code in grub_unicode_aglomerate_comb() doesn't check for an
+overflow when incrementing out->ncomb. If out->ncomb is already 255,
+after incrementing it will get 0 instead of 256, and cause illegal
+memory access in subsequent processing.
+
+This patch introduces GRUB_UNICODE_NCOMB_MAX to represent the max
+acceptable value of ncomb. The code now checks for this limit and
+ignores additional combining characters when limit is reached.
+
+Reported-by: Daniel Axtens <dja@axtens.net>
+Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit da90d62316a3b105d2fbd7334d6521936bd6dcf6)
+---
+ grub-core/normal/charset.c | 3 +++
+ include/grub/unicode.h     | 2 ++
+ 2 files changed, 5 insertions(+)
+
+diff --git a/grub-core/normal/charset.c b/grub-core/normal/charset.c
+index 7a5a7c153c..c243ca6dae 100644
+--- a/grub-core/normal/charset.c
++++ b/grub-core/normal/charset.c
+@@ -472,6 +472,9 @@ grub_unicode_aglomerate_comb (const grub_uint32_t *in, grub_size_t inlen,
+ 	  if (!haveout)
+ 	    continue;
+ 
++	  if (out->ncomb == GRUB_UNICODE_NCOMB_MAX)
++	    continue;
++
+ 	  if (comb_type == GRUB_UNICODE_COMB_MC
+ 	      || comb_type == GRUB_UNICODE_COMB_ME
+ 	      || comb_type == GRUB_UNICODE_COMB_MN)
+diff --git a/include/grub/unicode.h b/include/grub/unicode.h
+index 4de986a857..c4f6fca043 100644
+--- a/include/grub/unicode.h
++++ b/include/grub/unicode.h
+@@ -147,7 +147,9 @@ struct grub_unicode_glyph
+   grub_uint8_t bidi_level:6; /* minimum: 6 */
+   enum grub_bidi_type bidi_type:5; /* minimum: :5 */
+ 
++#define GRUB_UNICODE_NCOMB_MAX ((1 << 8) - 1)
+   unsigned ncomb:8;
++
+   /* Hint by unicode subsystem how wide this character usually is.
+      Real width is determined by font. Set only in UTF-8 stream.  */
+   int estimated_width:8;
diff --git a/SOURCES/0292-font-Try-opening-fonts-from-the-bundled-memdisk.patch b/SOURCES/0292-font-Try-opening-fonts-from-the-bundled-memdisk.patch
new file mode 100644
index 0000000..bad9d90
--- /dev/null
+++ b/SOURCES/0292-font-Try-opening-fonts-from-the-bundled-memdisk.patch
@@ -0,0 +1,78 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Chris Coulson <chris.coulson@canonical.com>
+Date: Wed, 16 Nov 2022 14:40:04 +0000
+Subject: [PATCH] font: Try opening fonts from the bundled memdisk
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+---
+ grub-core/font/font.c | 48 +++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 31 insertions(+), 17 deletions(-)
+
+diff --git a/grub-core/font/font.c b/grub-core/font/font.c
+index e6616e610c..e421d1ae6f 100644
+--- a/grub-core/font/font.c
++++ b/grub-core/font/font.c
+@@ -409,6 +409,27 @@ read_section_as_short (struct font_file_section *section,
+   return 0;
+ }
+ 
++static grub_file_t
++try_open_from_prefix (const char *prefix, const char *filename)
++{
++  grub_file_t file;
++  char *fullname, *ptr;
++
++  fullname = grub_malloc (grub_strlen (prefix) + grub_strlen (filename) + 1
++			  + sizeof ("/fonts/") + sizeof (".pf2"));
++  if (!fullname)
++    return 0;
++  ptr = grub_stpcpy (fullname, prefix);
++  ptr = grub_stpcpy (ptr, "/fonts/");
++  ptr = grub_stpcpy (ptr, filename);
++  ptr = grub_stpcpy (ptr, ".pf2");
++  *ptr = 0;
++
++  file = grub_buffile_open (fullname, GRUB_FILE_TYPE_FONT, 1024);
++  grub_free (fullname);
++  return file;
++}
++
+ /* Load a font and add it to the beginning of the global font list.
+    Returns 0 upon success, nonzero upon failure.  */
+ grub_font_t
+@@ -427,25 +448,18 @@ grub_font_load (const char *filename)
+     file = grub_buffile_open (filename, GRUB_FILE_TYPE_FONT, 1024);
+   else
+     {
+-      const char *prefix = grub_env_get ("prefix");
+-      char *fullname, *ptr;
+-      if (!prefix)
++      file = try_open_from_prefix ("(memdisk)", filename);
++      if (!file)
+ 	{
+-	  grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"),
+-		      "prefix");
+-	  goto fail;
++	  const char *prefix = grub_env_get ("prefix");
++	  if (!prefix)
++	    {
++	      grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"),
++			  "prefix");
++	      goto fail;
++	    }
++	  file = try_open_from_prefix (prefix, filename);
+ 	}
+-      fullname = grub_malloc (grub_strlen (prefix) + grub_strlen (filename) + 1
+-			      + sizeof ("/fonts/") + sizeof (".pf2"));
+-      if (!fullname)
+-	goto fail;
+-      ptr = grub_stpcpy (fullname, prefix);
+-      ptr = grub_stpcpy (ptr, "/fonts/");
+-      ptr = grub_stpcpy (ptr, filename);
+-      ptr = grub_stpcpy (ptr, ".pf2");
+-      *ptr = 0;
+-      file = grub_buffile_open (fullname, GRUB_FILE_TYPE_FONT, 1024);
+-      grub_free (fullname);
+     }
+   if (!file)
+     goto fail;
diff --git a/SOURCES/0293-mm-Clarify-grub_real_malloc.patch b/SOURCES/0293-mm-Clarify-grub_real_malloc.patch
new file mode 100644
index 0000000..0a99c08
--- /dev/null
+++ b/SOURCES/0293-mm-Clarify-grub_real_malloc.patch
@@ -0,0 +1,183 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Thu, 25 Nov 2021 02:22:46 +1100
+Subject: [PATCH] mm: Clarify grub_real_malloc()
+
+When iterating through the singly linked list of free blocks,
+grub_real_malloc() uses p and q for the current and previous blocks
+respectively. This isn't super clear, so swap to using prev and cur.
+
+This makes another quirk more obvious. The comment at the top of
+grub_real_malloc() might lead you to believe that the function will
+allocate from *first if there is space in that block.
+
+It actually doesn't do that, and it can't do that with the current
+data structures. If we used up all of *first, we would need to change
+the ->next of the previous block to point to *first->next, but we
+can't do that because it's a singly linked list and we don't have
+access to *first's previous block.
+
+What grub_real_malloc() actually does is set *first to the initial
+previous block, and *first->next is the block we try to allocate
+from. That allows us to keep all the data structures consistent.
+
+Document that.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 246ad6a44c281bb13486ddea0a26bb661db73106)
+---
+ grub-core/kern/mm.c | 76 +++++++++++++++++++++++++++++------------------------
+ 1 file changed, 41 insertions(+), 35 deletions(-)
+
+diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
+index d8c8377578..fb20e93acf 100644
+--- a/grub-core/kern/mm.c
++++ b/grub-core/kern/mm.c
+@@ -178,13 +178,20 @@ grub_mm_init_region (void *addr, grub_size_t size)
+ }
+ 
+ /* Allocate the number of units N with the alignment ALIGN from the ring
+-   buffer starting from *FIRST.  ALIGN must be a power of two. Both N and
+-   ALIGN are in units of GRUB_MM_ALIGN.  Return a non-NULL if successful,
+-   otherwise return NULL.  */
++ * buffer given in *FIRST.  ALIGN must be a power of two. Both N and
++ * ALIGN are in units of GRUB_MM_ALIGN.  Return a non-NULL if successful,
++ * otherwise return NULL.
++ *
++ * Note: because in certain circumstances we need to adjust the ->next
++ * pointer of the previous block, we iterate over the singly linked
++ * list with the pair (prev, cur). *FIRST is our initial previous, and
++ * *FIRST->next is our initial current pointer. So we will actually
++ * allocate from *FIRST->next first and *FIRST itself last.
++ */
+ static void *
+ grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align)
+ {
+-  grub_mm_header_t p, q;
++  grub_mm_header_t cur, prev;
+ 
+   /* When everything is allocated side effect is that *first will have alloc
+      magic marked, meaning that there is no room in this region.  */
+@@ -192,24 +199,24 @@ grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align)
+     return 0;
+ 
+   /* Try to search free slot for allocation in this memory region.  */
+-  for (q = *first, p = q->next; ; q = p, p = p->next)
++  for (prev = *first, cur = prev->next; ; prev = cur, cur = cur->next)
+     {
+       grub_off_t extra;
+ 
+-      extra = ((grub_addr_t) (p + 1) >> GRUB_MM_ALIGN_LOG2) & (align - 1);
++      extra = ((grub_addr_t) (cur + 1) >> GRUB_MM_ALIGN_LOG2) & (align - 1);
+       if (extra)
+ 	extra = align - extra;
+ 
+-      if (! p)
++      if (! cur)
+ 	grub_fatal ("null in the ring");
+ 
+-      if (p->magic != GRUB_MM_FREE_MAGIC)
+-	grub_fatal ("free magic is broken at %p: 0x%x", p, p->magic);
++      if (cur->magic != GRUB_MM_FREE_MAGIC)
++	grub_fatal ("free magic is broken at %p: 0x%x", cur, cur->magic);
+ 
+-      if (p->size >= n + extra)
++      if (cur->size >= n + extra)
+ 	{
+-	  extra += (p->size - extra - n) & (~(align - 1));
+-	  if (extra == 0 && p->size == n)
++	  extra += (cur->size - extra - n) & (~(align - 1));
++	  if (extra == 0 && cur->size == n)
+ 	    {
+ 	      /* There is no special alignment requirement and memory block
+ 	         is complete match.
+@@ -222,9 +229,9 @@ grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align)
+ 	         | alloc, size=n |          |
+ 	         +---------------+          v
+ 	       */
+-	      q->next = p->next;
++	      prev->next = cur->next;
+ 	    }
+-	  else if (align == 1 || p->size == n + extra)
++	  else if (align == 1 || cur->size == n + extra)
+ 	    {
+ 	      /* There might be alignment requirement, when taking it into
+ 	         account memory block fits in.
+@@ -241,23 +248,22 @@ grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align)
+ 	         | alloc, size=n |        |
+ 	         +---------------+        v
+ 	       */
+-
+-	      p->size -= n;
+-	      p += p->size;
++	      cur->size -= n;
++	      cur += cur->size;
+ 	    }
+ 	  else if (extra == 0)
+ 	    {
+ 	      grub_mm_header_t r;
+ 	      
+-	      r = p + extra + n;
++	      r = cur + extra + n;
+ 	      r->magic = GRUB_MM_FREE_MAGIC;
+-	      r->size = p->size - extra - n;
+-	      r->next = p->next;
+-	      q->next = r;
++	      r->size = cur->size - extra - n;
++	      r->next = cur->next;
++	      prev->next = r;
+ 
+-	      if (q == p)
++	      if (prev == cur)
+ 		{
+-		  q = r;
++		  prev = r;
+ 		  r->next = r;
+ 		}
+ 	    }
+@@ -284,32 +290,32 @@ grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align)
+ 	       */
+ 	      grub_mm_header_t r;
+ 
+-	      r = p + extra + n;
++	      r = cur + extra + n;
+ 	      r->magic = GRUB_MM_FREE_MAGIC;
+-	      r->size = p->size - extra - n;
+-	      r->next = p;
++	      r->size = cur->size - extra - n;
++	      r->next = cur;
+ 
+-	      p->size = extra;
+-	      q->next = r;
+-	      p += extra;
++	      cur->size = extra;
++	      prev->next = r;
++	      cur += extra;
+ 	    }
+ 
+-	  p->magic = GRUB_MM_ALLOC_MAGIC;
+-	  p->size = n;
++	  cur->magic = GRUB_MM_ALLOC_MAGIC;
++	  cur->size = n;
+ 
+ 	  /* Mark find as a start marker for next allocation to fasten it.
+ 	     This will have side effect of fragmenting memory as small
+ 	     pieces before this will be un-used.  */
+ 	  /* So do it only for chunks under 64K.  */
+ 	  if (n < (0x8000 >> GRUB_MM_ALIGN_LOG2)
+-	      || *first == p)
+-	    *first = q;
++	      || *first == cur)
++	    *first = prev;
+ 
+-	  return p + 1;
++	  return cur + 1;
+ 	}
+ 
+       /* Search was completed without result.  */
+-      if (p == *first)
++      if (cur == *first)
+ 	break;
+     }
+ 
diff --git a/SOURCES/0294-mm-grub_real_malloc-Make-small-allocs-comment-match-.patch b/SOURCES/0294-mm-grub_real_malloc-Make-small-allocs-comment-match-.patch
new file mode 100644
index 0000000..a5601c9
--- /dev/null
+++ b/SOURCES/0294-mm-grub_real_malloc-Make-small-allocs-comment-match-.patch
@@ -0,0 +1,32 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Thu, 25 Nov 2021 02:22:47 +1100
+Subject: [PATCH] mm: grub_real_malloc(): Make small allocs comment match code
+
+Small allocations move the region's *first pointer. The comment
+says that this happens for allocations under 64K. The code says
+it's for allocations under 32K. Commit 45bf8b3a7549 changed the
+code intentionally: make the comment match.
+
+Fixes: 45bf8b3a7549 (* grub-core/kern/mm.c (grub_real_malloc): Decrease cut-off of moving the)
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit a847895a8d000bdf27ad4d4326f883a0eed769ca)
+---
+ grub-core/kern/mm.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
+index fb20e93acf..db7e0b2a5b 100644
+--- a/grub-core/kern/mm.c
++++ b/grub-core/kern/mm.c
+@@ -306,7 +306,7 @@ grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align)
+ 	  /* Mark find as a start marker for next allocation to fasten it.
+ 	     This will have side effect of fragmenting memory as small
+ 	     pieces before this will be un-used.  */
+-	  /* So do it only for chunks under 64K.  */
++	  /* So do it only for chunks under 32K.  */
+ 	  if (n < (0x8000 >> GRUB_MM_ALIGN_LOG2)
+ 	      || *first == cur)
+ 	    *first = prev;
diff --git a/SOURCES/0295-mm-Document-grub_free.patch b/SOURCES/0295-mm-Document-grub_free.patch
new file mode 100644
index 0000000..6c9b7cc
--- /dev/null
+++ b/SOURCES/0295-mm-Document-grub_free.patch
@@ -0,0 +1,120 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Thu, 25 Nov 2021 02:22:48 +1100
+Subject: [PATCH] mm: Document grub_free()
+
+The grub_free() possesses a surprising number of quirks, and also
+uses single-letter variable names confusingly to iterate through
+the free list.
+
+Document what's going on.
+
+Use prev and cur to iterate over the free list.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 1f8d0b01738e49767d662d6426af3570a64565f0)
+---
+ grub-core/kern/mm.c | 63 ++++++++++++++++++++++++++++++++++-------------------
+ 1 file changed, 41 insertions(+), 22 deletions(-)
+
+diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
+index db7e0b2a5b..0351171cf9 100644
+--- a/grub-core/kern/mm.c
++++ b/grub-core/kern/mm.c
+@@ -446,54 +446,73 @@ grub_free (void *ptr)
+     }
+   else
+     {
+-      grub_mm_header_t q, s;
++      grub_mm_header_t cur, prev;
+ 
+ #if 0
+-      q = r->first;
++      cur = r->first;
+       do
+ 	{
+ 	  grub_printf ("%s:%d: q=%p, q->size=0x%x, q->magic=0x%x\n",
+-		       GRUB_FILE, __LINE__, q, q->size, q->magic);
+-	  q = q->next;
++		       GRUB_FILE, __LINE__, cur, cur->size, cur->magic);
++	  cur = cur->next;
+ 	}
+-      while (q != r->first);
++      while (cur != r->first);
+ #endif
+-
+-      for (s = r->first, q = s->next; q <= p || q->next >= p; s = q, q = s->next)
++      /* Iterate over all blocks in the free ring.
++       *
++       * The free ring is arranged from high addresses to low
++       * addresses, modulo wraparound.
++       *
++       * We are looking for a block with a higher address than p or
++       * whose next address is lower than p.
++       */
++      for (prev = r->first, cur = prev->next; cur <= p || cur->next >= p;
++	   prev = cur, cur = prev->next)
+ 	{
+-	  if (q->magic != GRUB_MM_FREE_MAGIC)
+-	    grub_fatal ("free magic is broken at %p: 0x%x", q, q->magic);
++	  if (cur->magic != GRUB_MM_FREE_MAGIC)
++	    grub_fatal ("free magic is broken at %p: 0x%x", cur, cur->magic);
+ 
+-	  if (q <= q->next && (q > p || q->next < p))
++	  /* Deal with wrap-around */
++	  if (cur <= cur->next && (cur > p || cur->next < p))
+ 	    break;
+ 	}
+ 
++      /* mark p as free and insert it between cur and cur->next */
+       p->magic = GRUB_MM_FREE_MAGIC;
+-      p->next = q->next;
+-      q->next = p;
++      p->next = cur->next;
++      cur->next = p;
+ 
++      /*
++       * If the block we are freeing can be merged with the next
++       * free block, do that.
++       */
+       if (p->next + p->next->size == p)
+ 	{
+ 	  p->magic = 0;
+ 
+ 	  p->next->size += p->size;
+-	  q->next = p->next;
++	  cur->next = p->next;
+ 	  p = p->next;
+ 	}
+ 
+-      r->first = q;
++      r->first = cur;
+ 
+-      if (q == p + p->size)
++      /* Likewise if can be merged with the preceeding free block */
++      if (cur == p + p->size)
+ 	{
+-	  q->magic = 0;
+-	  p->size += q->size;
+-	  if (q == s)
+-	    s = p;
+-	  s->next = p;
+-	  q = s;
++	  cur->magic = 0;
++	  p->size += cur->size;
++	  if (cur == prev)
++	    prev = p;
++	  prev->next = p;
++	  cur = prev;
+ 	}
+ 
+-      r->first = q;
++      /*
++       * Set r->first such that the just free()d block is tried first.
++       * (An allocation is tried from *first->next, and cur->next == p.)
++       */
++      r->first = cur;
+     }
+ }
+ 
diff --git a/SOURCES/0296-mm-Document-grub_mm_init_region.patch b/SOURCES/0296-mm-Document-grub_mm_init_region.patch
new file mode 100644
index 0000000..0173f04
--- /dev/null
+++ b/SOURCES/0296-mm-Document-grub_mm_init_region.patch
@@ -0,0 +1,73 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Thu, 25 Nov 2021 02:22:49 +1100
+Subject: [PATCH] mm: Document grub_mm_init_region()
+
+The grub_mm_init_region() does some things that seem magical, especially
+around region merging. Make it a bit clearer.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 246d69b7ea619fc1e77dcc5960e37aea45a9808c)
+---
+ grub-core/kern/mm.c | 31 ++++++++++++++++++++++++++++++-
+ 1 file changed, 30 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
+index 0351171cf9..1cbf98c7ab 100644
+--- a/grub-core/kern/mm.c
++++ b/grub-core/kern/mm.c
+@@ -128,23 +128,52 @@ grub_mm_init_region (void *addr, grub_size_t size)
+   if (((grub_addr_t) addr + 0x1000) > ~(grub_addr_t) size)
+     size = ((grub_addr_t) -0x1000) - (grub_addr_t) addr;
+ 
++  /* Attempt to merge this region with every existing region */
+   for (p = &grub_mm_base, q = *p; q; p = &(q->next), q = *p)
++    /*
++     * Is the new region immediately below an existing region? That
++     * is, is the address of the memory we're adding now (addr) + size
++     * of the memory we're adding (size) + the bytes we couldn't use
++     * at the start of the region we're considering (q->pre_size)
++     * equal to the address of q? In other words, does the memory
++     * looks like this?
++     *
++     * addr                          q
++     *   |----size-----|-q->pre_size-|<q region>|
++     */
+     if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q)
+       {
++	/*
++	 * Yes, we can merge the memory starting at addr into the
++	 * existing region from below. Align up addr to GRUB_MM_ALIGN
++	 * so that our new region has proper alignment.
++	 */
+ 	r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
++	/* Copy the region data across */
+ 	*r = *q;
++	/* Consider all the new size as pre-size */
+ 	r->pre_size += size;
+-	
++
++	/*
++	 * If we have enough pre-size to create a block, create a
++	 * block with it. Mark it as allocated and pass it to
++	 * grub_free (), which will sort out getting it into the free
++	 * list.
++	 */
+ 	if (r->pre_size >> GRUB_MM_ALIGN_LOG2)
+ 	  {
+ 	    h = (grub_mm_header_t) (r + 1);
++	    /* block size is pre-size converted to cells */
+ 	    h->size = (r->pre_size >> GRUB_MM_ALIGN_LOG2);
+ 	    h->magic = GRUB_MM_ALLOC_MAGIC;
++	    /* region size grows by block size converted back to bytes */
+ 	    r->size += h->size << GRUB_MM_ALIGN_LOG2;
++	    /* adjust pre_size to be accurate */
+ 	    r->pre_size &= (GRUB_MM_ALIGN - 1);
+ 	    *p = r;
+ 	    grub_free (h + 1);
+ 	  }
++	/* Replace the old region with the new region */
+ 	*p = r;
+ 	return;
+       }
diff --git a/SOURCES/0297-mm-Document-GRUB-internal-memory-management-structur.patch b/SOURCES/0297-mm-Document-GRUB-internal-memory-management-structur.patch
new file mode 100644
index 0000000..c793926
--- /dev/null
+++ b/SOURCES/0297-mm-Document-GRUB-internal-memory-management-structur.patch
@@ -0,0 +1,78 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Thu, 25 Nov 2021 02:22:45 +1100
+Subject: [PATCH] mm: Document GRUB internal memory management structures
+
+I spent more than a trivial quantity of time figuring out pre_size and
+whether a memory region's size contains the header cell or not.
+
+Document the meanings of all the properties. Hopefully now no-one else
+has to figure it out!
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit a6c5c52ccffd2674d43db25fb4baa9c528526aa0)
+---
+ include/grub/mm_private.h | 28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+diff --git a/include/grub/mm_private.h b/include/grub/mm_private.h
+index c2c4cb1511..203533cc3d 100644
+--- a/include/grub/mm_private.h
++++ b/include/grub/mm_private.h
+@@ -21,15 +21,27 @@
+ 
+ #include <grub/mm.h>
+ 
++/* For context, see kern/mm.c */
++
+ /* Magic words.  */
+ #define GRUB_MM_FREE_MAGIC	0x2d3c2808
+ #define GRUB_MM_ALLOC_MAGIC	0x6db08fa4
+ 
++/* A header describing a block of memory - either allocated or free */
+ typedef struct grub_mm_header
+ {
++  /*
++   * The 'next' free block in this region's circular free list.
++   * Only meaningful if the block is free.
++   */
+   struct grub_mm_header *next;
++  /* The block size, not in bytes but the number of cells of
++   * GRUB_MM_ALIGN bytes. Includes the header cell.
++   */
+   grub_size_t size;
++  /* either free or alloc magic, depending on the block type. */
+   grub_size_t magic;
++  /* pad to cell size: see the top of kern/mm.c. */
+ #if GRUB_CPU_SIZEOF_VOID_P == 4
+   char padding[4];
+ #elif GRUB_CPU_SIZEOF_VOID_P == 8
+@@ -48,11 +60,27 @@ typedef struct grub_mm_header
+ 
+ #define GRUB_MM_ALIGN	(1 << GRUB_MM_ALIGN_LOG2)
+ 
++/* A region from which we can make allocations. */
+ typedef struct grub_mm_region
+ {
++  /* The first free block in this region. */
+   struct grub_mm_header *first;
++
++  /*
++   * The next region in the linked list of regions. Regions are initially
++   * sorted in order of increasing size, but can grow, in which case the
++   * ordering may not be preserved.
++   */
+   struct grub_mm_region *next;
++
++  /*
++   * A grub_mm_region will always be aligned to cell size. The pre-size is
++   * the number of bytes we were given but had to skip in order to get that
++   * alignment.
++   */
+   grub_size_t pre_size;
++
++  /* How many bytes are in this region? (free and allocated) */
+   grub_size_t size;
+ }
+ *grub_mm_region_t;
diff --git a/SOURCES/0298-mm-Assert-that-we-preserve-header-vs-region-alignmen.patch b/SOURCES/0298-mm-Assert-that-we-preserve-header-vs-region-alignmen.patch
new file mode 100644
index 0000000..2893784
--- /dev/null
+++ b/SOURCES/0298-mm-Assert-that-we-preserve-header-vs-region-alignmen.patch
@@ -0,0 +1,56 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Thu, 21 Apr 2022 15:24:14 +1000
+Subject: [PATCH] mm: Assert that we preserve header vs region alignment
+
+grub_mm_region_init() does:
+
+  h = (grub_mm_header_t) (r + 1);
+
+where h is a grub_mm_header_t and r is a grub_mm_region_t.
+
+Cells are supposed to be GRUB_MM_ALIGN aligned, but while grub_mm_dump
+ensures this vs the region header, grub_mm_region_init() does not.
+
+It's better to be explicit than implicit here: rather than changing
+grub_mm_region_init() to ALIGN_UP(), require that the struct is
+explicitly a multiple of the header size.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+Tested-by: Patrick Steinhardt <ps@pks.im>
+(cherry picked from commit 1df8fe66c57087eb33bd6dc69f786ed124615aa7)
+---
+ include/grub/mm_private.h | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/include/grub/mm_private.h b/include/grub/mm_private.h
+index 203533cc3d..a688b92a83 100644
+--- a/include/grub/mm_private.h
++++ b/include/grub/mm_private.h
+@@ -20,6 +20,7 @@
+ #define GRUB_MM_PRIVATE_H	1
+ 
+ #include <grub/mm.h>
++#include <grub/misc.h>
+ 
+ /* For context, see kern/mm.c */
+ 
+@@ -89,4 +90,17 @@ typedef struct grub_mm_region
+ extern grub_mm_region_t EXPORT_VAR (grub_mm_base);
+ #endif
+ 
++static inline void
++grub_mm_size_sanity_check (void) {
++  /* Ensure we preserve alignment when doing h = (grub_mm_header_t) (r + 1). */
++  COMPILE_TIME_ASSERT ((sizeof (struct grub_mm_region) %
++		        sizeof (struct grub_mm_header)) == 0);
++
++  /*
++   * GRUB_MM_ALIGN is supposed to represent cell size, and a mm_header is
++   * supposed to be 1 cell.
++   */
++  COMPILE_TIME_ASSERT (sizeof (struct grub_mm_header) == GRUB_MM_ALIGN);
++}
++
+ #endif
diff --git a/SOURCES/0299-mm-When-adding-a-region-merge-with-region-after-as-w.patch b/SOURCES/0299-mm-When-adding-a-region-merge-with-region-after-as-w.patch
new file mode 100644
index 0000000..52cfeee
--- /dev/null
+++ b/SOURCES/0299-mm-When-adding-a-region-merge-with-region-after-as-w.patch
@@ -0,0 +1,203 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Thu, 21 Apr 2022 15:24:15 +1000
+Subject: [PATCH] mm: When adding a region, merge with region after as well as
+ before
+
+On x86_64-efi (at least) regions seem to be added from top down. The mm
+code will merge a new region with an existing region that comes
+immediately before the new region. This allows larger allocations to be
+satisfied that would otherwise be the case.
+
+On powerpc-ieee1275, however, regions are added from bottom up. So if
+we add 3x 32MB regions, we can still only satisfy a 32MB allocation,
+rather than the 96MB allocation we might otherwise be able to satisfy.
+
+  * Define 'post_size' as being bytes lost to the end of an allocation
+    due to being given weird sizes from firmware that are not multiples
+    of GRUB_MM_ALIGN.
+
+  * Allow merging of regions immediately _after_ existing regions, not
+    just before. As with the other approach, we create an allocated
+    block to represent the new space and the pass it to grub_free() to
+    get the metadata right.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Tested-by: Stefan Berger <stefanb@linux.ibm.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+Tested-by: Patrick Steinhardt <ps@pks.im>
+(cherry picked from commit 052e6068be622ff53f1238b449c300dbd0a8abcd)
+---
+ grub-core/kern/mm.c       | 128 +++++++++++++++++++++++++++++-----------------
+ include/grub/mm_private.h |   9 ++++
+ 2 files changed, 91 insertions(+), 46 deletions(-)
+
+diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
+index 1cbf98c7ab..7be33e23bf 100644
+--- a/grub-core/kern/mm.c
++++ b/grub-core/kern/mm.c
+@@ -130,53 +130,88 @@ grub_mm_init_region (void *addr, grub_size_t size)
+ 
+   /* Attempt to merge this region with every existing region */
+   for (p = &grub_mm_base, q = *p; q; p = &(q->next), q = *p)
+-    /*
+-     * Is the new region immediately below an existing region? That
+-     * is, is the address of the memory we're adding now (addr) + size
+-     * of the memory we're adding (size) + the bytes we couldn't use
+-     * at the start of the region we're considering (q->pre_size)
+-     * equal to the address of q? In other words, does the memory
+-     * looks like this?
+-     *
+-     * addr                          q
+-     *   |----size-----|-q->pre_size-|<q region>|
+-     */
+-    if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q)
+-      {
+-	/*
+-	 * Yes, we can merge the memory starting at addr into the
+-	 * existing region from below. Align up addr to GRUB_MM_ALIGN
+-	 * so that our new region has proper alignment.
+-	 */
+-	r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
+-	/* Copy the region data across */
+-	*r = *q;
+-	/* Consider all the new size as pre-size */
+-	r->pre_size += size;
++    {
++      /*
++       * Is the new region immediately below an existing region? That
++       * is, is the address of the memory we're adding now (addr) + size
++       * of the memory we're adding (size) + the bytes we couldn't use
++       * at the start of the region we're considering (q->pre_size)
++       * equal to the address of q? In other words, does the memory
++       * looks like this?
++       *
++       * addr                          q
++       *   |----size-----|-q->pre_size-|<q region>|
++       */
++      if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q)
++        {
++          /*
++           * Yes, we can merge the memory starting at addr into the
++           * existing region from below. Align up addr to GRUB_MM_ALIGN
++           * so that our new region has proper alignment.
++           */
++          r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
++          /* Copy the region data across */
++          *r = *q;
++          /* Consider all the new size as pre-size */
++          r->pre_size += size;
+ 
+-	/*
+-	 * If we have enough pre-size to create a block, create a
+-	 * block with it. Mark it as allocated and pass it to
+-	 * grub_free (), which will sort out getting it into the free
+-	 * list.
+-	 */
+-	if (r->pre_size >> GRUB_MM_ALIGN_LOG2)
+-	  {
+-	    h = (grub_mm_header_t) (r + 1);
+-	    /* block size is pre-size converted to cells */
+-	    h->size = (r->pre_size >> GRUB_MM_ALIGN_LOG2);
+-	    h->magic = GRUB_MM_ALLOC_MAGIC;
+-	    /* region size grows by block size converted back to bytes */
+-	    r->size += h->size << GRUB_MM_ALIGN_LOG2;
+-	    /* adjust pre_size to be accurate */
+-	    r->pre_size &= (GRUB_MM_ALIGN - 1);
+-	    *p = r;
+-	    grub_free (h + 1);
+-	  }
+-	/* Replace the old region with the new region */
+-	*p = r;
+-	return;
+-      }
++          /*
++           * If we have enough pre-size to create a block, create a
++           * block with it. Mark it as allocated and pass it to
++           * grub_free (), which will sort out getting it into the free
++           * list.
++           */
++          if (r->pre_size >> GRUB_MM_ALIGN_LOG2)
++            {
++              h = (grub_mm_header_t) (r + 1);
++              /* block size is pre-size converted to cells */
++              h->size = (r->pre_size >> GRUB_MM_ALIGN_LOG2);
++              h->magic = GRUB_MM_ALLOC_MAGIC;
++              /* region size grows by block size converted back to bytes */
++              r->size += h->size << GRUB_MM_ALIGN_LOG2;
++              /* adjust pre_size to be accurate */
++              r->pre_size &= (GRUB_MM_ALIGN - 1);
++              *p = r;
++              grub_free (h + 1);
++            }
++          /* Replace the old region with the new region */
++          *p = r;
++          return;
++        }
++
++      /*
++       * Is the new region immediately above an existing region? That
++       * is:
++       *   q                       addr
++       *   |<q region>|-q->post_size-|----size-----|
++       */
++      if ((grub_uint8_t *) q + sizeof (*q) + q->size + q->post_size ==
++	  (grub_uint8_t *) addr)
++	{
++	  /*
++	   * Yes! Follow a similar pattern to above, but simpler.
++	   * Our header starts at address - post_size, which should align us
++	   * to a cell boundary.
++	   *
++	   * Cast to (void *) first to avoid the following build error:
++	   *   kern/mm.c: In function ‘grub_mm_init_region’:
++	   *   kern/mm.c:211:15: error: cast increases required alignment of target type [-Werror=cast-align]
++	   *     211 |           h = (grub_mm_header_t) ((grub_uint8_t *) addr - q->post_size);
++	   *         |               ^
++	   * It is safe to do that because proper alignment is enforced in grub_mm_size_sanity_check().
++	   */
++	  h = (grub_mm_header_t)(void *) ((grub_uint8_t *) addr - q->post_size);
++	  /* our size is the allocated size plus post_size, in cells */
++	  h->size = (size + q->post_size) >> GRUB_MM_ALIGN_LOG2;
++	  h->magic = GRUB_MM_ALLOC_MAGIC;
++	  /* region size grows by block size converted back to bytes */
++	  q->size += h->size << GRUB_MM_ALIGN_LOG2;
++	  /* adjust new post_size to be accurate */
++	  q->post_size = (q->post_size + size) & (GRUB_MM_ALIGN - 1);
++	  grub_free (h + 1);
++	  return;
++	}
++    }
+ 
+   /* Allocate a region from the head.  */
+   r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
+@@ -195,6 +230,7 @@ grub_mm_init_region (void *addr, grub_size_t size)
+   r->first = h;
+   r->pre_size = (grub_addr_t) r - (grub_addr_t) addr;
+   r->size = (h->size << GRUB_MM_ALIGN_LOG2);
++  r->post_size = size - r->size;
+ 
+   /* Find where to insert this region. Put a smaller one before bigger ones,
+      to prevent fragmentation.  */
+diff --git a/include/grub/mm_private.h b/include/grub/mm_private.h
+index a688b92a83..96c2d816be 100644
+--- a/include/grub/mm_private.h
++++ b/include/grub/mm_private.h
+@@ -81,8 +81,17 @@ typedef struct grub_mm_region
+    */
+   grub_size_t pre_size;
+ 
++  /*
++   * Likewise, the post-size is the number of bytes we wasted at the end
++   * of the allocation because it wasn't a multiple of GRUB_MM_ALIGN
++   */
++  grub_size_t post_size;
++
+   /* How many bytes are in this region? (free and allocated) */
+   grub_size_t size;
++
++  /* pad to a multiple of cell size */
++  char padding[3 * GRUB_CPU_SIZEOF_VOID_P];
+ }
+ *grub_mm_region_t;
+ 
diff --git a/SOURCES/0300-mm-Debug-support-for-region-operations.patch b/SOURCES/0300-mm-Debug-support-for-region-operations.patch
new file mode 100644
index 0000000..f434260
--- /dev/null
+++ b/SOURCES/0300-mm-Debug-support-for-region-operations.patch
@@ -0,0 +1,71 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Thu, 21 Apr 2022 15:24:16 +1000
+Subject: [PATCH] mm: Debug support for region operations
+
+This is handy for debugging. Enable with "set debug=regions".
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+Tested-by: Patrick Steinhardt <ps@pks.im>
+(cherry picked from commit 8afa5ef45b797ba5d8147ceee85ac2c59dcc7f09)
+---
+ grub-core/kern/mm.c | 19 ++++++++++++++++---
+ 1 file changed, 16 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
+index 7be33e23bf..38bfb01df9 100644
+--- a/grub-core/kern/mm.c
++++ b/grub-core/kern/mm.c
+@@ -115,9 +115,8 @@ grub_mm_init_region (void *addr, grub_size_t size)
+   grub_mm_header_t h;
+   grub_mm_region_t r, *p, q;
+ 
+-#if 0
+-  grub_printf ("Using memory for heap: start=%p, end=%p\n", addr, addr + (unsigned int) size);
+-#endif
++  grub_dprintf ("regions", "Using memory for heap: start=%p, end=%p\n",
++                addr, (char *) addr + (unsigned int) size);
+ 
+   /* Exclude last 4K to avoid overflows. */
+   /* If addr + 0x1000 overflows then whole region is in excluded zone.  */
+@@ -142,8 +141,14 @@ grub_mm_init_region (void *addr, grub_size_t size)
+        * addr                          q
+        *   |----size-----|-q->pre_size-|<q region>|
+        */
++      grub_dprintf ("regions", "Can we extend into region above?"
++		    " %p + %" PRIxGRUB_SIZE " + %" PRIxGRUB_SIZE " ?=? %p\n",
++		    (grub_uint8_t *) addr, size, q->pre_size, (grub_uint8_t *) q);
+       if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q)
+         {
++	  grub_dprintf ("regions", "Yes: extending a region: (%p -> %p) -> (%p -> %p)\n",
++			q, (grub_uint8_t *) q + sizeof (*q) + q->size,
++			addr, (grub_uint8_t *) q + sizeof (*q) + q->size);
+           /*
+            * Yes, we can merge the memory starting at addr into the
+            * existing region from below. Align up addr to GRUB_MM_ALIGN
+@@ -185,9 +190,15 @@ grub_mm_init_region (void *addr, grub_size_t size)
+        *   q                       addr
+        *   |<q region>|-q->post_size-|----size-----|
+        */
++      grub_dprintf ("regions", "Can we extend into region below?"
++                    " %p + %" PRIxGRUB_SIZE " + %" PRIxGRUB_SIZE " + %" PRIxGRUB_SIZE " ?=? %p\n",
++                    (grub_uint8_t *) q, sizeof(*q), q->size, q->post_size, (grub_uint8_t *) addr);
+       if ((grub_uint8_t *) q + sizeof (*q) + q->size + q->post_size ==
+ 	  (grub_uint8_t *) addr)
+ 	{
++	  grub_dprintf ("regions", "Yes: extending a region: (%p -> %p) -> (%p -> %p)\n",
++			q, (grub_uint8_t *) q + sizeof (*q) + q->size,
++			q, (grub_uint8_t *) addr + size);
+ 	  /*
+ 	   * Yes! Follow a similar pattern to above, but simpler.
+ 	   * Our header starts at address - post_size, which should align us
+@@ -213,6 +224,8 @@ grub_mm_init_region (void *addr, grub_size_t size)
+ 	}
+     }
+ 
++  grub_dprintf ("regions", "No: considering a new region at %p of size %" PRIxGRUB_SIZE "\n",
++		addr, size);
+   /* Allocate a region from the head.  */
+   r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
+ 
diff --git a/SOURCES/0301-mm-Drop-unused-unloading-of-modules-on-OOM.patch b/SOURCES/0301-mm-Drop-unused-unloading-of-modules-on-OOM.patch
new file mode 100644
index 0000000..a18d912
--- /dev/null
+++ b/SOURCES/0301-mm-Drop-unused-unloading-of-modules-on-OOM.patch
@@ -0,0 +1,80 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Patrick Steinhardt <ps@pks.im>
+Date: Thu, 21 Apr 2022 15:24:17 +1000
+Subject: [PATCH] mm: Drop unused unloading of modules on OOM
+
+In grub_memalign(), there's a commented section which would allow for
+unloading of unneeded modules in case where there is not enough free
+memory available to satisfy a request. Given that this code is never
+compiled in, let's remove it together with grub_dl_unload_unneeded().
+
+Signed-off-by: Patrick Steinhardt <ps@pks.im>
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+Tested-by: Patrick Steinhardt <ps@pks.im>
+(cherry picked from commit 139fd9b134a01e0b5fe0ebefafa7f48d1ffb6d60)
+---
+ grub-core/kern/dl.c | 20 --------------------
+ grub-core/kern/mm.c |  8 --------
+ include/grub/dl.h   |  1 -
+ 3 files changed, 29 deletions(-)
+
+diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
+index d5de80186f..ab9101a5ad 100644
+--- a/grub-core/kern/dl.c
++++ b/grub-core/kern/dl.c
+@@ -998,23 +998,3 @@ grub_dl_unload (grub_dl_t mod)
+   grub_free (mod);
+   return 1;
+ }
+-
+-/* Unload unneeded modules.  */
+-void
+-grub_dl_unload_unneeded (void)
+-{
+-  /* Because grub_dl_remove modifies the list of modules, this
+-     implementation is tricky.  */
+-  grub_dl_t p = grub_dl_head;
+-
+-  while (p)
+-    {
+-      if (grub_dl_unload (p))
+-	{
+-	  p = grub_dl_head;
+-	  continue;
+-	}
+-
+-      p = p->next;
+-    }
+-}
+diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
+index 38bfb01df9..1825dc8289 100644
+--- a/grub-core/kern/mm.c
++++ b/grub-core/kern/mm.c
+@@ -444,14 +444,6 @@ grub_memalign (grub_size_t align, grub_size_t size)
+       count++;
+       goto again;
+ 
+-#if 0
+-    case 1:
+-      /* Unload unneeded modules.  */
+-      grub_dl_unload_unneeded ();
+-      count++;
+-      goto again;
+-#endif
+-
+     default:
+       break;
+     }
+diff --git a/include/grub/dl.h b/include/grub/dl.h
+index 45ac8e339f..6bc2560bf0 100644
+--- a/include/grub/dl.h
++++ b/include/grub/dl.h
+@@ -206,7 +206,6 @@ grub_dl_t EXPORT_FUNC(grub_dl_load) (const char *name);
+ grub_dl_t grub_dl_load_core (void *addr, grub_size_t size);
+ grub_dl_t EXPORT_FUNC(grub_dl_load_core_noinit) (void *addr, grub_size_t size);
+ int EXPORT_FUNC(grub_dl_unload) (grub_dl_t mod);
+-extern void grub_dl_unload_unneeded (void);
+ extern int EXPORT_FUNC(grub_dl_ref) (grub_dl_t mod);
+ extern int EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod);
+ extern int EXPORT_FUNC(grub_dl_ref_count) (grub_dl_t mod);
diff --git a/SOURCES/0302-mm-Allow-dynamically-requesting-additional-memory-re.patch b/SOURCES/0302-mm-Allow-dynamically-requesting-additional-memory-re.patch
new file mode 100644
index 0000000..d225c10
--- /dev/null
+++ b/SOURCES/0302-mm-Allow-dynamically-requesting-additional-memory-re.patch
@@ -0,0 +1,130 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Patrick Steinhardt <ps@pks.im>
+Date: Thu, 21 Apr 2022 15:24:18 +1000
+Subject: [PATCH] mm: Allow dynamically requesting additional memory regions
+
+Currently, all platforms will set up their heap on initialization of the
+platform code. While this works mostly fine, it poses some limitations
+on memory management on us. Most notably, allocating big chunks of
+memory in the gigabyte range would require us to pre-request this many
+bytes from the firmware and add it to the heap from the beginning on
+some platforms like EFI. As this isn't needed for most configurations,
+it is inefficient and may even negatively impact some usecases when,
+e.g., chainloading. Nonetheless, allocating big chunks of memory is
+required sometimes, where one example is the upcoming support for the
+Argon2 key derival function in LUKS2.
+
+In order to avoid pre-allocating big chunks of memory, this commit
+implements a runtime mechanism to add more pages to the system. When
+a given allocation cannot be currently satisfied, we'll call a given
+callback set up by the platform's own memory management subsystem,
+asking it to add a memory area with at least "n" bytes. If this
+succeeds, we retry searching for a valid memory region, which should
+now succeed.
+
+If this fails, we try asking for "n" bytes, possibly spread across
+multiple regions, in hopes that region merging means that we end up
+with enough memory for things to work out.
+
+Signed-off-by: Patrick Steinhardt <ps@pks.im>
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Tested-by: Stefan Berger <stefanb@linux.ibm.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+Tested-by: Patrick Steinhardt <ps@pks.im>
+(cherry picked from commit 887f98f0db43e33fba4ec1f85e42fae1185700bc)
+---
+ grub-core/kern/mm.c | 30 ++++++++++++++++++++++++++++++
+ include/grub/mm.h   | 18 ++++++++++++++++++
+ 2 files changed, 48 insertions(+)
+
+diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
+index 1825dc8289..f2e27f263b 100644
+--- a/grub-core/kern/mm.c
++++ b/grub-core/kern/mm.c
+@@ -28,6 +28,9 @@
+   - multiple regions may be used as free space. They may not be
+   contiguous.
+ 
++  - if existing regions are insufficient to satisfy an allocation, a new
++  region can be requested from firmware.
++
+   Regions are managed by a singly linked list, and the meta information is
+   stored in the beginning of each region. Space after the meta information
+   is used to allocate memory.
+@@ -81,6 +84,7 @@
+ 
+ 
+ grub_mm_region_t grub_mm_base;
++grub_mm_add_region_func_t grub_mm_add_region_fn;
+ 
+ /* Get a header from the pointer PTR, and set *P and *R to a pointer
+    to the header and a pointer to its region, respectively. PTR must
+@@ -444,6 +448,32 @@ grub_memalign (grub_size_t align, grub_size_t size)
+       count++;
+       goto again;
+ 
++    case 1:
++      /* Request additional pages, contiguous */
++      count++;
++
++      if (grub_mm_add_region_fn != NULL &&
++          grub_mm_add_region_fn (size, GRUB_MM_ADD_REGION_CONSECUTIVE) == GRUB_ERR_NONE)
++	goto again;
++
++      /* fallthrough  */
++
++    case 2:
++      /* Request additional pages, anything at all */
++      count++;
++
++      if (grub_mm_add_region_fn != NULL)
++        {
++          /*
++           * Try again even if this fails, in case it was able to partially
++           * satisfy the request
++           */
++          grub_mm_add_region_fn (size, GRUB_MM_ADD_REGION_NONE);
++          goto again;
++        }
++
++      /* fallthrough */
++
+     default:
+       break;
+     }
+diff --git a/include/grub/mm.h b/include/grub/mm.h
+index d81623d226..7c6f925ffd 100644
+--- a/include/grub/mm.h
++++ b/include/grub/mm.h
+@@ -20,6 +20,7 @@
+ #ifndef GRUB_MM_H
+ #define GRUB_MM_H	1
+ 
++#include <grub/err.h>
+ #include <grub/types.h>
+ #include <grub/symbol.h>
+ #include <grub/err.h>
+@@ -29,6 +30,23 @@
+ # define NULL	((void *) 0)
+ #endif
+ 
++#define GRUB_MM_ADD_REGION_NONE        0
++#define GRUB_MM_ADD_REGION_CONSECUTIVE (1 << 0)
++
++/*
++ * Function used to request memory regions of `grub_size_t` bytes. The second
++ * parameter is a bitfield of `GRUB_MM_ADD_REGION` flags.
++ */
++typedef grub_err_t (*grub_mm_add_region_func_t) (grub_size_t, unsigned int);
++
++/*
++ * Set this function pointer to enable adding memory-regions at runtime in case
++ * a memory allocation cannot be satisfied with existing regions.
++ */
++#ifndef GRUB_MACHINE_EMU
++extern grub_mm_add_region_func_t EXPORT_VAR(grub_mm_add_region_fn);
++#endif
++
+ void grub_mm_init_region (void *addr, grub_size_t size);
+ void *EXPORT_FUNC(grub_calloc) (grub_size_t nmemb, grub_size_t size);
+ void *EXPORT_FUNC(grub_malloc) (grub_size_t size);
diff --git a/SOURCES/0303-kern-efi-mm-Always-request-a-fixed-number-of-pages-o.patch b/SOURCES/0303-kern-efi-mm-Always-request-a-fixed-number-of-pages-o.patch
new file mode 100644
index 0000000..6fa378c
--- /dev/null
+++ b/SOURCES/0303-kern-efi-mm-Always-request-a-fixed-number-of-pages-o.patch
@@ -0,0 +1,103 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Patrick Steinhardt <ps@pks.im>
+Date: Thu, 21 Apr 2022 15:24:19 +1000
+Subject: [PATCH] kern/efi/mm: Always request a fixed number of pages on init
+
+When initializing the EFI memory subsystem, we will by default request
+a quarter of the available memory, bounded by a minimum/maximum value.
+Given that we're about to extend the EFI memory system to dynamically
+request additional pages from the firmware as required, this scaling of
+requested memory based on available memory will not make a lot of sense
+anymore.
+
+Remove this logic as a preparatory patch such that we'll instead defer
+to the runtime memory allocator. Note that ideally, we'd want to change
+this after dynamic requesting of pages has been implemented for the EFI
+platform. But because we'll need to split up initialization of the
+memory subsystem and the request of pages from the firmware, we'd have
+to duplicate quite some logic at first only to remove it afterwards
+again. This seems quite pointless, so we instead have patches slightly
+out of order.
+
+Signed-off-by: Patrick Steinhardt <ps@pks.im>
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+Tested-by: Patrick Steinhardt <ps@pks.im>
+(cherry picked from commit 938c3760b8c0fca759140be48307179b50107ff6)
+---
+ grub-core/kern/efi/mm.c | 35 +++--------------------------------
+ 1 file changed, 3 insertions(+), 32 deletions(-)
+
+diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
+index e460b072e6..782a1365a1 100644
+--- a/grub-core/kern/efi/mm.c
++++ b/grub-core/kern/efi/mm.c
+@@ -38,9 +38,8 @@
+    a multiplier of 4KB.  */
+ #define MEMORY_MAP_SIZE	0x3000
+ 
+-/* The minimum and maximum heap size for GRUB itself.  */
+-#define MIN_HEAP_SIZE	0x100000
+-#define MAX_HEAP_SIZE	(1600 * 0x100000)
++/* The default heap size for GRUB itself in bytes.  */
++#define DEFAULT_HEAP_SIZE	0x100000
+ 
+ static void *finish_mmap_buf = 0;
+ static grub_efi_uintn_t finish_mmap_size = 0;
+@@ -514,23 +513,6 @@ filter_memory_map (grub_efi_memory_descriptor_t *memory_map,
+   return filtered_desc;
+ }
+ 
+-/* Return the total number of pages.  */
+-static grub_efi_uint64_t
+-get_total_pages (grub_efi_memory_descriptor_t *memory_map,
+-		 grub_efi_uintn_t desc_size,
+-		 grub_efi_memory_descriptor_t *memory_map_end)
+-{
+-  grub_efi_memory_descriptor_t *desc;
+-  grub_efi_uint64_t total = 0;
+-
+-  for (desc = memory_map;
+-       desc < memory_map_end;
+-       desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
+-    total += desc->num_pages;
+-
+-  return total;
+-}
+-
+ /* Add memory regions.  */
+ static void
+ add_memory_regions (grub_efi_memory_descriptor_t *memory_map,
+@@ -694,8 +676,6 @@ grub_efi_mm_init (void)
+   grub_efi_memory_descriptor_t *filtered_memory_map_end;
+   grub_efi_uintn_t map_size;
+   grub_efi_uintn_t desc_size;
+-  grub_efi_uint64_t total_pages;
+-  grub_efi_uint64_t required_pages;
+   int mm_status;
+ 
+   grub_nx_init ();
+@@ -737,22 +717,13 @@ grub_efi_mm_init (void)
+   filtered_memory_map_end = filter_memory_map (memory_map, filtered_memory_map,
+ 					       desc_size, memory_map_end);
+ 
+-  /* By default, request a quarter of the available memory.  */
+-  total_pages = get_total_pages (filtered_memory_map, desc_size,
+-				 filtered_memory_map_end);
+-  required_pages = (total_pages >> 2);
+-  if (required_pages < BYTES_TO_PAGES (MIN_HEAP_SIZE))
+-    required_pages = BYTES_TO_PAGES (MIN_HEAP_SIZE);
+-  else if (required_pages > BYTES_TO_PAGES (MAX_HEAP_SIZE))
+-    required_pages = BYTES_TO_PAGES (MAX_HEAP_SIZE);
+-
+   /* Sort the filtered descriptors, so that GRUB can allocate pages
+      from smaller regions.  */
+   sort_memory_map (filtered_memory_map, desc_size, filtered_memory_map_end);
+ 
+   /* Allocate memory regions for GRUB's memory management.  */
+   add_memory_regions (filtered_memory_map, desc_size,
+-		      filtered_memory_map_end, required_pages);
++		      filtered_memory_map_end, BYTES_TO_PAGES (DEFAULT_HEAP_SIZE));
+ 
+ #if 0
+   /* For debug.  */
diff --git a/SOURCES/0304-kern-efi-mm-Extract-function-to-add-memory-regions.patch b/SOURCES/0304-kern-efi-mm-Extract-function-to-add-memory-regions.patch
new file mode 100644
index 0000000..28cf2e7
--- /dev/null
+++ b/SOURCES/0304-kern-efi-mm-Extract-function-to-add-memory-regions.patch
@@ -0,0 +1,85 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Patrick Steinhardt <ps@pks.im>
+Date: Thu, 21 Apr 2022 15:24:20 +1000
+Subject: [PATCH] kern/efi/mm: Extract function to add memory regions
+
+In preparation of support for runtime-allocating additional memory
+region, this patch extracts the function to retrieve the EFI memory
+map and add a subset of it to GRUB's own memory regions.
+
+Signed-off-by: Patrick Steinhardt <ps@pks.im>
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+Tested-by: Patrick Steinhardt <ps@pks.im>
+(cherry picked from commit 96a7ea29e3cb61b6c2302e260e8e6a6117e17fa3)
+[rharwood: backport around our nx]
+---
+ grub-core/kern/efi/mm.c | 21 +++++++++++++++------
+ 1 file changed, 15 insertions(+), 6 deletions(-)
+
+diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
+index 782a1365a1..a1d3b51fe6 100644
+--- a/grub-core/kern/efi/mm.c
++++ b/grub-core/kern/efi/mm.c
+@@ -667,8 +667,8 @@ grub_nx_init (void)
+     }
+ }
+ 
+-void
+-grub_efi_mm_init (void)
++static grub_err_t
++grub_efi_mm_add_regions (grub_size_t required_bytes)
+ {
+   grub_efi_memory_descriptor_t *memory_map;
+   grub_efi_memory_descriptor_t *memory_map_end;
+@@ -683,7 +683,7 @@ grub_efi_mm_init (void)
+   /* Prepare a memory region to store two memory maps.  */
+   memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
+   if (! memory_map)
+-    grub_fatal ("cannot allocate memory");
++    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory for memory map");
+ 
+   /* Obtain descriptors for available memory.  */
+   map_size = MEMORY_MAP_SIZE;
+@@ -701,14 +701,14 @@ grub_efi_mm_init (void)
+ 
+       memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (map_size));
+       if (! memory_map)
+-	grub_fatal ("cannot allocate memory");
++	return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory for new memory map");
+ 
+       mm_status = grub_efi_get_memory_map (&map_size, memory_map, 0,
+ 					   &desc_size, 0);
+     }
+ 
+   if (mm_status < 0)
+-    grub_fatal ("cannot get memory map");
++    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "error fetching memory map from EFI");
+ 
+   memory_map_end = NEXT_MEMORY_DESCRIPTOR (memory_map, map_size);
+ 
+@@ -723,7 +723,7 @@ grub_efi_mm_init (void)
+ 
+   /* Allocate memory regions for GRUB's memory management.  */
+   add_memory_regions (filtered_memory_map, desc_size,
+-		      filtered_memory_map_end, BYTES_TO_PAGES (DEFAULT_HEAP_SIZE));
++		      filtered_memory_map_end, BYTES_TO_PAGES (required_bytes));
+ 
+ #if 0
+   /* For debug.  */
+@@ -741,6 +741,15 @@ grub_efi_mm_init (void)
+   /* Release the memory maps.  */
+   grub_efi_free_pages ((grub_addr_t) memory_map,
+ 		       2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
++
++  return GRUB_ERR_NONE;
++}
++
++void
++grub_efi_mm_init (void)
++{
++  if (grub_efi_mm_add_regions (DEFAULT_HEAP_SIZE) != GRUB_ERR_NONE)
++    grub_fatal ("%s", grub_errmsg);
+ }
+ 
+ #if defined (__aarch64__) || defined (__arm__) || defined (__riscv)
diff --git a/SOURCES/0305-kern-efi-mm-Pass-up-errors-from-add_memory_regions.patch b/SOURCES/0305-kern-efi-mm-Pass-up-errors-from-add_memory_regions.patch
new file mode 100644
index 0000000..b4b7891
--- /dev/null
+++ b/SOURCES/0305-kern-efi-mm-Pass-up-errors-from-add_memory_regions.patch
@@ -0,0 +1,87 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Patrick Steinhardt <ps@pks.im>
+Date: Thu, 21 Apr 2022 15:24:21 +1000
+Subject: [PATCH] kern/efi/mm: Pass up errors from add_memory_regions()
+
+The function add_memory_regions() is currently only called on system
+initialization to allocate a fixed amount of pages. As such, it didn't
+need to return any errors: in case it failed, we cannot proceed anyway.
+This will change with the upcoming support for requesting more memory
+from the firmware at runtime, where it doesn't make sense anymore to
+fail hard.
+
+Refactor the function to return an error to prepare for this. Note that
+this does not change the behaviour when initializing the memory system
+because grub_efi_mm_init() knows to call grub_fatal() in case
+grub_efi_mm_add_regions() returns an error.
+
+Signed-off-by: Patrick Steinhardt <ps@pks.im>
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+Tested-by: Patrick Steinhardt <ps@pks.im>
+(cherry picked from commit 15a015698921240adc1ac266a3b5bc5fcbd81521)
+---
+ grub-core/kern/efi/mm.c | 22 +++++++++++++++-------
+ 1 file changed, 15 insertions(+), 7 deletions(-)
+
+diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
+index a1d3b51fe6..e0ebc65dba 100644
+--- a/grub-core/kern/efi/mm.c
++++ b/grub-core/kern/efi/mm.c
+@@ -514,7 +514,7 @@ filter_memory_map (grub_efi_memory_descriptor_t *memory_map,
+ }
+ 
+ /* Add memory regions.  */
+-static void
++static grub_err_t
+ add_memory_regions (grub_efi_memory_descriptor_t *memory_map,
+ 		    grub_efi_uintn_t desc_size,
+ 		    grub_efi_memory_descriptor_t *memory_map_end,
+@@ -542,9 +542,9 @@ add_memory_regions (grub_efi_memory_descriptor_t *memory_map,
+ 					   GRUB_EFI_ALLOCATE_ADDRESS,
+ 					   GRUB_EFI_LOADER_CODE);      
+       if (! addr)
+-	grub_fatal ("cannot allocate conventional memory %p with %u pages",
+-		    (void *) ((grub_addr_t) start),
+-		    (unsigned) pages);
++	return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++			    "Memory starting at %p (%u pages) marked as free, but EFI would not allocate",
++			    (void *) ((grub_addr_t) start), (unsigned) pages);
+ 
+       grub_mm_init_region (addr, PAGES_TO_BYTES (pages));
+ 
+@@ -554,7 +554,11 @@ add_memory_regions (grub_efi_memory_descriptor_t *memory_map,
+     }
+ 
+   if (required_pages > 0)
+-    grub_fatal ("too little memory");
++    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
++                       "could not allocate all requested memory: %" PRIuGRUB_UINT64_T " pages still required after iterating EFI memory map",
++                       required_pages);
++
++  return GRUB_ERR_NONE;
+ }
+ 
+ void
+@@ -676,6 +680,7 @@ grub_efi_mm_add_regions (grub_size_t required_bytes)
+   grub_efi_memory_descriptor_t *filtered_memory_map_end;
+   grub_efi_uintn_t map_size;
+   grub_efi_uintn_t desc_size;
++  grub_err_t err;
+   int mm_status;
+ 
+   grub_nx_init ();
+@@ -722,8 +727,11 @@ grub_efi_mm_add_regions (grub_size_t required_bytes)
+   sort_memory_map (filtered_memory_map, desc_size, filtered_memory_map_end);
+ 
+   /* Allocate memory regions for GRUB's memory management.  */
+-  add_memory_regions (filtered_memory_map, desc_size,
+-		      filtered_memory_map_end, BYTES_TO_PAGES (required_bytes));
++  err = add_memory_regions (filtered_memory_map, desc_size,
++			    filtered_memory_map_end,
++			    BYTES_TO_PAGES (required_bytes));
++  if (err != GRUB_ERR_NONE)
++    return err;
+ 
+ #if 0
+   /* For debug.  */
diff --git a/SOURCES/0306-kern-efi-mm-Implement-runtime-addition-of-pages.patch b/SOURCES/0306-kern-efi-mm-Implement-runtime-addition-of-pages.patch
new file mode 100644
index 0000000..1f52581
--- /dev/null
+++ b/SOURCES/0306-kern-efi-mm-Implement-runtime-addition-of-pages.patch
@@ -0,0 +1,75 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Patrick Steinhardt <ps@pks.im>
+Date: Thu, 21 Apr 2022 15:24:22 +1000
+Subject: [PATCH] kern/efi/mm: Implement runtime addition of pages
+
+Adjust the interface of grub_efi_mm_add_regions() to take a set of
+GRUB_MM_ADD_REGION_* flags, which most notably is currently only the
+GRUB_MM_ADD_REGION_CONSECUTIVE flag. This allows us to set the function
+up as callback for the memory subsystem and have it call out to us in
+case there's not enough pages available in the current heap.
+
+Signed-off-by: Patrick Steinhardt <ps@pks.im>
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+Tested-by: Patrick Steinhardt <ps@pks.im>
+(cherry picked from commit 1df2934822df4c1170dde069d97cfbf7a9572bba)
+---
+ grub-core/kern/efi/mm.c | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
+index e0ebc65dba..016ba6cf2f 100644
+--- a/grub-core/kern/efi/mm.c
++++ b/grub-core/kern/efi/mm.c
+@@ -518,7 +518,8 @@ static grub_err_t
+ add_memory_regions (grub_efi_memory_descriptor_t *memory_map,
+ 		    grub_efi_uintn_t desc_size,
+ 		    grub_efi_memory_descriptor_t *memory_map_end,
+-		    grub_efi_uint64_t required_pages)
++		    grub_efi_uint64_t required_pages,
++		    unsigned int flags)
+ {
+   grub_efi_memory_descriptor_t *desc;
+ 
+@@ -532,6 +533,10 @@ add_memory_regions (grub_efi_memory_descriptor_t *memory_map,
+ 
+       start = desc->physical_start;
+       pages = desc->num_pages;
++
++      if (pages < required_pages && (flags & GRUB_MM_ADD_REGION_CONSECUTIVE))
++	continue;
++
+       if (pages > required_pages)
+ 	{
+ 	  start += PAGES_TO_BYTES (pages - required_pages);
+@@ -672,7 +677,7 @@ grub_nx_init (void)
+ }
+ 
+ static grub_err_t
+-grub_efi_mm_add_regions (grub_size_t required_bytes)
++grub_efi_mm_add_regions (grub_size_t required_bytes, unsigned int flags)
+ {
+   grub_efi_memory_descriptor_t *memory_map;
+   grub_efi_memory_descriptor_t *memory_map_end;
+@@ -729,7 +734,8 @@ grub_efi_mm_add_regions (grub_size_t required_bytes)
+   /* Allocate memory regions for GRUB's memory management.  */
+   err = add_memory_regions (filtered_memory_map, desc_size,
+ 			    filtered_memory_map_end,
+-			    BYTES_TO_PAGES (required_bytes));
++			    BYTES_TO_PAGES (required_bytes),
++			    flags);
+   if (err != GRUB_ERR_NONE)
+     return err;
+ 
+@@ -756,8 +762,9 @@ grub_efi_mm_add_regions (grub_size_t required_bytes)
+ void
+ grub_efi_mm_init (void)
+ {
+-  if (grub_efi_mm_add_regions (DEFAULT_HEAP_SIZE) != GRUB_ERR_NONE)
++  if (grub_efi_mm_add_regions (DEFAULT_HEAP_SIZE, GRUB_MM_ADD_REGION_NONE) != GRUB_ERR_NONE)
+     grub_fatal ("%s", grub_errmsg);
++  grub_mm_add_region_fn = grub_efi_mm_add_regions;
+ }
+ 
+ #if defined (__aarch64__) || defined (__arm__) || defined (__riscv)
diff --git a/SOURCES/0307-efi-Increase-default-memory-allocation-to-32-MiB.patch b/SOURCES/0307-efi-Increase-default-memory-allocation-to-32-MiB.patch
new file mode 100644
index 0000000..b70c3cc
--- /dev/null
+++ b/SOURCES/0307-efi-Increase-default-memory-allocation-to-32-MiB.patch
@@ -0,0 +1,31 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Tue, 20 Sep 2022 00:30:30 +1000
+Subject: [PATCH] efi: Increase default memory allocation to 32 MiB
+
+We have multiple reports of things being slower with a 1 MiB initial static
+allocation, and a report (more difficult to nail down) of a boot failure
+as a result of the smaller initial allocation.
+
+Make the initial memory allocation 32 MiB.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 75e38e86e7d9202f050b093f20500d9ad4c6dad9)
+---
+ grub-core/kern/efi/mm.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
+index 016ba6cf2f..b27e966e1f 100644
+--- a/grub-core/kern/efi/mm.c
++++ b/grub-core/kern/efi/mm.c
+@@ -39,7 +39,7 @@
+ #define MEMORY_MAP_SIZE	0x3000
+ 
+ /* The default heap size for GRUB itself in bytes.  */
+-#define DEFAULT_HEAP_SIZE	0x100000
++#define DEFAULT_HEAP_SIZE	0x2000000
+ 
+ static void *finish_mmap_buf = 0;
+ static grub_efi_uintn_t finish_mmap_size = 0;
diff --git a/SOURCES/0308-mm-Try-invalidate-disk-caches-last-when-out-of-memor.patch b/SOURCES/0308-mm-Try-invalidate-disk-caches-last-when-out-of-memor.patch
new file mode 100644
index 0000000..c919891
--- /dev/null
+++ b/SOURCES/0308-mm-Try-invalidate-disk-caches-last-when-out-of-memor.patch
@@ -0,0 +1,57 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zhang Boyang <zhangboyang.id@gmail.com>
+Date: Sat, 15 Oct 2022 22:15:11 +0800
+Subject: [PATCH] mm: Try invalidate disk caches last when out of memory
+
+Every heap grow will cause all disk caches invalidated which decreases
+performance severely. This patch moves disk cache invalidation code to
+the last of memory squeezing measures. So, disk caches are released only
+when there are no other ways to get free memory.
+
+Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+Reviewed-by: Patrick Steinhardt <ps@pks.im>
+(cherry picked from commit 17975d10a80e2457e5237f87fa58a7943031983e)
+---
+ grub-core/kern/mm.c | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
+index f2e27f263b..da1ac9427c 100644
+--- a/grub-core/kern/mm.c
++++ b/grub-core/kern/mm.c
+@@ -443,12 +443,6 @@ grub_memalign (grub_size_t align, grub_size_t size)
+   switch (count)
+     {
+     case 0:
+-      /* Invalidate disk caches.  */
+-      grub_disk_cache_invalidate_all ();
+-      count++;
+-      goto again;
+-
+-    case 1:
+       /* Request additional pages, contiguous */
+       count++;
+ 
+@@ -458,7 +452,7 @@ grub_memalign (grub_size_t align, grub_size_t size)
+ 
+       /* fallthrough  */
+ 
+-    case 2:
++    case 1:
+       /* Request additional pages, anything at all */
+       count++;
+ 
+@@ -474,6 +468,12 @@ grub_memalign (grub_size_t align, grub_size_t size)
+ 
+       /* fallthrough */
+ 
++    case 2:
++      /* Invalidate disk caches.  */
++      grub_disk_cache_invalidate_all ();
++      count++;
++      goto again;
++
+     default:
+       break;
+     }
diff --git a/SOURCES/0309-ppc64le-signed-boot-media-changes.patch b/SOURCES/0309-ppc64le-signed-boot-media-changes.patch
new file mode 100644
index 0000000..bd71437
--- /dev/null
+++ b/SOURCES/0309-ppc64le-signed-boot-media-changes.patch
@@ -0,0 +1,125 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Wed, 25 Jan 2023 16:10:58 -0500
+Subject: [PATCH] ppc64le: signed boot media changes
+
+Skip mdraid < 1.1 on isos since mdraid* can't even
+
+Prior to this change, on ppc64le with part_msdos and the mdraid* modules
+enabled, we see:
+
+    disk/diskfilter.c:191: scanning ieee1275/cdrom
+    kern/disk.c:196: Opening `ieee1275/cdrom'...
+    disk/ieee1275/ofdisk.c:477: Opening `cdrom'.
+    disk/ieee1275/ofdisk.c:502: MAX_RETRIES set to 20
+    kern/disk.c:288: Opening `ieee1275/cdrom' succeeded.
+    disk/diskfilter.c:136: Scanning for DISKFILTER devices on disk ieee1275/cdrom
+    partmap/msdos.c:184: partition 0: flag 0x80, type 0x96, start 0x0, len
+    0x6a5d70
+    disk/diskfilter.c:136: Scanning for DISKFILTER devices on disk ieee1275/cdrom
+    SCSI-DISK: Access beyond end of device !
+    SCSI-DISK: Access beyond end of device !
+    SCSI-DISK: Access beyond end of device !
+    SCSI-DISK: Access beyond end of device !
+    SCSI-DISK: Access beyond end of device !
+    disk/ieee1275/ofdisk.c:578: MAX_RETRIES set to 20
+
+These latter two lines repeat many times, eventually ending in:
+
+    kern/disk.c:388: ieee1275/cdrom read failed
+    error: ../../grub-core/disk/ieee1275/ofdisk.c:608:failure reading sector
+    0x1a9720 from `ieee1275/cdrom'.
+
+and the system drops to a "grub>" prompt.
+
+Prior to 1.1, mdraid stored the superblock offset from the end of the
+disk, and the firmware really doesn't like reads there.  Best guess was
+that the firmware and the iso image appear to diagree on the blocksize
+(512 vs. 2048), and the diskfilter RAID probing is too much for it.
+It's tempting to just skip probing for cdroms, but unfortunately isos
+can be virtualized elsewhere - such as regular disks.
+
+Also fix detection of root, and try the chrp path as a fallback if the
+built prefix doesn't work.
+
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+
+wip
+---
+ grub-core/disk/mdraid1x_linux.c  | 8 +++++++-
+ grub-core/disk/mdraid_linux.c    | 5 +++++
+ grub-core/kern/ieee1275/openfw.c | 2 +-
+ grub-core/normal/main.c          | 5 +++++
+ 4 files changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/disk/mdraid1x_linux.c b/grub-core/disk/mdraid1x_linux.c
+index 38444b02c7..08c57ae16e 100644
+--- a/grub-core/disk/mdraid1x_linux.c
++++ b/grub-core/disk/mdraid1x_linux.c
+@@ -129,7 +129,13 @@ grub_mdraid_detect (grub_disk_t disk,
+       grub_uint32_t level;
+       struct grub_diskfilter_vg *array;
+       char *uuid;
+-	
++
++#ifdef __powerpc__
++      /* Firmware will yell at us for reading too far. */
++      if (minor_version == 0)
++        continue;
++#endif
++
+       if (size == GRUB_DISK_SIZE_UNKNOWN && minor_version == 0)
+ 	continue;
+ 	
+diff --git a/grub-core/disk/mdraid_linux.c b/grub-core/disk/mdraid_linux.c
+index e40216f511..98fcfb1be6 100644
+--- a/grub-core/disk/mdraid_linux.c
++++ b/grub-core/disk/mdraid_linux.c
+@@ -189,6 +189,11 @@ grub_mdraid_detect (grub_disk_t disk,
+   grub_uint32_t level;
+   struct grub_diskfilter_vg *ret;
+ 
++#ifdef __powerpc__
++  /* Firmware will yell at us for reading too far. */
++  return NULL;
++#endif
++
+   /* The sector where the mdraid 0.90 superblock is stored, if available.  */
+   size = grub_disk_native_sectors (disk);
+   if (size == GRUB_DISK_SIZE_UNKNOWN)
+diff --git a/grub-core/kern/ieee1275/openfw.c b/grub-core/kern/ieee1275/openfw.c
+index 3a6689abb1..0278054c61 100644
+--- a/grub-core/kern/ieee1275/openfw.c
++++ b/grub-core/kern/ieee1275/openfw.c
+@@ -499,7 +499,7 @@ grub_ieee1275_encode_devname (const char *path)
+ 	*optr++ ='\\';
+       *optr++ = *iptr++;
+     }
+-  if (partition && partition[0])
++  if (partition && partition[0] >= '0' && partition[0] <= '9')
+     {
+       unsigned int partno = grub_strtoul (partition, 0, 0);
+ 
+diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
+index 8f5fd81003..d59145f861 100644
+--- a/grub-core/normal/main.c
++++ b/grub-core/normal/main.c
+@@ -372,6 +372,7 @@ grub_try_normal_prefix (const char *prefix)
+            file = grub_file_open (config, GRUB_FILE_TYPE_CONFIG);
+            if (file)
+              {
++               grub_env_set ("prefix", prefix);
+                grub_file_close (file);
+                err = GRUB_ERR_NONE;
+              }
+@@ -447,6 +448,10 @@ 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");
++#ifdef __powerpc__
++      if (err == GRUB_ERR_FILE_NOT_FOUND)
++        err = grub_try_normal_prefix ("/boot/grub");
++#endif
+       if (err == GRUB_ERR_FILE_NOT_FOUND)
+         err = grub_try_normal_discover ();
+       if (err == GRUB_ERR_FILE_NOT_FOUND)
diff --git a/SOURCES/0310-core-Fix-several-implicit-function-declarations.patch b/SOURCES/0310-core-Fix-several-implicit-function-declarations.patch
new file mode 100644
index 0000000..48c5bb0
--- /dev/null
+++ b/SOURCES/0310-core-Fix-several-implicit-function-declarations.patch
@@ -0,0 +1,54 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Arjun Shankar <arjun@redhat.com>
+Date: Sun, 5 Feb 2023 11:13:55 +0100
+Subject: [PATCH] core: Fix several implicit function declarations
+
+These #include lines ensure that grub2 continues to build with C99
+where implicit function declarations are removed.
+
+Related to:
+
+  <https://fedoraproject.org/wiki/Changes/PortingToModernC>
+  <https://fedoraproject.org/wiki/Toolchain/PortingToModernC>
+---
+ grub-core/commands/efi/connectefi.c | 1 +
+ grub-core/net/http.c                | 1 +
+ grub-core/term/at_keyboard.c        | 1 +
+ 3 files changed, 3 insertions(+)
+
+diff --git a/grub-core/commands/efi/connectefi.c b/grub-core/commands/efi/connectefi.c
+index 8ab75bd51b..3752ae17ed 100644
+--- a/grub-core/commands/efi/connectefi.c
++++ b/grub-core/commands/efi/connectefi.c
+@@ -21,6 +21,7 @@
+ #include <grub/efi/api.h>
+ #include <grub/efi/pci.h>
+ #include <grub/efi/efi.h>
++#include <grub/efi/disk.h>
+ #include <grub/command.h>
+ #include <grub/err.h>
+ #include <grub/i18n.h>
+diff --git a/grub-core/net/http.c b/grub-core/net/http.c
+index 57d2721719..5f956b743e 100644
+--- a/grub-core/net/http.c
++++ b/grub-core/net/http.c
+@@ -26,6 +26,7 @@
+ #include <grub/dl.h>
+ #include <grub/file.h>
+ #include <grub/i18n.h>
++#include <grub/env.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
+diff --git a/grub-core/term/at_keyboard.c b/grub-core/term/at_keyboard.c
+index dac0f946fe..de3e4abe44 100644
+--- a/grub-core/term/at_keyboard.c
++++ b/grub-core/term/at_keyboard.c
+@@ -25,6 +25,7 @@
+ #include <grub/time.h>
+ #include <grub/loader.h>
+ #include <grub/ps2.h>
++#include <grub/env.h>
+ 
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 
diff --git a/SOURCES/0311-loader-Add-support-for-grub-emu-to-kexec-Linux-menu-.patch b/SOURCES/0311-loader-Add-support-for-grub-emu-to-kexec-Linux-menu-.patch
new file mode 100644
index 0000000..65834d8
--- /dev/null
+++ b/SOURCES/0311-loader-Add-support-for-grub-emu-to-kexec-Linux-menu-.patch
@@ -0,0 +1,431 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Raymund Will <rw@suse.com>
+Date: Mon, 24 Oct 2022 14:33:50 -0400
+Subject: [PATCH] loader: Add support for grub-emu to kexec Linux menu entries
+
+The GRUB emulator is used as a debugging utility but it could also be
+used as a user-space bootloader if there is support to boot an operating
+system.
+
+The Linux kernel is already able to (re)boot another kernel via the
+kexec boot mechanism. So the grub-emu tool could rely on this feature
+and have linux and initrd commands that are used to pass a kernel,
+initramfs image and command line parameters to kexec for booting
+a selected menu entry.
+
+By default the systemctl kexec option is used so systemd can shutdown
+all of the running services before doing a reboot using kexec. But if
+this is not present, it can fall back to executing the kexec user-space
+tool directly. The ability to force a kexec-reboot when systemctl kexec
+fails must only be used in controlled environments to avoid possible
+filesystem corruption and data loss.
+
+Signed-off-by: Raymund Will <rw@suse.com>
+Signed-off-by: John Jolly <jjolly@suse.com>
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+Signed-off-by: Robbie Harwood <rharwood@redhat.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit e364307f6acc2f631b4c1fefda0791b9ce1f205f)
+[rharwood: conflicts around makefile and grub_exit return code]
+---
+ grub-core/Makefile.core.def  |   3 -
+ grub-core/kern/emu/main.c    |   4 +
+ grub-core/kern/emu/misc.c    |  18 ++++-
+ grub-core/loader/emu/linux.c | 178 +++++++++++++++++++++++++++++++++++++++++++
+ include/grub/emu/exec.h      |   4 +-
+ include/grub/emu/hostfile.h  |   3 +-
+ include/grub/emu/misc.h      |   3 +
+ docs/grub.texi               |  30 ++++++--
+ grub-core/Makefile.am        |   1 +
+ 9 files changed, 230 insertions(+), 14 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 741a033978..f21da23213 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -1864,11 +1864,8 @@ module = {
+   riscv32 = loader/riscv/linux.c;
+   riscv64 = loader/riscv/linux.c;
+   emu = loader/emu/linux.c;
+-
+   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 12277c34d2..68e2b283bb 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_("use kexec to boot Linux kernels via systemctl (pass twice to enable dangerous fallback to non-systemctl)."), 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 d278c2921f..02d27c3440 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 0000000000..0cf378a376
+--- /dev/null
++++ b/grub-core/loader/emu/linux.c
+@@ -0,0 +1,178 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2022  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", "-la", 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_dprintf ("linux", "%serforming 'kexec -la %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_dprintf ("linux", "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': %d"), rc);
++
++  /*
++   * WARNING: forcible reset should only be used in read-only environments.
++   * grub-emu cannot check for these - users beware.
++   */
++  grub_dprintf ("linux", "Performing 'kexec -ex'");
++  kexec[1] = "-ex";
++  kexec[2] = NULL;
++  rc = grub_util_exec (kexec);
++  if (rc != GRUB_ERR_NONE)
++    grub_fatal (N_("error trying to directly perform 'kexec -ex': %d"), rc);
++
++  return rc;
++}
++
++static grub_err_t
++grub_linux_unload (void)
++{
++  /* Unloading: we're no longer in use. */
++  grub_dl_unref (my_mod);
++  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;
++
++  /* Mark ourselves as in-use. */
++  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]);
++
++  grub_free (kernel_path);
++  kernel_path = grub_xasprintf ("%s", argv[0]);
++
++  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]);
++
++  grub_free (initrd_path);
++  initrd_path = grub_xasprintf ("%s", argv[0]);
++
++  /* We are done - mark ourselves as on longer in use. */
++  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;
++}
++
++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 d1073ef86a..1b61b4a2e5 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 cfb1e2b566..a61568e36e 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 ff9c48a649..01056954b9 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/docs/grub.texi b/docs/grub.texi
+index a4da9c2a1b..1750b72ee9 100644
+--- a/docs/grub.texi
++++ b/docs/grub.texi
+@@ -923,17 +923,17 @@ magic.
+ @node General boot methods
+ @section How to boot operating systems
+ 
+-GRUB has two distinct boot methods. One of the two is to load an
+-operating system directly, and the other is to chain-load another boot
+-loader which then will load an operating system actually. Generally
+-speaking, the former is more desirable, because you don't need to
+-install or maintain other boot loaders and GRUB is flexible enough to
+-load an operating system from an arbitrary disk/partition. However,
+-the latter is sometimes required, since GRUB doesn't support all the
+-existing operating systems natively.
++GRUB has three distinct boot methods: loading an operating system
++directly, using kexec from userspace, and chainloading another
++bootloader. Generally speaking, the first two are more desirable
++because you don't need to install or maintain other boot loaders and
++GRUB is flexible enough to load an operating system from an arbitrary
++disk/partition. However, chainloading is sometimes required, as GRUB
++doesn't support all existing operating systems natively.
+ 
+ @menu
+ * Loading an operating system directly::
++* Kexec::
+ * Chain-loading::
+ @end menu
+ 
+@@ -959,6 +959,20 @@ use more complicated instructions. @xref{DOS/Windows}, for more
+ information.
+ 
+ 
++@node Kexec
++@subsection Kexec with grub2-emu
++
++GRUB can be run in userspace by invoking the grub2-emu tool. It will
++read all configuration scripts as if booting directly (see @xref{Loading
++an operating system directly}). With the @code{--kexec} flag, and
++kexec(8) support from the operating system, the @command{linux} command
++will directly boot the target image. For systems that lack working
++systemctl(1) support for kexec, passing the @code{--kexec} flag twice
++will fallback to invoking kexec(8) directly; note however that this
++fallback may be unsafe outside read-only environments, as it does not
++invoke shutdown machinery.
++
++
+ @node Chain-loading
+ @subsection Chain-loading an OS
+ 
+diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
+index c2e8a82bce..dd49939aaa 100644
+--- a/grub-core/Makefile.am
++++ b/grub-core/Makefile.am
+@@ -309,6 +309,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/0312-powerpc-Drop-Open-Hack-Ware-remove-GRUB_IEEE1275_FLA.patch b/SOURCES/0312-powerpc-Drop-Open-Hack-Ware-remove-GRUB_IEEE1275_FLA.patch
new file mode 100644
index 0000000..735075a
--- /dev/null
+++ b/SOURCES/0312-powerpc-Drop-Open-Hack-Ware-remove-GRUB_IEEE1275_FLA.patch
@@ -0,0 +1,111 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Mon, 6 Sep 2021 15:46:12 +1000
+Subject: [PATCH] powerpc: Drop Open Hack'Ware - remove
+ GRUB_IEEE1275_FLAG_FORCE_CLAIM
+
+Open Hack'Ware was the only user. It added a lot of complexity.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 333e63b356f1ce833cda1937ed8351618cbdf9d3)
+---
+ grub-core/kern/ieee1275/init.c            |  6 +-----
+ grub-core/lib/ieee1275/relocator.c        |  4 ----
+ grub-core/loader/powerpc/ieee1275/linux.c | 14 --------------
+ include/grub/ieee1275/ieee1275.h          | 11 -----------
+ 4 files changed, 1 insertion(+), 34 deletions(-)
+
+diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
+index 0dcd114ce5..6581c2c996 100644
+--- a/grub-core/kern/ieee1275/init.c
++++ b/grub-core/kern/ieee1275/init.c
+@@ -207,11 +207,7 @@ grub_claim_heap (void)
+ {
+   unsigned long 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);
++  grub_machine_mmap_iterate (heap_init, &total);
+ }
+ #endif
+ 
+diff --git a/grub-core/lib/ieee1275/relocator.c b/grub-core/lib/ieee1275/relocator.c
+index c6dd8facb0..d1bb45c75e 100644
+--- a/grub-core/lib/ieee1275/relocator.c
++++ b/grub-core/lib/ieee1275/relocator.c
+@@ -38,8 +38,6 @@ grub_relocator_firmware_get_max_events (void)
+ {
+   int counter = 0;
+ 
+-  if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM))
+-    return 0;
+   grub_machine_mmap_iterate (count, &counter);
+   return 2 * counter;
+ }
+@@ -92,8 +90,6 @@ grub_relocator_firmware_fill_events (struct grub_relocator_mmap_event *events)
+     .counter = 0
+   };
+ 
+-  if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM))
+-    return 0;
+   grub_machine_mmap_iterate (grub_relocator_firmware_fill_events_iter, &ctx);
+   return ctx.counter;
+ }
+diff --git a/grub-core/loader/powerpc/ieee1275/linux.c b/grub-core/loader/powerpc/ieee1275/linux.c
+index 818b2a86d1..6fdd863130 100644
+--- a/grub-core/loader/powerpc/ieee1275/linux.c
++++ b/grub-core/loader/powerpc/ieee1275/linux.c
+@@ -111,20 +111,6 @@ grub_linux_claimmap_iterate (grub_addr_t target, grub_size_t size,
+     .found_addr = (grub_addr_t) -1
+   };
+ 
+-  if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM))
+-    {
+-      grub_uint64_t addr = target;
+-      if (addr < GRUB_IEEE1275_STATIC_HEAP_START
+-	  + GRUB_IEEE1275_STATIC_HEAP_LEN)
+-	addr = GRUB_IEEE1275_STATIC_HEAP_START
+-	  + GRUB_IEEE1275_STATIC_HEAP_LEN;
+-      addr = ALIGN_UP (addr, align);
+-      if (grub_claimmap (addr, size) == GRUB_ERR_NONE)
+-	return addr;
+-      return (grub_addr_t) -1;
+-    }
+-	
+-
+   grub_machine_mmap_iterate (alloc_mem, &ctx);
+ 
+   return ctx.found_addr;
+diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h
+index b5a1d49bbc..6a1d3e5d70 100644
+--- a/include/grub/ieee1275/ieee1275.h
++++ b/include/grub/ieee1275/ieee1275.h
+@@ -85,14 +85,6 @@ extern grub_ieee1275_ihandle_t EXPORT_VAR(grub_ieee1275_mmu);
+ 
+ extern int (* EXPORT_VAR(grub_ieee1275_entry_fn)) (void *) GRUB_IEEE1275_ENTRY_FN_ATTRIBUTE;
+ 
+-/* Static heap, used only if FORCE_CLAIM is set,
+-   happens on Open Hack'Ware. Should be in platform-specific
+-   header but is used only on PPC anyway.
+-*/
+-#define GRUB_IEEE1275_STATIC_HEAP_START 0x1000000
+-#define GRUB_IEEE1275_STATIC_HEAP_LEN   0x1000000
+-
+-
+ enum grub_ieee1275_flag
+ {
+   /* Old World Macintosh firmware fails seek when "dev:0" is opened.  */
+@@ -119,9 +111,6 @@ enum grub_ieee1275_flag
+   /* Open Hack'Ware stops when grub_ieee1275_interpret is used.  */
+   GRUB_IEEE1275_FLAG_CANNOT_INTERPRET,
+ 
+-  /* Open Hack'Ware has no memory map, just claim what we need.  */
+-  GRUB_IEEE1275_FLAG_FORCE_CLAIM,
+-
+   /* Open Hack'Ware don't support the ANSI sequence.  */
+   GRUB_IEEE1275_FLAG_NO_ANSI,
+ 
diff --git a/SOURCES/0313-ieee1275-request-memory-with-ibm-client-architecture.patch b/SOURCES/0313-ieee1275-request-memory-with-ibm-client-architecture.patch
new file mode 100644
index 0000000..4e95630
--- /dev/null
+++ b/SOURCES/0313-ieee1275-request-memory-with-ibm-client-architecture.patch
@@ -0,0 +1,308 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Mon, 6 Feb 2023 10:03:20 -0500
+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 enough to reliably verify a kernel. Fortunately, the Power
+Architecture Platform Reference (PAPR) defines a method we can call to ask
+for more memory: 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.
+
+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.
+
+ - Set option vector 3 to request Floating Point, VMX and Decimal Floating
+   point, but don't abort the boot if we can't get them.
+
+ - Set option vector 4 to request a minimum VP percentage to 1%, which is
+   what Linux requests, and is below the default of 10%. Without this,
+   some systems with very large or very small configurations fail to boot.
+
+This will cause a CAS reboot and the partition will restart with 512MB
+of RMA. Importantly, grub will notice the 512MB and not call CAS again.
+
+Notes about the choices of parameters:
+
+ - 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".)
+
+ - unspecified CAS vectors take on default values. Some of these values
+   might restrict the ability of certain hardware configurations to boot.
+   This is why we need to specify the VP percentage in vector 4, which is
+   in turn why we need to specify vector 3.
+
+Finally, we should have enough memory to verify a kernel, and we will
+reach Linux. 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). Linux includes a much more restrictive set of
+PVR values and processor support levels, and this CAS invocation will likely
+induce another reboot. On this reboot grub will again notice the higher RMA,
+and not call CAS. We will get to Linux again, Linux will call CAS again, but
+because the values are now set for Linux this will not induce another CAS
+reboot and we will finally boot all the way to userspace.
+
+On all subsequent boots, everything will be configured with 512MB of RMA,
+so there will be no further CAS reboots from grub. (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>
+Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit d5571590b7de61887efac1c298901455697ba307)
+---
+ grub-core/kern/ieee1275/cmain.c  |   5 ++
+ grub-core/kern/ieee1275/init.c   | 167 ++++++++++++++++++++++++++++++++++++++-
+ include/grub/ieee1275/ieee1275.h |  12 ++-
+ 3 files changed, 182 insertions(+), 2 deletions(-)
+
+diff --git a/grub-core/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c
+index 04df9d2c66..dce7b84922 100644
+--- a/grub-core/kern/ieee1275/cmain.c
++++ b/grub-core/kern/ieee1275/cmain.c
+@@ -127,6 +127,11 @@ grub_ieee1275_find_options (void)
+ 	      break;
+ 	    }
+ 	}
++
++#if defined(__powerpc__)
++      if (grub_strncmp (tmp, "IBM,", 4) == 0)
++	grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY);
++#endif
+     }
+ 
+   if (is_smartfirmware)
+diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
+index 6581c2c996..8ae405bc79 100644
+--- a/grub-core/kern/ieee1275/init.c
++++ b/grub-core/kern/ieee1275/init.c
+@@ -202,11 +202,176 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
+   return 0;
+ }
+ 
+-static void 
++/*
++ * 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'.  */
++  if (grub_ieee1275_finddevice ("/", &root))
++    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't find / node");
++  if (grub_ieee1275_get_integer_property (root, "#address-cells", &address_cells,
++					  sizeof (address_cells), 0))
++    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't examine #address-cells");
++  if (grub_ieee1275_get_integer_property (root, "#size-cells", &size_cells,
++					  sizeof (size_cells), 0))
++    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't examine #size-cells");
++
++  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;
++}
++
++#if defined(__powerpc__)
++
++/* See PAPR or 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;
++} GRUB_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;
++  grub_uint8_t vec3_size;
++  grub_uint16_t vec3;
++  grub_uint8_t vec4_size;
++  grub_uint16_t vec4;
++} GRUB_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 = 4 - 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
++    },
++    .vec3_size = 2 - 1,
++    .vec3 = 0x00e0, /* ask for FP + VMX + DFP but don't halt if unsatisfied */
++    .vec4_size = 2 - 1,
++    .vec4 = 0x0001, /* set required minimum capacity % to the lowest value */
++  };
++
++  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 from grub...");
++  IEEE1275_CALL_ENTRY_FN (&args);
++  grub_printf ("done\n");
++
++  grub_ieee1275_close (root);
++}
++
++#endif /* __powerpc__ */
++
++static void
+ grub_claim_heap (void)
+ {
+   unsigned long total = 0;
+ 
++#if defined(__powerpc__)
++  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 == GRUB_ERR_NONE && rma_size < (512 * 1024 * 1024))
++	grub_ieee1275_ibm_cas ();
++    }
++#endif
++
+   grub_machine_mmap_iterate (heap_init, &total);
+ }
+ #endif
+diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h
+index 6a1d3e5d70..560c968460 100644
+--- a/include/grub/ieee1275/ieee1275.h
++++ b/include/grub/ieee1275/ieee1275.h
+@@ -138,7 +138,17 @@ enum grub_ieee1275_flag
+ 
+   GRUB_IEEE1275_FLAG_RAW_DEVNAMES,
+   
+-  GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT
++  GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT,
++
++#if defined(__powerpc__)
++  /*
++   * 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,
++#endif
+ };
+ 
+ extern int EXPORT_FUNC(grub_ieee1275_test_flag) (enum grub_ieee1275_flag flag);
diff --git a/SOURCES/0314-ieee1275-drop-len-1-quirk-in-heap_init.patch b/SOURCES/0314-ieee1275-drop-len-1-quirk-in-heap_init.patch
new file mode 100644
index 0000000..4ce064b
--- /dev/null
+++ b/SOURCES/0314-ieee1275-drop-len-1-quirk-in-heap_init.patch
@@ -0,0 +1,36 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Mon, 6 Feb 2023 10:03:21 -0500
+Subject: [PATCH] ieee1275: drop len -= 1 quirk in heap_init
+
+This was apparently 'required by some firmware': commit dc9468500919
+("2007-02-12  Hollis Blanchard  <hollis@penguinppc.org>").
+
+It's not clear what firmware that was, and what platform from 14 years ago
+which exhibited the bug then is still both in use and buggy now.
+
+It doesn't cause issues on qemu (mac99 or pseries) or under PFW for Power8.
+
+I don't have access to old Mac hardware, but if anyone feels especially
+strongly we can put it under some feature flag. I really want to disable
+it under pseries because it will mess with region merging.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit fc639d430297321ee4f77c5d2d698f698cec0dc7)
+---
+ grub-core/kern/ieee1275/init.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
+index 8ae405bc79..c8d551759d 100644
+--- a/grub-core/kern/ieee1275/init.c
++++ b/grub-core/kern/ieee1275/init.c
+@@ -168,7 +168,6 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
+ 	  addr = 0x180000;
+ 	}
+     }
+-  len -= 1; /* Required for some firmware.  */
+ 
+   /* Never exceed HEAP_MAX_SIZE  */
+   if (*total + len > HEAP_MAX_SIZE)
diff --git a/SOURCES/0315-ieee1275-support-runtime-memory-claiming.patch b/SOURCES/0315-ieee1275-support-runtime-memory-claiming.patch
new file mode 100644
index 0000000..17ad61d
--- /dev/null
+++ b/SOURCES/0315-ieee1275-support-runtime-memory-claiming.patch
@@ -0,0 +1,435 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Mon, 6 Feb 2023 10:03:22 -0500
+Subject: [PATCH] ieee1275: support runtime memory claiming
+
+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
+   difficult to change with appended signatures.
+ - We only have 32MB of heap.
+ - Distro kernels are now often around 30MB.
+
+So we want to be able to claim more memory from OpenFirmware for our heap
+at runtime.
+
+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. Even with our CAS work, an LPAR
+   on a PowerVM box is likely to have only 512MB available to OpenFirmware
+   even if it has many gigabytes of memory allocated.
+
+What should we do?
+
+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
+
+So to give us a little wriggle room, I think we want to leave at least
+128MB for the loader to put vmlinux and initrd in memory and leave Linux
+with space to satisfy its early allocations.
+
+Allow other space to be allocated at runtime.
+
+Tested-by: Stefan Berger <stefanb@linux.ibm.com>
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+(cherry picked from commit a5c710789ccdd27a84ae4a34c7d453bd585e2b66)
+[rharwood: _start?]
+---
+ grub-core/kern/ieee1275/init.c | 270 ++++++++++++++++++++++++++++++++++++++---
+ docs/grub-dev.texi             |   7 +-
+ 2 files changed, 257 insertions(+), 20 deletions(-)
+
+diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
+index c8d551759d..85af8fa97b 100644
+--- a/grub-core/kern/ieee1275/init.c
++++ b/grub-core/kern/ieee1275/init.c
+@@ -46,13 +46,26 @@
+ #endif
+ #include <grub/lockdown.h>
+ 
+-/* The maximum heap size we're going to claim */
++/* The maximum heap size we're going to claim at boot. Not used by sparc. */
+ #ifdef __i386__
+ #define HEAP_MAX_SIZE		(unsigned long) (64 * 1024 * 1024)
+-#else
++#else /* __powerpc__ */
+ #define HEAP_MAX_SIZE		(unsigned long) (32 * 1024 * 1024)
+ #endif
+ 
++/* RMO max. address at 768 MB */
++#define RMO_ADDR_MAX		(grub_uint64_t) (768 * 1024 * 1024)
++
++/*
++ * The amount of OF space we will not claim here so as to leave space for
++ * the loader and linux to service early allocations.
++ *
++ * In 2021, Daniel Axtens claims that we should leave at least 128MB to
++ * ensure we can load a stock kernel and initrd on a pseries guest with
++ * a 512MB real memory area under PowerVM.
++ */
++#define RUNTIME_MIN_SPACE (128UL * 1024 * 1024)
++
+ extern char _end[];
+ 
+ #ifdef __sparc__
+@@ -147,16 +160,52 @@ grub_claim_heap (void)
+ 				 + GRUB_KERNEL_MACHINE_STACK_SIZE), 0x200000);
+ }
+ #else
+-/* Helper for grub_claim_heap.  */
++/* Helpers for mm on powerpc. */
++
++/*
++ * How much memory does OF believe exists in total?
++ *
++ * This isn't necessarily the true total. It can be the total memory
++ * accessible in real mode for a pseries guest, for example.
++ */
++static grub_uint64_t rmo_top;
++
+ static int
+-heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
+-	   void *data)
++count_free (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
++	    void *data)
+ {
+-  unsigned long *total = data;
++  if (type != GRUB_MEMORY_AVAILABLE)
++    return 0;
++
++  /* Do not consider memory beyond 4GB */
++  if (addr > 0xffffffffULL)
++    return 0;
++
++  if (addr + len > 0xffffffffULL)
++    len = 0xffffffffULL - addr;
++
++  *(grub_uint32_t *) data += len;
++
++  return 0;
++}
++
++static int
++regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
++	      unsigned int flags, void *data)
++{
++  grub_uint32_t total = *(grub_uint32_t *) data;
++  grub_uint64_t linux_rmo_save;
+ 
+   if (type != GRUB_MEMORY_AVAILABLE)
+     return 0;
+ 
++  /* Do not consider memory beyond 4GB */
++  if (addr > 0xffffffffULL)
++    return 0;
++
++  if (addr + len > 0xffffffffULL)
++    len = 0xffffffffULL - addr;
++
+   if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_PRE1_5M_CLAIM))
+     {
+       if (addr + len <= 0x180000)
+@@ -169,10 +218,6 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
+ 	}
+     }
+ 
+-  /* 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
+@@ -184,6 +229,108 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
+       len = 0;
+     }
+ 
++  /*
++   * Linux likes to claim memory at min(RMO top, 768MB) and works down
++   * without reference to /memory/available. (See prom_init.c::alloc_down)
++   *
++   * If this block contains min(RMO top, 768MB), do not claim below that for
++   * at least a few MB (this is where RTAS, SML and potentially TCEs live).
++   *
++   * We also need to leave enough space for the DT in the RMA. (See
++   * prom_init.c::alloc_up)
++   *
++   * Finally, we also want to make sure that when grub loads the kernel,
++   * it isn't going to use up all the memory we're trying to reserve! So
++   * enforce our entire RUNTIME_MIN_SPACE here:
++   *
++   * |---------- Top of memory ----------|
++   * |                                   |
++   * |             available             |
++   * |                                   |
++   * |----------     768 MB    ----------|
++   * |                                   |
++   * |              reserved             |
++   * |                                   |
++   * |--- 768 MB - runtime min space  ---|
++   * |                                   |
++   * |             available             |
++   * |                                   |
++   * |----------      0 MB     ----------|
++   *
++   * Edge cases:
++   *
++   * - Total memory less than RUNTIME_MIN_SPACE: only claim up to HEAP_MAX_SIZE.
++   *   (enforced elsewhere)
++   *
++   * - Total memory between RUNTIME_MIN_SPACE and 768MB:
++   *
++   * |---------- Top of memory ----------|
++   * |                                   |
++   * |              reserved             |
++   * |                                   |
++   * |----  top - runtime min space  ----|
++   * |                                   |
++   * |             available             |
++   * |                                   |
++   * |----------      0 MB     ----------|
++   *
++   * This by itself would not leave us with RUNTIME_MIN_SPACE of free bytes: if
++   * rmo_top < 768MB, we will almost certainly have FW claims in the reserved
++   * region. We try to address that elsewhere: grub_ieee1275_mm_add_region will
++   * not call us if the resulting free space would be less than RUNTIME_MIN_SPACE.
++   */
++  linux_rmo_save = grub_min (RMO_ADDR_MAX, rmo_top) - RUNTIME_MIN_SPACE;
++  if (rmo_top > RUNTIME_MIN_SPACE)
++    {
++      if (rmo_top <= RMO_ADDR_MAX)
++        {
++          if (addr > linux_rmo_save)
++            {
++              grub_dprintf ("ieee1275", "rejecting region in RUNTIME_MIN_SPACE reservation (%llx)\n",
++                            addr);
++              return 0;
++            }
++          else if (addr + len > linux_rmo_save)
++            {
++              grub_dprintf ("ieee1275", "capping region: (%llx -> %llx) -> (%llx -> %llx)\n",
++                            addr, addr + len, addr, rmo_top - RUNTIME_MIN_SPACE);
++              len = linux_rmo_save - addr;
++            }
++        }
++      else
++        {
++          /*
++           * we order these cases to prefer higher addresses and avoid some
++           * splitting issues
++           */
++          if (addr < RMO_ADDR_MAX && (addr + len) > RMO_ADDR_MAX)
++            {
++              grub_dprintf ("ieee1275",
++                            "adjusting region for RUNTIME_MIN_SPACE: (%llx -> %llx) -> (%llx -> %llx)\n",
++                            addr, addr + len, RMO_ADDR_MAX, addr + len);
++              len = (addr + len) - RMO_ADDR_MAX;
++              addr = RMO_ADDR_MAX;
++            }
++          else if ((addr < linux_rmo_save) && ((addr + len) > linux_rmo_save))
++            {
++              grub_dprintf ("ieee1275", "capping region: (%llx -> %llx) -> (%llx -> %llx)\n",
++                            addr, addr + len, addr, linux_rmo_save);
++              len = linux_rmo_save - addr;
++            }
++          else if (addr >= linux_rmo_save && (addr + len) <= RMO_ADDR_MAX)
++            {
++              grub_dprintf ("ieee1275", "rejecting region in RUNTIME_MIN_SPACE reservation (%llx)\n",
++                            addr);
++              return 0;
++            }
++        }
++    }
++  if (flags & GRUB_MM_ADD_REGION_CONSECUTIVE && len < total)
++    return 0;
++
++  if (len > total)
++    len = total;
++
+   if (len)
+     {
+       grub_err_t err;
+@@ -192,15 +339,95 @@ 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;
+ }
+ 
++static int
++heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
++	   void *data)
++{
++  return regions_claim (addr, len, type, GRUB_MM_ADD_REGION_NONE, data);
++}
++
++static int
++region_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
++	   void *data)
++{
++  return regions_claim (addr, len, type, GRUB_MM_ADD_REGION_CONSECUTIVE, data);
++}
++
++static grub_err_t
++grub_ieee1275_mm_add_region (grub_size_t size, unsigned int flags)
++{
++  grub_uint32_t free_memory = 0;
++  grub_uint32_t avail = 0;
++  grub_uint32_t total;
++
++  grub_dprintf ("ieee1275", "mm requested region of size %x, flags %x\n",
++               size, flags);
++
++  /*
++   * Update free memory each time, which is a bit inefficient but guards us
++   * against a situation where some OF driver goes out to firmware for
++   * memory and we don't realise.
++   */
++  grub_machine_mmap_iterate (count_free, &free_memory);
++
++  /* Ensure we leave enough space to boot. */
++  if (free_memory <= RUNTIME_MIN_SPACE + size)
++    {
++      grub_dprintf ("ieee1275", "Cannot satisfy allocation and retain minimum runtime space\n");
++      return GRUB_ERR_OUT_OF_MEMORY;
++    }
++
++  if (free_memory > RUNTIME_MIN_SPACE)
++      avail = free_memory - RUNTIME_MIN_SPACE;
++
++  grub_dprintf ("ieee1275", "free = 0x%x available = 0x%x\n", free_memory, avail);
++
++  if (flags & GRUB_MM_ADD_REGION_CONSECUTIVE)
++    {
++      /* first try rounding up hard for the sake of speed */
++      total = grub_max (ALIGN_UP (size, 1024 * 1024) + 1024 * 1024, 32 * 1024 * 1024);
++      total = grub_min (avail, total);
++
++      grub_dprintf ("ieee1275", "looking for %x bytes of memory (%x requested)\n", total, size);
++
++      grub_machine_mmap_iterate (region_claim, &total);
++      grub_dprintf ("ieee1275", "get memory from fw %s\n", total == 0 ? "succeeded" : "failed");
++
++      if (total != 0)
++        {
++          total = grub_min (avail, size);
++
++          grub_dprintf ("ieee1275", "fallback for %x bytes of memory (%x requested)\n", total, size);
++
++          grub_machine_mmap_iterate (region_claim, &total);
++          grub_dprintf ("ieee1275", "fallback from fw %s\n", total == 0 ? "succeeded" : "failed");
++        }
++    }
++  else
++    {
++      /* provide padding for a grub_mm_header_t and region */
++      total = grub_min (avail, size);
++      grub_machine_mmap_iterate (heap_init, &total);
++      grub_dprintf ("ieee1275", "get noncontig memory from fw %s\n", total == 0 ? "succeeded" : "failed");
++    }
++
++  if (total == 0)
++    return GRUB_ERR_NONE;
++  else
++    return GRUB_ERR_OUT_OF_MEMORY;
++}
++
+ /*
+  * How much memory does OF believe it has? (regardless of whether
+  * it's accessible or not)
+@@ -356,17 +583,24 @@ grub_ieee1275_ibm_cas (void)
+ static void
+ grub_claim_heap (void)
+ {
+-  unsigned long total = 0;
++  grub_err_t err;
++  grub_uint32_t total = HEAP_MAX_SIZE;
++
++  err = grub_ieee1275_total_mem (&rmo_top);
++
++  /*
++   * If we cannot size the available memory, we can't be sure we're leaving
++   * space for the kernel, initrd and things Linux loads early in boot. So only
++   * allow further allocations from firmware on success
++   */
++  if (err == GRUB_ERR_NONE)
++    grub_mm_add_region_fn = grub_ieee1275_mm_add_region;
+ 
+ #if defined(__powerpc__)
+   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 == GRUB_ERR_NONE && rma_size < (512 * 1024 * 1024))
++      if (err == GRUB_ERR_NONE && rmo_top < (512 * 1024 * 1024))
+ 	grub_ieee1275_ibm_cas ();
+     }
+ #endif
+diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
+index 7b2455a8fe..7edc5b7e2b 100644
+--- a/docs/grub-dev.texi
++++ b/docs/grub-dev.texi
+@@ -1047,7 +1047,10 @@ 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 and powerpc-ieee1275, GRUB will allocate 32MiB for its heap on
++startup. It may allocate more at runtime, as long as at least 128MiB remain free
++in OpenFirmware.
+ 
+ On sparc64-ieee1275 stack is 256KiB and heap is 2MiB.
+ 
+@@ -1075,7 +1078,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 available memory - 128MiB
+ @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/0316-ieee1275-implement-vec5-for-cas-negotiation.patch b/SOURCES/0316-ieee1275-implement-vec5-for-cas-negotiation.patch
new file mode 100644
index 0000000..fad39db
--- /dev/null
+++ b/SOURCES/0316-ieee1275-implement-vec5-for-cas-negotiation.patch
@@ -0,0 +1,74 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Diego Domingos <diegodo@linux.vnet.ibm.com>
+Date: Mon, 6 Feb 2023 10:03:23 -0500
+Subject: [PATCH] ieee1275: implement vec5 for cas negotiation
+
+As a legacy support, if the vector 5 is not implemented, Power Hypervisor will
+consider the max CPUs as 64 instead 256 currently supported during
+client-architecture-support negotiation.
+
+This patch implements the vector 5 and set the MAX CPUs to 256 while setting the
+others values to 0 (default).
+
+Signed-off-by: Diego Domingos <diegodo@linux.vnet.ibm.com>
+Acked-by: Daniel Axtens <dja@axtens.net>
+Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
+Signed-off-by: Avnish Chouhan <avnish@linux.vnet.ibm.com>
+(cherry picked from commit 942f19959fe7465fb52a1da39ff271a7ab704892)
+---
+ grub-core/kern/ieee1275/init.c | 21 ++++++++++++++++++++-
+ 1 file changed, 20 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
+index 85af8fa97b..72d4fed312 100644
+--- a/grub-core/kern/ieee1275/init.c
++++ b/grub-core/kern/ieee1275/init.c
+@@ -502,6 +502,19 @@ struct option_vector2
+   grub_uint8_t max_pft_size;
+ } GRUB_PACKED;
+ 
++struct option_vector5
++{
++  grub_uint8_t byte1;
++  grub_uint8_t byte2;
++  grub_uint8_t byte3;
++  grub_uint8_t cmo;
++  grub_uint8_t associativity;
++  grub_uint8_t bin_opts;
++  grub_uint8_t micro_checkpoint;
++  grub_uint8_t reserved0;
++  grub_uint32_t max_cpus;
++} GRUB_PACKED;
++
+ struct pvr_entry
+ {
+   grub_uint32_t mask;
+@@ -523,6 +536,8 @@ struct cas_vector
+   grub_uint16_t vec3;
+   grub_uint8_t vec4_size;
+   grub_uint16_t vec4;
++  grub_uint8_t vec5_size;
++  struct option_vector5 vec5;
+ } GRUB_PACKED;
+ 
+ /*
+@@ -547,7 +562,7 @@ grub_ieee1275_ibm_cas (void)
+   struct cas_vector vector =
+   {
+     .pvr_list = { { 0x00000000, 0xffffffff } }, /* any processor */
+-    .num_vecs = 4 - 1,
++    .num_vecs = 5 - 1,
+     .vec1_size = 0,
+     .vec1 = 0x80, /* ignore */
+     .vec2_size = 1 + sizeof (struct option_vector2) - 2,
+@@ -558,6 +573,10 @@ grub_ieee1275_ibm_cas (void)
+     .vec3 = 0x00e0, /* ask for FP + VMX + DFP but don't halt if unsatisfied */
+     .vec4_size = 2 - 1,
+     .vec4 = 0x0001, /* set required minimum capacity % to the lowest value */
++    .vec5_size = 1 + sizeof (struct option_vector5) - 2,
++    .vec5 = {
++      0, 192, 0, 128, 0, 0, 0, 0, 256
++    }
+   };
+ 
+   INIT_IEEE1275_COMMON (&args.common, "call-method", 3, 2);
diff --git a/SOURCES/0317-ibmvtpm-Add-support-for-trusted-boot-using-a-vTPM-2..patch b/SOURCES/0317-ibmvtpm-Add-support-for-trusted-boot-using-a-vTPM-2..patch
new file mode 100644
index 0000000..02c0282
--- /dev/null
+++ b/SOURCES/0317-ibmvtpm-Add-support-for-trusted-boot-using-a-vTPM-2..patch
@@ -0,0 +1,246 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Stefan Berger <stefanb@linux.ibm.com>
+Date: Mon, 6 Feb 2023 10:03:25 -0500
+Subject: [PATCH] ibmvtpm: Add support for trusted boot using a vTPM 2.0
+
+Add support for trusted boot using a vTPM 2.0 on the IBM IEEE1275
+PowerPC platform. With this patch grub now measures text and binary data
+into the TPM's PCRs 8 and 9 in the same way as the x86_64 platform
+does.
+
+This patch requires Daniel Axtens's patches for claiming more memory.
+
+Note: The tpm_init() function cannot be called from GRUB_MOD_INIT() since
+it does not find the device nodes upon module initialization and
+therefore the call to tpm_init() must be deferred to grub_tpm_measure().
+
+For vTPM support to work on PowerVM, system driver levels 1010.30
+or 1020.00 are required.
+
+Note: Previous versions of firmware levels with the 2hash-ext-log
+API call have a bug that, once this API call is invoked, has the
+effect of disabling the vTPM driver under Linux causing an error
+message to be displayed in the Linux kernel log. Those users will
+have to update their machines to the firmware levels mentioned
+above.
+
+Cc: Eric Snowberg <eric.snowberg@oracle.com>
+Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 2aa5ef83743dfea79377309ff4f5e9c9a55de355)
+---
+ grub-core/Makefile.core.def           |   7 ++
+ grub-core/commands/ieee1275/ibmvtpm.c | 155 ++++++++++++++++++++++++++++++++++
+ include/grub/ieee1275/ieee1275.h      |   3 +
+ docs/grub.texi                        |   3 +-
+ 4 files changed, 167 insertions(+), 1 deletion(-)
+ create mode 100644 grub-core/commands/ieee1275/ibmvtpm.c
+
+diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
+index f21da23213..02ea718652 100644
+--- a/grub-core/Makefile.core.def
++++ b/grub-core/Makefile.core.def
+@@ -1175,6 +1175,13 @@ module = {
+   enable = powerpc_ieee1275;
+ };
+ 
++module = {
++  name = tpm;
++  common = commands/tpm.c;
++  ieee1275 = commands/ieee1275/ibmvtpm.c;
++  enable = powerpc_ieee1275;
++};
++
+ module = {
+   name = terminal;
+   common = commands/terminal.c;
+diff --git a/grub-core/commands/ieee1275/ibmvtpm.c b/grub-core/commands/ieee1275/ibmvtpm.c
+new file mode 100644
+index 0000000000..239942d27e
+--- /dev/null
++++ b/grub-core/commands/ieee1275/ibmvtpm.c
+@@ -0,0 +1,155 @@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2022  Free Software Foundation, Inc.
++ *  Copyright (C) 2022  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/>.
++ *
++ *  IBM vTPM support code.
++ */
++
++#include <grub/err.h>
++#include <grub/types.h>
++#include <grub/tpm.h>
++#include <grub/ieee1275/ieee1275.h>
++#include <grub/mm.h>
++#include <grub/misc.h>
++
++static grub_ieee1275_ihandle_t tpm_ihandle;
++static grub_uint8_t tpm_version;
++
++#define IEEE1275_IHANDLE_INVALID ((grub_ieee1275_ihandle_t) 0)
++
++static void
++tpm_get_tpm_version (void)
++{
++  grub_ieee1275_phandle_t vtpm;
++  char buffer[20];
++
++  if (!grub_ieee1275_finddevice ("/vdevice/vtpm", &vtpm) &&
++      !grub_ieee1275_get_property (vtpm, "compatible", buffer,
++				   sizeof (buffer), NULL) &&
++      !grub_strcmp (buffer, "IBM,vtpm20"))
++    tpm_version = 2;
++}
++
++static grub_err_t
++tpm_init (void)
++{
++  static int init_success = 0;
++
++  if (!init_success)
++    {
++      if (grub_ieee1275_open ("/vdevice/vtpm", &tpm_ihandle) < 0)
++	{
++	  tpm_ihandle = IEEE1275_IHANDLE_INVALID;
++	  return GRUB_ERR_UNKNOWN_DEVICE;
++	}
++
++      init_success = 1;
++
++      tpm_get_tpm_version ();
++    }
++
++  return GRUB_ERR_NONE;
++}
++
++static int
++ibmvtpm_2hash_ext_log (grub_uint8_t pcrindex,
++		       grub_uint32_t eventtype,
++		       const char *description,
++		       grub_size_t description_size,
++		       void *buf, grub_size_t size)
++{
++  struct tpm_2hash_ext_log
++  {
++    struct grub_ieee1275_common_hdr common;
++    grub_ieee1275_cell_t method;
++    grub_ieee1275_cell_t ihandle;
++    grub_ieee1275_cell_t size;
++    grub_ieee1275_cell_t buf;
++    grub_ieee1275_cell_t description_size;
++    grub_ieee1275_cell_t description;
++    grub_ieee1275_cell_t eventtype;
++    grub_ieee1275_cell_t pcrindex;
++    grub_ieee1275_cell_t catch_result;
++    grub_ieee1275_cell_t rc;
++  };
++  struct tpm_2hash_ext_log args;
++
++  INIT_IEEE1275_COMMON (&args.common, "call-method", 8, 2);
++  args.method = (grub_ieee1275_cell_t) "2hash-ext-log";
++  args.ihandle = tpm_ihandle;
++  args.pcrindex = pcrindex;
++  args.eventtype = eventtype;
++  args.description = (grub_ieee1275_cell_t) description;
++  args.description_size = description_size;
++  args.buf = (grub_ieee1275_cell_t) buf;
++  args.size = (grub_ieee1275_cell_t) size;
++
++  if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
++    return -1;
++
++  /*
++   * catch_result is set if firmware does not support 2hash-ext-log
++   * rc is GRUB_IEEE1275_CELL_FALSE (0) on failure
++   */
++  if ((args.catch_result) || args.rc == GRUB_IEEE1275_CELL_FALSE)
++    return -1;
++
++  return 0;
++}
++
++static grub_err_t
++tpm2_log_event (unsigned char *buf, grub_size_t size, grub_uint8_t pcr,
++		const char *description)
++{
++  static int error_displayed = 0;
++  int rc;
++
++  rc = ibmvtpm_2hash_ext_log (pcr, EV_IPL,
++			      description, grub_strlen(description) + 1,
++			      buf, size);
++  if (rc && !error_displayed)
++    {
++      error_displayed++;
++      return grub_error (GRUB_ERR_BAD_DEVICE,
++			 "2HASH-EXT-LOG failed: Firmware is likely too old.\n");
++    }
++
++  return GRUB_ERR_NONE;
++}
++
++grub_err_t
++grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr,
++		  const char *description)
++{
++  /*
++   * Call tpm_init() 'late' rather than from GRUB_MOD_INIT() so that device nodes
++   * can be found.
++   */
++  grub_err_t err = tpm_init ();
++
++  /* Absence of a TPM isn't a failure. */
++  if (err != GRUB_ERR_NONE)
++    return GRUB_ERR_NONE;
++
++  grub_dprintf ("tpm", "log_event, pcr = %d, size = 0x%" PRIxGRUB_SIZE ", %s\n",
++		pcr, size, description);
++
++  if (tpm_version == 2)
++    return tpm2_log_event (buf, size, pcr, description);
++
++  return GRUB_ERR_NONE;
++}
+diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h
+index 560c968460..27b9cf259b 100644
+--- a/include/grub/ieee1275/ieee1275.h
++++ b/include/grub/ieee1275/ieee1275.h
+@@ -24,6 +24,9 @@
+ #include <grub/types.h>
+ #include <grub/machine/ieee1275.h>
+ 
++#define GRUB_IEEE1275_CELL_FALSE       ((grub_ieee1275_cell_t) 0)
++#define GRUB_IEEE1275_CELL_TRUE        ((grub_ieee1275_cell_t) -1)
++
+ struct grub_ieee1275_mem_region
+ {
+   unsigned int start;
+diff --git a/docs/grub.texi b/docs/grub.texi
+index 1750b72ee9..825278a7f3 100644
+--- a/docs/grub.texi
++++ b/docs/grub.texi
+@@ -6235,7 +6235,8 @@ tpm module is loaded. As such it is recommended that the tpm module be built
+ into @file{core.img} in order to avoid a potential gap in measurement between
+ @file{core.img} being loaded and the tpm module being loaded.
+ 
+-Measured boot is currently only supported on EFI platforms.
++Measured boot is currently only supported on EFI and IBM IEEE1275 PowerPC
++platforms.
+ 
+ @node Lockdown
+ @section Lockdown when booting on a secure setup
diff --git a/SOURCES/0318-powerpc-Drop-Open-Hack-Ware.patch b/SOURCES/0318-powerpc-Drop-Open-Hack-Ware.patch
new file mode 100644
index 0000000..1d201d3
--- /dev/null
+++ b/SOURCES/0318-powerpc-Drop-Open-Hack-Ware.patch
@@ -0,0 +1,69 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens <dja@axtens.net>
+Date: Mon, 6 Sep 2021 15:46:11 +1000
+Subject: [PATCH] powerpc: Drop Open Hack'Ware
+
+Open Hack'Ware was an alternative firmware of powerpc under QEMU.
+
+The last commit to any Open Hack'Ware repo I can find is from 2014 [1].
+
+Open Hack'Ware was used for the QEMU "prep" machine type, which was
+deprecated in QEMU in commit 54c86f5a4844 (hw/ppc: deprecate the
+machine type 'prep', replaced by '40p') in QEMU v3.1, and had reportedly
+been broken for years before without anyone noticing. Support was removed
+in February 2020 by commit b2ce76a0730e (hw/ppc/prep: Remove the
+deprecated "prep" machine and the OpenHackware BIOS).
+
+Open Hack'Ware's limitations require some messy code in GRUB. This
+complexity is not worth carrying any more.
+
+Remove detection of Open Hack'Ware. We will clean up the feature flags
+in following commits.
+
+[1]: https://github.com/qemu/openhackware and
+     https://repo.or.cz/w/openhackware.git are QEMU submodules. They have
+     only small changes on top of OHW v0.4.1, which was imported into
+     QEMU SCM in 2010. I can't find anything resembling an official repo
+     any more.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit f9ce538eec88c5cffbfde021c4e8a95a5e9d0e8f)
+---
+ grub-core/kern/ieee1275/cmain.c | 16 ----------------
+ 1 file changed, 16 deletions(-)
+
+diff --git a/grub-core/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c
+index dce7b84922..cb42f60ebe 100644
+--- a/grub-core/kern/ieee1275/cmain.c
++++ b/grub-core/kern/ieee1275/cmain.c
+@@ -49,7 +49,6 @@ grub_ieee1275_find_options (void)
+   grub_ieee1275_phandle_t root;
+   grub_ieee1275_phandle_t options;
+   grub_ieee1275_phandle_t openprom;
+-  grub_ieee1275_phandle_t bootrom;
+   int rc;
+   grub_uint32_t realmode = 0;
+   char tmp[256];
+@@ -198,21 +197,6 @@ grub_ieee1275_find_options (void)
+ 
+       grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_HAS_CURSORONOFF);
+     }
+-
+-  if (! grub_ieee1275_finddevice ("/rom/boot-rom", &bootrom)
+-      || ! grub_ieee1275_finddevice ("/boot-rom", &bootrom))
+-    {
+-      rc = grub_ieee1275_get_property (bootrom, "model", tmp, sizeof (tmp), 0);
+-      if (rc >= 0 && !grub_strncmp (tmp, "PPC Open Hack'Ware",
+-				    sizeof ("PPC Open Hack'Ware") - 1))
+-	{
+-	  grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_BROKEN_OUTPUT);
+-	  grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CANNOT_SET_COLORS);
+-	  grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CANNOT_INTERPRET);
+-	  grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM);
+-	  grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_ANSI);
+-	}
+-    }
+ }
+ 
+ void
diff --git a/SOURCES/0319-osdep-linux-hostdisk-Modify-sector-by-sysfs-as-disk-.patch b/SOURCES/0319-osdep-linux-hostdisk-Modify-sector-by-sysfs-as-disk-.patch
new file mode 100644
index 0000000..6e7b17f
--- /dev/null
+++ b/SOURCES/0319-osdep-linux-hostdisk-Modify-sector-by-sysfs-as-disk-.patch
@@ -0,0 +1,70 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Mukesh Kumar Chaurasiya <mchauras@linux.vnet.ibm.com>
+Date: Thu, 9 Feb 2023 13:09:16 +0530
+Subject: [PATCH] osdep/linux/hostdisk: Modify sector by sysfs as disk sector
+
+The disk sector size provided by sysfs file system considers the sector
+size of 512 irrespective of disk sector size, thus causing the read by
+the GRUB to an incorrect offset from what was originally intended.
+
+Considering the 512 sector size of sysfs data the actual sector needs to
+be modified corresponding to disk sector size.
+
+Signed-off-by: Mukesh Kumar Chaurasiya <mchauras@linux.vnet.ibm.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit f7564844f82b57078d601befadc438b5bc1fa01b)
+---
+ grub-core/osdep/linux/hostdisk.c | 7 ++++---
+ include/grub/disk.h              | 7 +++++++
+ 2 files changed, 11 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/osdep/linux/hostdisk.c b/grub-core/osdep/linux/hostdisk.c
+index 7bc99ac1c1..a9ea0bb465 100644
+--- a/grub-core/osdep/linux/hostdisk.c
++++ b/grub-core/osdep/linux/hostdisk.c
+@@ -240,7 +240,8 @@ have_devfs (void)
+ #pragma GCC diagnostic ignored "-Wformat-nonliteral"
+ 
+ static int
+-grub_hostdisk_linux_find_partition (char *dev, grub_disk_addr_t sector)
++grub_hostdisk_linux_find_partition (const grub_disk_t disk, char *dev,
++                                    grub_disk_addr_t sector)
+ {
+   size_t len = strlen (dev);
+   const char *format;
+@@ -305,7 +306,7 @@ grub_hostdisk_linux_find_partition (char *dev, grub_disk_addr_t sector)
+       if (fstat (fd, &st) < 0
+ 	  || !grub_util_device_is_mapped_stat (&st)
+ 	  || !grub_util_get_dm_node_linear_info (st.st_rdev, 0, 0, &start))
+-	start = grub_util_find_partition_start_os (real_dev);
++	start = grub_disk_to_native_sector (disk, grub_util_find_partition_start_os (real_dev));
+       /* We don't care about errors here.  */
+       grub_errno = GRUB_ERR_NONE;
+ 
+@@ -386,7 +387,7 @@ grub_util_fd_open_device (const grub_disk_t disk, grub_disk_addr_t sector, int f
+ 	&& strncmp (dev, "/dev/", 5) == 0)
+       {
+ 	if (sector >= part_start)
+-	  is_partition = grub_hostdisk_linux_find_partition (dev, part_start);
++	  is_partition = grub_hostdisk_linux_find_partition (disk, dev, part_start);
+ 	else
+ 	  *max = part_start - sector;
+       }
+diff --git a/include/grub/disk.h b/include/grub/disk.h
+index 06210a7049..881addcc77 100644
+--- a/include/grub/disk.h
++++ b/include/grub/disk.h
+@@ -208,6 +208,13 @@ grub_disk_from_native_sector (grub_disk_t disk, grub_disk_addr_t sector)
+   return sector << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS);
+ }
+ 
++/* Convert from GRUB native disk sized sector to disk sized sector. */
++static inline grub_disk_addr_t
++grub_disk_to_native_sector (grub_disk_t disk, grub_disk_addr_t sector)
++{
++  return sector >> (disk->log_sector_size - GRUB_DISK_SECTOR_BITS);
++}
++
+ /* This is called from the memory manager.  */
+ void grub_disk_cache_invalidate_all (void);
+ 
diff --git a/SOURCES/0320-mm-Adjust-new-region-size-to-take-management-overhea.patch b/SOURCES/0320-mm-Adjust-new-region-size-to-take-management-overhea.patch
new file mode 100644
index 0000000..456f8f7
--- /dev/null
+++ b/SOURCES/0320-mm-Adjust-new-region-size-to-take-management-overhea.patch
@@ -0,0 +1,145 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zhang Boyang <zhangboyang.id@gmail.com>
+Date: Sun, 29 Jan 2023 19:49:31 +0800
+Subject: [PATCH] mm: Adjust new region size to take management overhead into
+ account
+
+When grub_memalign() encounters out-of-memory, it will try
+grub_mm_add_region_fn() to request more memory from system firmware.
+However, the size passed to it doesn't take region management overhead
+into account. Adding a memory area of "size" bytes may result in a heap
+region of less than "size" bytes really available. Thus, the new region
+may not be adequate for current allocation request, confusing
+out-of-memory handling code.
+
+This patch introduces GRUB_MM_MGMT_OVERHEAD to address the region
+management overhead (e.g. metadata, padding). The value of this new
+constant must be large enough to make sure grub_memalign(align, size)
+always succeeds after a successful call to
+  grub_mm_init_region(addr, size + align + GRUB_MM_MGMT_OVERHEAD),
+for any given addr and size (assuming no integer overflow).
+
+The size passed to grub_mm_add_region_fn() is now correctly adjusted,
+thus if grub_mm_add_region_fn() succeeded, current allocation request
+can always succeed.
+
+Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 2282cbfe5aa1ff6c1bbcbdcd2003089ad7c03ba3)
+---
+ grub-core/kern/mm.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 61 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
+index da1ac9427c..f29a3e5cbd 100644
+--- a/grub-core/kern/mm.c
++++ b/grub-core/kern/mm.c
+@@ -83,6 +83,46 @@
+ 
+ 
+ 
++/*
++ * GRUB_MM_MGMT_OVERHEAD is an upper bound of management overhead of
++ * each region, with any possible padding taken into account.
++ *
++ * The value must be large enough to make sure grub_memalign(align, size)
++ * always succeeds after a successful call to
++ * grub_mm_init_region(addr, size + align + GRUB_MM_MGMT_OVERHEAD),
++ * for any given addr, align and size (assuming no interger overflow).
++ *
++ * The worst case which has maximum overhead is shown in the figure below:
++ *
++ * +-- addr
++ * v                                           |<- size + align ->|
++ * +---------+----------------+----------------+------------------+---------+
++ * | padding | grub_mm_region | grub_mm_header |   usable bytes   | padding |
++ * +---------+----------------+----------------+------------------+---------+
++ * |<-  a  ->|<-     b      ->|<-     c      ->|<-      d       ->|<-  e  ->|
++ *                                             ^
++ *    b == sizeof (struct grub_mm_region)      | / Assuming no other suitable
++ *    c == sizeof (struct grub_mm_header)      | | block is available, then:
++ *    d == size + align                        +-| If align == 0, this will be
++ *                                               | the pointer returned by next
++ * Assuming addr % GRUB_MM_ALIGN == 1, then:     | grub_memalign(align, size).
++ *    a == GRUB_MM_ALIGN - 1                     | If align > 0, this chunk may
++ *                                               | need to be split to fulfill
++ * Assuming d % GRUB_MM_ALIGN == 1, then:        | alignment requirements, and
++ *    e == GRUB_MM_ALIGN - 1                     | the returned pointer may be
++ *                                               \ inside these usable bytes.
++ * Therefore, the maximum overhead is:
++ *    a + b + c + e == (GRUB_MM_ALIGN - 1) + sizeof (struct grub_mm_region)
++ *                     + sizeof (struct grub_mm_header) + (GRUB_MM_ALIGN - 1)
++ */
++#define GRUB_MM_MGMT_OVERHEAD	((GRUB_MM_ALIGN - 1) \
++				 + sizeof (struct grub_mm_region) \
++				 + sizeof (struct grub_mm_header) \
++				 + (GRUB_MM_ALIGN - 1))
++
++/* The size passed to grub_mm_add_region_fn() is aligned up by this value. */
++#define GRUB_MM_HEAP_GROW_ALIGN	4096
++
+ grub_mm_region_t grub_mm_base;
+ grub_mm_add_region_func_t grub_mm_add_region_fn;
+ 
+@@ -230,6 +270,11 @@ grub_mm_init_region (void *addr, grub_size_t size)
+ 
+   grub_dprintf ("regions", "No: considering a new region at %p of size %" PRIxGRUB_SIZE "\n",
+ 		addr, size);
++  /*
++   * If you want to modify the code below, please also take a look at
++   * GRUB_MM_MGMT_OVERHEAD and make sure it is synchronized with the code.
++   */
++
+   /* Allocate a region from the head.  */
+   r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
+ 
+@@ -410,6 +455,7 @@ grub_memalign (grub_size_t align, grub_size_t size)
+ {
+   grub_mm_region_t r;
+   grub_size_t n = ((size + GRUB_MM_ALIGN - 1) >> GRUB_MM_ALIGN_LOG2) + 1;
++  grub_size_t grow;
+   int count = 0;
+ 
+   if (!grub_mm_base)
+@@ -418,10 +464,22 @@ grub_memalign (grub_size_t align, grub_size_t size)
+   if (size > ~(grub_size_t) align)
+     goto fail;
+ 
++  /*
++   * Pre-calculate the necessary size of heap growth (if applicable),
++   * with region management overhead taken into account.
++   */
++  if (grub_add (size + align, GRUB_MM_MGMT_OVERHEAD, &grow))
++    goto fail;
++
++  /* Align up heap growth to make it friendly to CPU/MMU. */
++  if (grow > ~(grub_size_t) (GRUB_MM_HEAP_GROW_ALIGN - 1))
++    goto fail;
++  grow = ALIGN_UP (grow, GRUB_MM_HEAP_GROW_ALIGN);
++
+   /* We currently assume at least a 32-bit grub_size_t,
+      so limiting allocations to <adress space size> - 1MiB
+      in name of sanity is beneficial. */
+-  if ((size + align) > ~(grub_size_t) 0x100000)
++  if (grow > ~(grub_size_t) 0x100000)
+     goto fail;
+ 
+   align = (align >> GRUB_MM_ALIGN_LOG2);
+@@ -447,7 +505,7 @@ grub_memalign (grub_size_t align, grub_size_t size)
+       count++;
+ 
+       if (grub_mm_add_region_fn != NULL &&
+-          grub_mm_add_region_fn (size, GRUB_MM_ADD_REGION_CONSECUTIVE) == GRUB_ERR_NONE)
++          grub_mm_add_region_fn (grow, GRUB_MM_ADD_REGION_CONSECUTIVE) == GRUB_ERR_NONE)
+ 	goto again;
+ 
+       /* fallthrough  */
+@@ -462,7 +520,7 @@ grub_memalign (grub_size_t align, grub_size_t size)
+            * Try again even if this fails, in case it was able to partially
+            * satisfy the request
+            */
+-          grub_mm_add_region_fn (size, GRUB_MM_ADD_REGION_NONE);
++          grub_mm_add_region_fn (grow, GRUB_MM_ADD_REGION_NONE);
+           goto again;
+         }
+ 
diff --git a/SOURCES/0321-mm-Preallocate-some-space-when-adding-new-regions.patch b/SOURCES/0321-mm-Preallocate-some-space-when-adding-new-regions.patch
new file mode 100644
index 0000000..291092d
--- /dev/null
+++ b/SOURCES/0321-mm-Preallocate-some-space-when-adding-new-regions.patch
@@ -0,0 +1,53 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zhang Boyang <zhangboyang.id@gmail.com>
+Date: Sun, 29 Jan 2023 19:49:32 +0800
+Subject: [PATCH] mm: Preallocate some space when adding new regions
+
+When grub_memalign() encounters out-of-memory, it will try
+grub_mm_add_region_fn() to request more memory from system firmware.
+However, it doesn't preallocate memory space for future allocation
+requests. In extreme cases, it requires one call to
+grub_mm_add_region_fn() for each memory allocation request. This can
+be very slow.
+
+This patch introduces GRUB_MM_HEAP_GROW_EXTRA, the minimal heap growth
+granularity. The new region size is now set to the bigger one of its
+original value and GRUB_MM_HEAP_GROW_EXTRA. Thus, it will result in some
+memory space preallocated if current allocations request is small.
+
+The value of GRUB_MM_HEAP_GROW_EXTRA is set to 1MB. If this value is
+smaller, the cost of small memory allocations will be higher. If this
+value is larger, more memory will be wasted and it might cause
+out-of-memory on machines with small amount of RAM.
+
+Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 21869baec15239b6d99122b32b14a778af4c754f)
+---
+ grub-core/kern/mm.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
+index f29a3e5cbd..cc8a4703bc 100644
+--- a/grub-core/kern/mm.c
++++ b/grub-core/kern/mm.c
+@@ -123,6 +123,9 @@
+ /* The size passed to grub_mm_add_region_fn() is aligned up by this value. */
+ #define GRUB_MM_HEAP_GROW_ALIGN	4096
+ 
++/* Minimal heap growth granularity when existing heap space is exhausted. */
++#define GRUB_MM_HEAP_GROW_EXTRA	0x100000
++
+ grub_mm_region_t grub_mm_base;
+ grub_mm_add_region_func_t grub_mm_add_region_fn;
+ 
+@@ -471,6 +474,9 @@ grub_memalign (grub_size_t align, grub_size_t size)
+   if (grub_add (size + align, GRUB_MM_MGMT_OVERHEAD, &grow))
+     goto fail;
+ 
++  /* Preallocate some extra space if heap growth is small. */
++  grow = grub_max (grow, GRUB_MM_HEAP_GROW_EXTRA);
++
+   /* Align up heap growth to make it friendly to CPU/MMU. */
+   if (grow > ~(grub_size_t) (GRUB_MM_HEAP_GROW_ALIGN - 1))
+     goto fail;
diff --git a/SOURCES/0322-mm-Avoid-complex-heap-growth-math-in-hot-path.patch b/SOURCES/0322-mm-Avoid-complex-heap-growth-math-in-hot-path.patch
new file mode 100644
index 0000000..fe6b3e1
--- /dev/null
+++ b/SOURCES/0322-mm-Avoid-complex-heap-growth-math-in-hot-path.patch
@@ -0,0 +1,72 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zhang Boyang <zhangboyang.id@gmail.com>
+Date: Sun, 29 Jan 2023 19:49:33 +0800
+Subject: [PATCH] mm: Avoid complex heap growth math in hot path
+
+We do a lot of math about heap growth in hot path of grub_memalign().
+However, the result is only used if out of memory is encountered, which
+is seldom.
+
+This patch moves these calculations away from hot path. These
+calculations are now only done if out of memory is encountered. This
+change can also help compiler to optimize integer overflow checks away.
+
+Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
+Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
+(cherry picked from commit 65bc45963014773e2062ccc63ff34a089d2e352e)
+---
+ grub-core/kern/mm.c | 34 ++++++++++++++++++++--------------
+ 1 file changed, 20 insertions(+), 14 deletions(-)
+
+diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
+index cc8a4703bc..630d7be0e2 100644
+--- a/grub-core/kern/mm.c
++++ b/grub-core/kern/mm.c
+@@ -467,20 +467,7 @@ grub_memalign (grub_size_t align, grub_size_t size)
+   if (size > ~(grub_size_t) align)
+     goto fail;
+ 
+-  /*
+-   * Pre-calculate the necessary size of heap growth (if applicable),
+-   * with region management overhead taken into account.
+-   */
+-  if (grub_add (size + align, GRUB_MM_MGMT_OVERHEAD, &grow))
+-    goto fail;
+-
+-  /* Preallocate some extra space if heap growth is small. */
+-  grow = grub_max (grow, GRUB_MM_HEAP_GROW_EXTRA);
+-
+-  /* Align up heap growth to make it friendly to CPU/MMU. */
+-  if (grow > ~(grub_size_t) (GRUB_MM_HEAP_GROW_ALIGN - 1))
+-    goto fail;
+-  grow = ALIGN_UP (grow, GRUB_MM_HEAP_GROW_ALIGN);
++  grow = size + align;
+ 
+   /* We currently assume at least a 32-bit grub_size_t,
+      so limiting allocations to <adress space size> - 1MiB
+@@ -510,6 +497,25 @@ grub_memalign (grub_size_t align, grub_size_t size)
+       /* Request additional pages, contiguous */
+       count++;
+ 
++      /*
++       * Calculate the necessary size of heap growth (if applicable),
++       * with region management overhead taken into account.
++       */
++      if (grub_add (grow, GRUB_MM_MGMT_OVERHEAD, &grow))
++	goto fail;
++
++      /* Preallocate some extra space if heap growth is small. */
++      grow = grub_max (grow, GRUB_MM_HEAP_GROW_EXTRA);
++
++      /* Align up heap growth to make it friendly to CPU/MMU. */
++      if (grow > ~(grub_size_t) (GRUB_MM_HEAP_GROW_ALIGN - 1))
++	goto fail;
++      grow = ALIGN_UP (grow, GRUB_MM_HEAP_GROW_ALIGN);
++
++      /* Do the same sanity check again. */
++      if (grow > ~(grub_size_t) 0x100000)
++	goto fail;
++
+       if (grub_mm_add_region_fn != NULL &&
+           grub_mm_add_region_fn (grow, GRUB_MM_ADD_REGION_CONSECUTIVE) == GRUB_ERR_NONE)
+ 	goto again;
diff --git a/SOURCES/grub.macros b/SOURCES/grub.macros
index 62ed7b6..4a2883d 100755
--- a/SOURCES/grub.macros
+++ b/SOURCES/grub.macros
@@ -9,11 +9,14 @@
 %global _configure ../configure
 
 %if %{?_with_ccache: 1}%{?!_with_ccache: 0}
-%global cc_equals CC=/usr/%{_lib}/ccache/gcc
+%global ccpath /usr/%{_lib}/ccache/gcc
 %else
-%global cc_equals %{nil}
+%global ccpath %{__cc}
 %endif
 
+# gnulib actively ignores CFLAGS because it's terrible
+%global cc_equals "CC=%{ccpath} -fPIE -Wl,-z,noexecstack"
+
 %global cflags_sed						\\\
 	sed							\\\
 		-e 's/-O. //g'					\\\
@@ -122,7 +125,7 @@
 %endif
 
 %ifarch ppc64le
-%global platform_modules " appendedsig tpm "
+%global platform_modules " appendedsig ofnet tpm "
 %endif
 
 %ifarch aarch64 %{arm} riscv64
@@ -322,6 +325,7 @@ 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		\
+rm -r build-aux m4						\
 ./bootstrap							\
 %{nil}
 
diff --git a/SOURCES/grub.patches b/SOURCES/grub.patches
index e38c069..510ab16 100644
--- a/SOURCES/grub.patches
+++ b/SOURCES/grub.patches
@@ -20,287 +20,303 @@ 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
-Patch0222: 0222-Print-module-name-on-license-check-failure.patch
-Patch0223: 0223-powerpc-ieee1275-load-grub-at-4MB-not-2MB.patch
-Patch0224: 0224-grub-mkconfig-restore-umask-for-grub.cfg.patch
-Patch0225: 0225-commands-search-Fix-bug-stopping-iteration-when-no-f.patch
-Patch0226: 0226-search-new-efidisk-only-option-on-EFI-systems.patch
-Patch0227: 0227-efi-new-connectefi-command.patch
-Patch0228: 0228-powerpc-do-CAS-in-a-more-compatible-way.patch
-Patch0229: 0229-powerpc-prefix-detection-support-device-names-with-c.patch
-Patch0230: 0230-ibmvtpm-Add-support-for-trusted-boot-using-a-vTPM-2..patch
-Patch0231: 0231-make-ofdisk_retries-optional.patch
-Patch0232: 0232-loader-efi-chainloader-grub_load_and_start_image-doe.patch
-Patch0233: 0233-loader-efi-chainloader-simplify-the-loader-state.patch
-Patch0234: 0234-commands-boot-Add-API-to-pass-context-to-loader.patch
-Patch0235: 0235-loader-efi-chainloader-Use-grub_loader_set_ex.patch
-Patch0236: 0236-loader-i386-efi-linux-Avoid-a-use-after-free-in-the-.patch
-Patch0237: 0237-loader-i386-efi-linux-Use-grub_loader_set_ex.patch
-Patch0238: 0238-loader-i386-efi-linux-Fix-a-memory-leak-in-the-initr.patch
-Patch0239: 0239-kern-efi-sb-Reject-non-kernel-files-in-the-shim_lock.patch
-Patch0240: 0240-kern-file-Do-not-leak-device_name-on-error-in-grub_f.patch
-Patch0241: 0241-video-readers-png-Abort-sooner-if-a-read-operation-f.patch
-Patch0242: 0242-video-readers-png-Refuse-to-handle-multiple-image-he.patch
-Patch0243: 0243-video-readers-png-Drop-greyscale-support-to-fix-heap.patch
-Patch0244: 0244-video-readers-png-Avoid-heap-OOB-R-W-inserting-huff-.patch
-Patch0245: 0245-video-readers-png-Sanity-check-some-huffman-codes.patch
-Patch0246: 0246-video-readers-jpeg-Abort-sooner-if-a-read-operation-.patch
-Patch0247: 0247-video-readers-jpeg-Do-not-reallocate-a-given-huff-ta.patch
-Patch0248: 0248-video-readers-jpeg-Refuse-to-handle-multiple-start-o.patch
-Patch0249: 0249-video-readers-jpeg-Block-int-underflow-wild-pointer-.patch
-Patch0250: 0250-normal-charset-Fix-array-out-of-bounds-formatting-un.patch
-Patch0251: 0251-net-netbuff-Block-overly-large-netbuff-allocs.patch
-Patch0252: 0252-net-ip-Do-IP-fragment-maths-safely.patch
-Patch0253: 0253-net-dns-Fix-double-free-addresses-on-corrupt-DNS-res.patch
-Patch0254: 0254-net-dns-Don-t-read-past-the-end-of-the-string-we-re-.patch
-Patch0255: 0255-net-tftp-Prevent-a-UAF-and-double-free-from-a-failed.patch
-Patch0256: 0256-net-tftp-Avoid-a-trivial-UAF.patch
-Patch0257: 0257-net-http-Do-not-tear-down-socket-if-it-s-already-bee.patch
-Patch0258: 0258-net-http-Fix-OOB-write-for-split-http-headers.patch
-Patch0259: 0259-net-http-Error-out-on-headers-with-LF-without-CR.patch
-Patch0260: 0260-fs-f2fs-Do-not-read-past-the-end-of-nat-journal-entr.patch
-Patch0261: 0261-fs-f2fs-Do-not-read-past-the-end-of-nat-bitmap.patch
-Patch0262: 0262-fs-f2fs-Do-not-copy-file-names-that-are-too-long.patch
-Patch0263: 0263-fs-btrfs-Fix-several-fuzz-issues-with-invalid-dir-it.patch
-Patch0264: 0264-fs-btrfs-Fix-more-ASAN-and-SEGV-issues-found-with-fu.patch
-Patch0265: 0265-fs-btrfs-Fix-more-fuzz-issues-related-to-chunks.patch
-Patch0266: 0266-misc-Make-grub_min-and-grub_max-more-resilient.patch
-Patch0267: 0267-ReiserFS-switch-to-using-grub_min-grub_max.patch
-Patch0268: 0268-misc-make-grub_boot_time-also-call-grub_dprintf-boot.patch
-Patch0269: 0269-modules-make-.module_license-read-only.patch
-Patch0270: 0270-modules-strip-.llvm_addrsig-sections-and-similar.patch
-Patch0271: 0271-modules-Don-t-allocate-space-for-non-allocable-secti.patch
-Patch0272: 0272-pe-add-the-DOS-header-struct-and-fix-some-bad-naming.patch
-Patch0273: 0273-EFI-allocate-kernel-in-EFI_RUNTIME_SERVICES_CODE-ins.patch
-Patch0274: 0274-modules-load-module-sections-at-page-aligned-address.patch
-Patch0275: 0275-nx-add-memory-attribute-get-set-API.patch
-Patch0276: 0276-nx-set-page-permissions-for-loaded-modules.patch
-Patch0277: 0277-nx-set-attrs-in-our-kernel-loaders.patch
-Patch0278: 0278-nx-set-the-nx-compatible-flag-in-EFI-grub-images.patch
-Patch0279: 0279-Make-debug-file-show-which-file-filters-get-run.patch
-Patch0280: 0280-efi-make-the-default-arena-most-of-ram.patch
-Patch0281: 0281-efi-use-enumerated-array-positions-for-our-allocatio.patch
-Patch0282: 0282-efi-split-allocation-policy-for-kernel-vs-initrd-mem.patch
-Patch0283: 0283-efi-allocate-the-initrd-within-the-bounds-expressed-.patch
-Patch0284: 0284-efi-use-EFI_LOADER_-CODE-DATA-for-kernel-and-initrd-.patch
-Patch0285: 0285-BLS-create-etc-kernel-cmdline-during-mkconfig.patch
-Patch0286: 0286-ieee1275-implement-vec5-for-cas-negotiation.patch
-Patch0287: 0287-squish-don-t-dup-rhgb-quiet-check-mtimes.patch
-Patch0288: 0288-squish-give-up-on-rhgb-quiet.patch
-Patch0289: 0289-squish-BLS-only-write-etc-kernel-cmdline-if-writable.patch
-Patch0290: 0290-x86-efi-Fix-an-incorrect-array-size-in-kernel-alloca.patch
-Patch0291: 0291-commands-efi-tpm-Refine-the-status-of-log-event.patch
-Patch0292: 0292-commands-efi-tpm-Use-grub_strcpy-instead-of-grub_mem.patch
-Patch0293: 0293-efi-tpm-Add-EFI_CC_MEASUREMENT_PROTOCOL-support.patch
-Patch0294: 0294-font-Reject-glyphs-exceeds-font-max_glyph_width-or-f.patch
-Patch0295: 0295-font-Fix-size-overflow-in-grub_font_get_glyph_intern.patch
-Patch0296: 0296-font-Fix-several-integer-overflows-in-grub_font_cons.patch
-Patch0297: 0297-font-Remove-grub_font_dup_glyph.patch
-Patch0298: 0298-font-Fix-integer-overflow-in-ensure_comb_space.patch
-Patch0299: 0299-font-Fix-integer-overflow-in-BMP-index.patch
-Patch0300: 0300-font-Fix-integer-underflow-in-binary-search-of-char-.patch
-Patch0301: 0301-kern-efi-sb-Enforce-verification-of-font-files.patch
-Patch0302: 0302-fbutil-Fix-integer-overflow.patch
-Patch0303: 0303-font-Fix-an-integer-underflow-in-blit_comb.patch
-Patch0304: 0304-font-Harden-grub_font_blit_glyph-and-grub_font_blit_.patch
-Patch0305: 0305-font-Assign-null_font-to-glyphs-in-ascii_font_glyph.patch
-Patch0306: 0306-normal-charset-Fix-an-integer-overflow-in-grub_unico.patch
+Patch0023: 0023-Enable-pager-by-default.-985860.patch
+Patch0024: 0024-Don-t-say-GNU-Linux-in-generated-menus.patch
+Patch0025: 0025-Add-.eh_frame-to-list-of-relocations-stripped.patch
+Patch0026: 0026-Don-t-require-a-password-to-boot-entries-generated-b.patch
+Patch0027: 0027-use-fw_path-prefix-when-fallback-searching-for-grub-.patch
+Patch0028: 0028-Try-mac-guid-etc-before-grub.cfg-on-tftp-config-file.patch
+Patch0029: 0029-Generate-OS-and-CLASS-in-10_linux-from-etc-os-releas.patch
+Patch0030: 0030-Minimize-the-sort-ordering-for-.debug-and-rescue-ker.patch
+Patch0031: 0031-Try-prefix-if-fw_path-doesn-t-work.patch
+Patch0032: 0032-Make-grub2-mkconfig-construct-titles-that-look-like-.patch
+Patch0033: 0033-Add-friendly-grub2-password-config-tool-985962.patch
+Patch0034: 0034-tcp-add-window-scaling-support.patch
+Patch0035: 0035-efinet-and-bootp-add-support-for-dhcpv6.patch
+Patch0036: 0036-Add-grub-get-kernel-settings-and-use-it-in-10_linux.patch
+Patch0037: 0037-bz1374141-fix-incorrect-mask-for-ppc64.patch
+Patch0038: 0038-Make-grub_fatal-also-backtrace.patch
+Patch0039: 0039-Make-our-info-pages-say-grub2-where-appropriate.patch
+Patch0040: 0040-macos-just-build-chainloader-entries-don-t-try-any-x.patch
+Patch0041: 0041-grub2-btrfs-Add-ability-to-boot-from-subvolumes.patch
+Patch0042: 0042-export-btrfs_subvol-and-btrfs_subvolid.patch
+Patch0043: 0043-grub2-btrfs-03-follow_default.patch
+Patch0044: 0044-grub2-btrfs-04-grub2-install.patch
+Patch0045: 0045-grub2-btrfs-05-grub2-mkconfig.patch
+Patch0046: 0046-grub2-btrfs-06-subvol-mount.patch
+Patch0047: 0047-Fallback-to-old-subvol-name-scheme-to-support-old-sn.patch
+Patch0048: 0048-Grub-not-working-correctly-with-btrfs-snapshots-bsc-.patch
+Patch0049: 0049-Add-grub_efi_allocate_pool-and-grub_efi_free_pool-wr.patch
+Patch0050: 0050-Use-grub_efi_.-memory-helpers-where-reasonable.patch
+Patch0051: 0051-Add-PRIxGRUB_EFI_STATUS-and-use-it.patch
+Patch0052: 0052-don-t-use-int-for-efi-status.patch
+Patch0053: 0053-make-GRUB_MOD_INIT-declare-its-function-prototypes.patch
+Patch0054: 0054-Don-t-guess-boot-efi-as-HFS-on-ppc-machines-in-grub-.patch
+Patch0055: 0055-20_linux_xen-load-xen-or-multiboot-2-modules-as-need.patch
+Patch0056: 0056-Make-pmtimer-tsc-calibration-not-take-51-seconds-to-.patch
+Patch0057: 0057-align-struct-efi_variable-better.patch
+Patch0058: 0058-Add-BLS-support-to-grub-mkconfig.patch
+Patch0059: 0059-Don-t-attempt-to-backtrace-on-grub_abort-for-grub-em.patch
+Patch0060: 0060-Add-grub2-switch-to-blscfg.patch
+Patch0061: 0061-make-better-backtraces.patch
+Patch0062: 0062-normal-don-t-draw-our-startup-message-if-debug-is-se.patch
+Patch0063: 0063-Work-around-some-minor-include-path-weirdnesses.patch
+Patch0064: 0064-Make-it-possible-to-enabled-build-id-sha1.patch
+Patch0065: 0065-Add-grub_qdprintf-grub_dprintf-without-the-file-line.patch
+Patch0066: 0066-Make-a-gdb-dprintf-that-tells-us-load-addresses.patch
+Patch0067: 0067-Fixup-for-newer-compiler.patch
+Patch0068: 0068-Don-t-attempt-to-export-the-start-and-_start-symbols.patch
+Patch0069: 0069-Fixup-for-newer-compiler.patch
+Patch0070: 0070-Add-support-for-non-Ethernet-network-cards.patch
+Patch0071: 0071-net-read-bracketed-ipv6-addrs-and-port-numbers.patch
+Patch0072: 0072-bootp-New-net_bootp6-command.patch
+Patch0073: 0073-efinet-UEFI-IPv6-PXE-support.patch
+Patch0074: 0074-grub.texi-Add-net_bootp6-doument.patch
+Patch0075: 0075-bootp-Add-processing-DHCPACK-packet-from-HTTP-Boot.patch
+Patch0076: 0076-efinet-Setting-network-from-UEFI-device-path.patch
+Patch0077: 0077-efinet-Setting-DNS-server-from-UEFI-protocol.patch
+Patch0078: 0078-Support-UEFI-networking-protocols.patch
+Patch0079: 0079-AUDIT-0-http-boot-tracker-bug.patch
+Patch0080: 0080-grub-editenv-Add-incr-command-to-increment-integer-v.patch
+Patch0081: 0081-Add-auto-hide-menu-support.patch
+Patch0082: 0082-Add-grub-set-bootflag-utility.patch
+Patch0083: 0083-docs-Add-grub-boot-indeterminate.service-example.patch
+Patch0084: 0084-gentpl-add-disable-support.patch
+Patch0085: 0085-gentpl-add-pc-firmware-type.patch
+Patch0086: 0086-efinet-also-use-the-firmware-acceleration-for-http.patch
+Patch0087: 0087-efi-http-Make-root_url-reflect-the-protocol-hostname.patch
+Patch0088: 0088-Make-it-so-we-can-tell-configure-which-cflags-utils-.patch
+Patch0089: 0089-module-verifier-make-it-possible-to-run-checkers-on-.patch
+Patch0090: 0090-Rework-how-the-fdt-command-builds.patch
+Patch0091: 0091-Disable-non-wordsize-allocations-on-arm.patch
+Patch0092: 0092-Prepend-prefix-when-HTTP-path-is-relative.patch
+Patch0093: 0093-Make-grub_error-more-verbose.patch
+Patch0094: 0094-Make-reset-an-alias-for-the-reboot-command.patch
+Patch0095: 0095-Add-a-version-command.patch
+Patch0096: 0096-Add-more-dprintf-and-nerf-dprintf-in-script.c.patch
+Patch0097: 0097-arm-arm64-loader-Better-memory-allocation-and-error-.patch
+Patch0098: 0098-Try-to-pick-better-locations-for-kernel-and-initrd.patch
+Patch0099: 0099-Attempt-to-fix-up-all-the-places-Wsign-compare-error.patch
+Patch0100: 0100-Don-t-use-Wno-sign-compare-Wno-conversion-Wno-error-.patch
+Patch0101: 0101-x86-efi-Use-bounce-buffers-for-reading-to-addresses-.patch
+Patch0102: 0102-x86-efi-Re-arrange-grub_cmd_linux-a-little-bit.patch
+Patch0103: 0103-x86-efi-Make-our-own-allocator-for-kernel-stuff.patch
+Patch0104: 0104-x86-efi-Allow-initrd-params-cmdline-allocations-abov.patch
+Patch0105: 0105-Fix-getroot.c-s-trampolines.patch
+Patch0106: 0106-Do-not-allow-stack-trampolines-anywhere.patch
+Patch0107: 0107-Reimplement-boot_counter.patch
+Patch0108: 0108-Fix-menu-entry-selection-based-on-ID-and-title.patch
+Patch0109: 0109-Make-the-menu-entry-users-option-argument-to-be-opti.patch
+Patch0110: 0110-Add-efi-export-env-and-efi-load-env-commands.patch
+Patch0111: 0111-Make-it-possible-to-subtract-conditions-from-debug.patch
+Patch0112: 0112-Export-all-variables-from-the-initial-context-when-c.patch
+Patch0113: 0113-grub.d-Split-out-boot-success-reset-from-menu-auto-h.patch
+Patch0114: 0114-Don-t-assume-that-boot-commands-will-only-return-on-.patch
+Patch0115: 0115-grub-set-bootflag-Update-comment-about-running-as-ro.patch
+Patch0116: 0116-grub-set-bootflag-Write-new-env-to-tmpfile-and-then-.patch
+Patch0117: 0117-grub.d-Fix-boot_indeterminate-getting-set-on-boot_su.patch
+Patch0118: 0118-Add-start-symbol-for-RISC-V.patch
+Patch0119: 0119-bootstrap.conf-Force-autogen.sh-to-use-python3.patch
+Patch0120: 0120-efi-http-Export-fw-http-_path-variables-to-make-them.patch
+Patch0121: 0121-efi-http-Enclose-literal-IPv6-addresses-in-square-br.patch
+Patch0122: 0122-efi-net-Allow-to-specify-a-port-number-in-addresses.patch
+Patch0123: 0123-efi-ip4_config-Improve-check-to-detect-literal-IPv6-.patch
+Patch0124: 0124-efi-net-Print-a-debug-message-if-parsing-the-address.patch
+Patch0125: 0125-kern-term-Also-accept-F8-as-a-user-interrupt-key.patch
+Patch0126: 0126-efi-Set-image-base-address-before-jumping-to-the-PE-.patch
+Patch0127: 0127-tpm-Don-t-propagate-TPM-measurement-errors-to-the-ve.patch
+Patch0128: 0128-x86-efi-Reduce-maximum-bounce-buffer-size-to-16-MiB.patch
+Patch0129: 0129-http-Prepend-prefix-when-the-HTTP-path-is-relative-a.patch
+Patch0130: 0130-Fix-a-missing-return-in-efi-export-env-and-efi-load-.patch
+Patch0131: 0131-efi-dhcp-fix-some-allocation-error-checking.patch
+Patch0132: 0132-efi-http-fix-some-allocation-error-checking.patch
+Patch0133: 0133-efi-ip-46-_config.c-fix-some-potential-allocation-ov.patch
+Patch0134: 0134-efilinux-Fix-integer-overflows-in-grub_cmd_initrd.patch
+Patch0135: 0135-linuxefi-fail-kernel-validation-without-shim-protoco.patch
+Patch0136: 0136-Fix-const-char-pointers-in-grub-core-net-bootp.c.patch
+Patch0137: 0137-Fix-const-char-pointers-in-grub-core-net-efi-ip4_con.patch
+Patch0138: 0138-Fix-const-char-pointers-in-grub-core-net-efi-ip6_con.patch
+Patch0139: 0139-Fix-const-char-pointers-in-grub-core-net-efi-net.c.patch
+Patch0140: 0140-Fix-const-char-pointers-in-grub-core-net-efi-pxe.c.patch
+Patch0141: 0141-Add-systemd-integration-scripts-to-make-systemctl-re.patch
+Patch0142: 0142-systemd-integration.sh-Also-set-old-menu_show_once-g.patch
+Patch0143: 0143-at_keyboard-use-set-1-when-keyboard-is-in-Translate-.patch
+Patch0144: 0144-grub-install-disable-support-for-EFI-platforms.patch
+Patch0145: 0145-New-with-debug-timestamps-configure-flag-to-prepend-.patch
+Patch0146: 0146-Added-debug-statements-to-grub_disk_open-and-grub_di.patch
+Patch0147: 0147-Introduce-function-grub_debug_is_enabled-void-return.patch
+Patch0148: 0148-Don-t-clear-screen-when-debugging-is-enabled.patch
+Patch0149: 0149-kern-file-Fix-error-handling-in-grub_file_open.patch
+Patch0150: 0150-grub_file_-instrumentation-new-file-debug-tag.patch
+Patch0151: 0151-ieee1275-Avoiding-many-unecessary-open-close.patch
+Patch0152: 0152-ieee1275-powerpc-implements-fibre-channel-discovery-.patch
+Patch0153: 0153-ieee1275-powerpc-enables-device-mapper-discovery.patch
+Patch0154: 0154-Add-at_keyboard_fallback_set-var-to-force-the-set-ma.patch
+Patch0155: 0155-Add-suport-for-signing-grub-with-an-appended-signatu.patch
+Patch0156: 0156-docs-grub-Document-signing-grub-under-UEFI.patch
+Patch0157: 0157-docs-grub-Document-signing-grub-with-an-appended-sig.patch
+Patch0158: 0158-dl-provide-a-fake-grub_dl_set_persistent-for-the-emu.patch
+Patch0159: 0159-pgp-factor-out-rsa_pad.patch
+Patch0160: 0160-crypto-move-storage-for-grub_crypto_pk_-to-crypto.c.patch
+Patch0161: 0161-posix_wrap-tweaks-in-preparation-for-libtasn1.patch
+Patch0162: 0162-libtasn1-import-libtasn1-4.16.0.patch
+Patch0163: 0163-libtasn1-disable-code-not-needed-in-grub.patch
+Patch0164: 0164-libtasn1-changes-for-grub-compatibility.patch
+Patch0165: 0165-libtasn1-compile-into-asn1-module.patch
+Patch0166: 0166-test_asn1-test-module-for-libtasn1.patch
+Patch0167: 0167-grub-install-support-embedding-x509-certificates.patch
+Patch0168: 0168-appended-signatures-import-GNUTLS-s-ASN.1-descriptio.patch
+Patch0169: 0169-appended-signatures-parse-PKCS-7-signedData-and-X.50.patch
+Patch0170: 0170-appended-signatures-support-verifying-appended-signa.patch
+Patch0171: 0171-appended-signatures-verification-tests.patch
+Patch0172: 0172-appended-signatures-documentation.patch
+Patch0173: 0173-ieee1275-enter-lockdown-based-on-ibm-secure-boot.patch
+Patch0174: 0174-ieee1275-drop-HEAP_MAX_ADDR-HEAP_MIN_SIZE.patch
+Patch0175: 0175-appendedsig-x509-Also-handle-the-Extended-Key-Usage-.patch
+Patch0176: 0176-ieee1275-ofdisk-retry-on-open-failure.patch
+Patch0177: 0177-Allow-chainloading-EFI-apps-from-loop-mounts.patch
+Patch0178: 0178-efinet-Add-DHCP-proxy-support.patch
+Patch0179: 0179-fs-ext2-Ignore-checksum-seed-incompat-feature.patch
+Patch0180: 0180-Don-t-update-the-cmdline-when-generating-legacy-menu.patch
+Patch0181: 0181-Suppress-gettext-error-message.patch
+Patch0182: 0182-grub-set-password-Always-use-boot-grub2-user.cfg-as-.patch
+Patch0183: 0183-templates-Check-for-EFI-at-runtime-instead-of-config.patch
+Patch0184: 0184-efi-Print-an-error-if-boot-to-firmware-setup-is-not-.patch
+Patch0185: 0185-arm64-Fix-EFI-loader-kernel-image-allocation.patch
+Patch0186: 0186-normal-main-Discover-the-device-to-read-the-config-f.patch
+Patch0187: 0187-powerpc-adjust-setting-of-prefix-for-signed-binary-c.patch
+Patch0188: 0188-fs-xfs-Fix-unreadable-filesystem-with-v4-superblock.patch
+Patch0189: 0189-Print-module-name-on-license-check-failure.patch
+Patch0190: 0190-powerpc-ieee1275-load-grub-at-4MB-not-2MB.patch
+Patch0191: 0191-grub-mkconfig-restore-umask-for-grub.cfg.patch
+Patch0192: 0192-fs-btrfs-Use-full-btrfs-bootloader-area.patch
+Patch0193: 0193-Add-Fedora-location-of-DejaVu-SANS-font.patch
+Patch0194: 0194-normal-menu-Don-t-show-Booting-s-msg-when-auto-booti.patch
+Patch0195: 0195-EFI-suppress-the-Welcome-to-GRUB-message-in-EFI-buil.patch
+Patch0196: 0196-EFI-console-Do-not-set-colorstate-until-the-first-te.patch
+Patch0197: 0197-EFI-console-Do-not-set-cursor-until-the-first-text-o.patch
+Patch0198: 0198-Use-visual-indentation-in-config.h.in.patch
+Patch0199: 0199-Where-present-ensure-config-util.h-precedes-config.h.patch
+Patch0200: 0200-Drop-gnulib-fix-base64.patch.patch
+Patch0201: 0201-Drop-gnulib-no-abort.patch.patch
+Patch0202: 0202-Update-gnulib-version-and-drop-most-gnulib-patches.patch
+Patch0203: 0203-commands-search-Fix-bug-stopping-iteration-when-no-f.patch
+Patch0204: 0204-search-new-efidisk-only-option-on-EFI-systems.patch
+Patch0205: 0205-efi-new-connectefi-command.patch
+Patch0206: 0206-grub-core-loader-i386-efi-linux.c-do-not-validate-ke.patch
+Patch0207: 0207-grub-core-loader-arm64-linux.c-do-not-validate-kerne.patch
+Patch0208: 0208-grub-core-loader-efi-chainloader.c-do-not-validate-c.patch
+Patch0209: 0209-grub-core-loader-efi-linux.c-drop-now-unused-grub_li.patch
+Patch0210: 0210-powerpc-prefix-detection-support-device-names-with-c.patch
+Patch0211: 0211-make-ofdisk_retries-optional.patch
+Patch0212: 0212-loader-efi-chainloader-grub_load_and_start_image-doe.patch
+Patch0213: 0213-loader-efi-chainloader-simplify-the-loader-state.patch
+Patch0214: 0214-commands-boot-Add-API-to-pass-context-to-loader.patch
+Patch0215: 0215-loader-efi-chainloader-Use-grub_loader_set_ex.patch
+Patch0216: 0216-loader-i386-efi-linux-Avoid-a-use-after-free-in-the-.patch
+Patch0217: 0217-loader-i386-efi-linux-Use-grub_loader_set_ex.patch
+Patch0218: 0218-loader-i386-efi-linux-Fix-a-memory-leak-in-the-initr.patch
+Patch0219: 0219-kern-efi-sb-Reject-non-kernel-files-in-the-shim_lock.patch
+Patch0220: 0220-kern-file-Do-not-leak-device_name-on-error-in-grub_f.patch
+Patch0221: 0221-video-readers-png-Abort-sooner-if-a-read-operation-f.patch
+Patch0222: 0222-video-readers-png-Refuse-to-handle-multiple-image-he.patch
+Patch0223: 0223-video-readers-png-Drop-greyscale-support-to-fix-heap.patch
+Patch0224: 0224-video-readers-png-Avoid-heap-OOB-R-W-inserting-huff-.patch
+Patch0225: 0225-video-readers-png-Sanity-check-some-huffman-codes.patch
+Patch0226: 0226-video-readers-jpeg-Abort-sooner-if-a-read-operation-.patch
+Patch0227: 0227-video-readers-jpeg-Do-not-reallocate-a-given-huff-ta.patch
+Patch0228: 0228-video-readers-jpeg-Refuse-to-handle-multiple-start-o.patch
+Patch0229: 0229-video-readers-jpeg-Block-int-underflow-wild-pointer-.patch
+Patch0230: 0230-normal-charset-Fix-array-out-of-bounds-formatting-un.patch
+Patch0231: 0231-net-netbuff-Block-overly-large-netbuff-allocs.patch
+Patch0232: 0232-net-ip-Do-IP-fragment-maths-safely.patch
+Patch0233: 0233-net-dns-Fix-double-free-addresses-on-corrupt-DNS-res.patch
+Patch0234: 0234-net-dns-Don-t-read-past-the-end-of-the-string-we-re-.patch
+Patch0235: 0235-net-tftp-Prevent-a-UAF-and-double-free-from-a-failed.patch
+Patch0236: 0236-net-tftp-Avoid-a-trivial-UAF.patch
+Patch0237: 0237-net-http-Do-not-tear-down-socket-if-it-s-already-bee.patch
+Patch0238: 0238-net-http-Fix-OOB-write-for-split-http-headers.patch
+Patch0239: 0239-net-http-Error-out-on-headers-with-LF-without-CR.patch
+Patch0240: 0240-fs-f2fs-Do-not-read-past-the-end-of-nat-journal-entr.patch
+Patch0241: 0241-fs-f2fs-Do-not-read-past-the-end-of-nat-bitmap.patch
+Patch0242: 0242-fs-f2fs-Do-not-copy-file-names-that-are-too-long.patch
+Patch0243: 0243-fs-btrfs-Fix-several-fuzz-issues-with-invalid-dir-it.patch
+Patch0244: 0244-fs-btrfs-Fix-more-ASAN-and-SEGV-issues-found-with-fu.patch
+Patch0245: 0245-fs-btrfs-Fix-more-fuzz-issues-related-to-chunks.patch
+Patch0246: 0246-misc-Make-grub_min-and-grub_max-more-resilient.patch
+Patch0247: 0247-ReiserFS-switch-to-using-grub_min-grub_max.patch
+Patch0248: 0248-misc-make-grub_boot_time-also-call-grub_dprintf-boot.patch
+Patch0249: 0249-modules-make-.module_license-read-only.patch
+Patch0250: 0250-modules-strip-.llvm_addrsig-sections-and-similar.patch
+Patch0251: 0251-modules-Don-t-allocate-space-for-non-allocable-secti.patch
+Patch0252: 0252-pe-add-the-DOS-header-struct-and-fix-some-bad-naming.patch
+Patch0253: 0253-EFI-allocate-kernel-in-EFI_RUNTIME_SERVICES_CODE-ins.patch
+Patch0254: 0254-modules-load-module-sections-at-page-aligned-address.patch
+Patch0255: 0255-nx-add-memory-attribute-get-set-API.patch
+Patch0256: 0256-nx-set-page-permissions-for-loaded-modules.patch
+Patch0257: 0257-nx-set-attrs-in-our-kernel-loaders.patch
+Patch0258: 0258-nx-set-the-nx-compatible-flag-in-EFI-grub-images.patch
+Patch0259: 0259-grub-probe-document-the-behavior-of-multiple-v.patch
+Patch0260: 0260-grub_fs_probe-dprint-errors-from-filesystems.patch
+Patch0261: 0261-fs-fat-don-t-error-when-mtime-is-0.patch
+Patch0262: 0262-Make-debug-file-show-which-file-filters-get-run.patch
+Patch0263: 0263-efi-use-enumerated-array-positions-for-our-allocatio.patch
+Patch0264: 0264-efi-split-allocation-policy-for-kernel-vs-initrd-mem.patch
+Patch0265: 0265-efi-allocate-the-initrd-within-the-bounds-expressed-.patch
+Patch0266: 0266-efi-use-EFI_LOADER_-CODE-DATA-for-kernel-and-initrd-.patch
+Patch0267: 0267-BLS-create-etc-kernel-cmdline-during-mkconfig.patch
+Patch0268: 0268-squish-don-t-dup-rhgb-quiet-check-mtimes.patch
+Patch0269: 0269-squish-give-up-on-rhgb-quiet.patch
+Patch0270: 0270-squish-BLS-only-write-etc-kernel-cmdline-if-writable.patch
+Patch0271: 0271-blscfg-Don-t-root-device-in-emu-builds.patch
+Patch0272: 0272-loader-arm64-linux-Remove-magic-number-header-field-.patch
+Patch0273: 0273-Correct-BSS-zeroing-on-aarch64.patch
+Patch0274: 0274-linuxefi-Invalidate-i-cache-before-starting-the-kern.patch
+Patch0275: 0275-x86-efi-Fix-an-incorrect-array-size-in-kernel-alloca.patch
+Patch0276: 0276-commands-efi-tpm-Refine-the-status-of-log-event.patch
+Patch0277: 0277-commands-efi-tpm-Use-grub_strcpy-instead-of-grub_mem.patch
+Patch0278: 0278-efi-tpm-Add-EFI_CC_MEASUREMENT_PROTOCOL-support.patch
+Patch0279: 0279-font-Reject-glyphs-exceeds-font-max_glyph_width-or-f.patch
+Patch0280: 0280-font-Fix-size-overflow-in-grub_font_get_glyph_intern.patch
+Patch0281: 0281-font-Fix-several-integer-overflows-in-grub_font_cons.patch
+Patch0282: 0282-font-Remove-grub_font_dup_glyph.patch
+Patch0283: 0283-font-Fix-integer-overflow-in-ensure_comb_space.patch
+Patch0284: 0284-font-Fix-integer-overflow-in-BMP-index.patch
+Patch0285: 0285-font-Fix-integer-underflow-in-binary-search-of-char-.patch
+Patch0286: 0286-kern-efi-sb-Enforce-verification-of-font-files.patch
+Patch0287: 0287-fbutil-Fix-integer-overflow.patch
+Patch0288: 0288-font-Fix-an-integer-underflow-in-blit_comb.patch
+Patch0289: 0289-font-Harden-grub_font_blit_glyph-and-grub_font_blit_.patch
+Patch0290: 0290-font-Assign-null_font-to-glyphs-in-ascii_font_glyph.patch
+Patch0291: 0291-normal-charset-Fix-an-integer-overflow-in-grub_unico.patch
+Patch0292: 0292-font-Try-opening-fonts-from-the-bundled-memdisk.patch
+Patch0293: 0293-mm-Clarify-grub_real_malloc.patch
+Patch0294: 0294-mm-grub_real_malloc-Make-small-allocs-comment-match-.patch
+Patch0295: 0295-mm-Document-grub_free.patch
+Patch0296: 0296-mm-Document-grub_mm_init_region.patch
+Patch0297: 0297-mm-Document-GRUB-internal-memory-management-structur.patch
+Patch0298: 0298-mm-Assert-that-we-preserve-header-vs-region-alignmen.patch
+Patch0299: 0299-mm-When-adding-a-region-merge-with-region-after-as-w.patch
+Patch0300: 0300-mm-Debug-support-for-region-operations.patch
+Patch0301: 0301-mm-Drop-unused-unloading-of-modules-on-OOM.patch
+Patch0302: 0302-mm-Allow-dynamically-requesting-additional-memory-re.patch
+Patch0303: 0303-kern-efi-mm-Always-request-a-fixed-number-of-pages-o.patch
+Patch0304: 0304-kern-efi-mm-Extract-function-to-add-memory-regions.patch
+Patch0305: 0305-kern-efi-mm-Pass-up-errors-from-add_memory_regions.patch
+Patch0306: 0306-kern-efi-mm-Implement-runtime-addition-of-pages.patch
+Patch0307: 0307-efi-Increase-default-memory-allocation-to-32-MiB.patch
+Patch0308: 0308-mm-Try-invalidate-disk-caches-last-when-out-of-memor.patch
+Patch0309: 0309-ppc64le-signed-boot-media-changes.patch
+Patch0310: 0310-core-Fix-several-implicit-function-declarations.patch
+Patch0311: 0311-loader-Add-support-for-grub-emu-to-kexec-Linux-menu-.patch
+Patch0312: 0312-powerpc-Drop-Open-Hack-Ware-remove-GRUB_IEEE1275_FLA.patch
+Patch0313: 0313-ieee1275-request-memory-with-ibm-client-architecture.patch
+Patch0314: 0314-ieee1275-drop-len-1-quirk-in-heap_init.patch
+Patch0315: 0315-ieee1275-support-runtime-memory-claiming.patch
+Patch0316: 0316-ieee1275-implement-vec5-for-cas-negotiation.patch
+Patch0317: 0317-ibmvtpm-Add-support-for-trusted-boot-using-a-vTPM-2..patch
+Patch0318: 0318-powerpc-Drop-Open-Hack-Ware.patch
+Patch0319: 0319-osdep-linux-hostdisk-Modify-sector-by-sysfs-as-disk-.patch
+Patch0320: 0320-mm-Adjust-new-region-size-to-take-management-overhea.patch
+Patch0321: 0321-mm-Preallocate-some-space-when-adding-new-regions.patch
+Patch0322: 0322-mm-Avoid-complex-heap-growth-math-in-hot-path.patch
diff --git a/SPECS/grub2.spec b/SPECS/grub2.spec
index d1f48b7..94dab2b 100644
--- a/SPECS/grub2.spec
+++ b/SPECS/grub2.spec
@@ -9,12 +9,14 @@
 %undefine _missing_build_ids_terminate_build
 %global _configure_gnuconfig_hack 0
 
-%global gnulibversion fixes
+# It's a commit from their gnulib's development tree.  They don't do releases,
+# and it is *awful* to update this.
+%global gnulibversion 9f48fb992a3d7e96610c4ce8be969cff2d61a01b
 
 Name:                 grub2
 Epoch:                1
 Version:              2.06
-Release:              46%{?dist}.3.rocky.0.2
+Release:              46%{?dist}.5.rocky.0.2
 Summary:              Bootloader with support for Linux, Multiboot and more
 License:              GPLv3+
 URL:                  http://www.gnu.org/software/grub/
@@ -32,22 +34,15 @@ Source9:              strtoull_test.c
 Source10:             20-grub.install
 Source11:             grub.patches
 Source12:             sbat.csv.in
-Source13:             701-ca.cer
-Source14:             701-cer.cer
 
 
 %include %{SOURCE1}
 
-%ifarch x86_64 aarch64
+%ifarch x86_64 aarch64 ppc64le
 %define sb_ca		%{_datadir}/pki/sb-certs/secureboot-ca-%{_arch}.cer
 %define sb_cer		%{_datadir}/pki/sb-certs/secureboot-grub2-%{_arch}.cer
 %endif
 
-%ifarch ppc64le
-%define sb_ca %{SOURCE13}
-%define sb_cer %{SOURCE14}
-%endif
-
 %if 0%{?centos}
 
 %ifarch x86_64 aarch64 ppc64le
@@ -58,7 +53,7 @@ Source14:             701-cer.cer
 %define sb_key		rockylinuxsecurebootkey
 %endif
 %ifarch ppc64le
-%define sb_key		redhatsecureboot702
+%define sb_key		rockylinuxsecurebootkey
 %endif
 
 %endif
@@ -439,7 +434,6 @@ mv ${EFI_HOME}/grub.cfg.stb ${EFI_HOME}/grub.cfg
 %{_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
@@ -540,15 +534,23 @@ mv ${EFI_HOME}/grub.cfg.stb ${EFI_HOME}/grub.cfg
 %endif
 
 %changelog
-* Tue Feb 14 2023 Release Engineering <releng@rockylinux.org> - 2.06-46.rocky.0.2
+* Tue Apr 11 2023 Release Engineering <releng@rockylinux.org> - 2.06-46.rocky.0.2
 - Removing redhat old cert sources entries (Sherif Nagy)
 - Preserving rhel8 sbat entry based on shim-review feedback ticket no. 194
 - Adding prod cert
-- Porting to 8.5
+- Porting to 9.1
 - Cleaning up grup.macro extra signing certs and updating rocky test CA and CERT
 - Cleaning up grup.macro extra signing certs
 - Adding Rocky testing CA, CERT and sbat files
 
+* Mon Mar 27 2023 Robbie Harwood <rharwood@redhat.com> - 2.06-46.el9_1.5
+- Sync (actually 2.06-61)
+- Resolves: #2181506
+
+* Tue Feb 07 2023 Robbie Harwood <rharwood@redhat.com> - 2.06-46.el9_1.4
+- Sync with 9.2 (actually 2.06-58)
+- Resolves: #2156419
+
 * Fri Nov 11 2022 Robbie Harwood <rharwood@redhat.com> - 2.06-46.el9_1.3
 - Give up on redhat-sb-certs
 - Resolves: CVE-2022-2601
-- 
GitLab