All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/3] GRUB: Supporting Secure Boot of xen
@ 2024-03-28 15:12 ` Ross Lagerwall via Grub-devel
  0 siblings, 0 replies; 9+ messages in thread
From: Ross Lagerwall @ 2024-03-28 15:12 UTC (permalink / raw)
  To: grub-devel
  Cc: xen-devel, Ross Lagerwall, Daniel Kiper, Daniel Kiper,
	Andrew Cooper, Roger Pau Monné,
	Jan Beulich

This patch series implements support for loading and verifying a signed
xen binary. This would allow the same xen binary to be used for BIOS
boot, UEFI boot, and UEFI boot with Secure Boot verification.
There is an accompanying Xen patch series.

The first patch updates the multiboot2 specification to allow PE
binaries.

Patch 2 implements the spec change.
Patch 3 allows loading multiboot modules without verification since the
multiboot kernel is expected to verify them if needed.

Ross

Changed in v2:

* Use magic numbers to determine whether to load as ELF or PE rather
  than introducing a load type tag.
* Drop the 64 bit entry address extensions to the protocol - this is no
  longer necessary after some Xen build changes.
* Dropped the patch to allow verification after compression since it is
  not critical to make this work.

Diff stat for patch 1 (to be applied to  the "multiboot2" branch):

Ross Lagerwall (1):
  multiboot2: Add support for the PE binary type

 doc/multiboot.texi | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

Diff stat for patch 2 and 3 (to be applied to the "master" branch):

Ross Lagerwall (2):
  multiboot2: Add PE load support
  efi: Allow loading multiboot modules without verification

 grub-core/Makefile.core.def       |   1 +
 grub-core/kern/efi/sb.c           |   1 +
 grub-core/loader/multiboot.c      |   7 +
 grub-core/loader/multiboot_mbi2.c |  11 +-
 grub-core/loader/multiboot_pe.c   | 702 ++++++++++++++++++++++++++++++
 include/grub/efi/pe32.h           |  64 +++
 include/grub/multiboot.h          |   3 +
 include/grub/multiboot2.h         |   9 +
 8 files changed, 797 insertions(+), 1 deletion(-)
 create mode 100644 grub-core/loader/multiboot_pe.c

-- 
2.43.0



^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v2 0/3] GRUB: Supporting Secure Boot of xen
@ 2024-03-28 15:12 ` Ross Lagerwall via Grub-devel
  0 siblings, 0 replies; 9+ messages in thread
From: Ross Lagerwall via Grub-devel @ 2024-03-28 15:12 UTC (permalink / raw)
  To: grub-devel
  Cc: Ross Lagerwall, xen-devel, Daniel Kiper, Daniel Kiper,
	Andrew Cooper, Roger Pau Monné,
	Jan Beulich

This patch series implements support for loading and verifying a signed
xen binary. This would allow the same xen binary to be used for BIOS
boot, UEFI boot, and UEFI boot with Secure Boot verification.
There is an accompanying Xen patch series.

The first patch updates the multiboot2 specification to allow PE
binaries.

Patch 2 implements the spec change.
Patch 3 allows loading multiboot modules without verification since the
multiboot kernel is expected to verify them if needed.

Ross

Changed in v2:

* Use magic numbers to determine whether to load as ELF or PE rather
  than introducing a load type tag.
* Drop the 64 bit entry address extensions to the protocol - this is no
  longer necessary after some Xen build changes.
* Dropped the patch to allow verification after compression since it is
  not critical to make this work.

Diff stat for patch 1 (to be applied to  the "multiboot2" branch):

Ross Lagerwall (1):
  multiboot2: Add support for the PE binary type

 doc/multiboot.texi | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

Diff stat for patch 2 and 3 (to be applied to the "master" branch):

Ross Lagerwall (2):
  multiboot2: Add PE load support
  efi: Allow loading multiboot modules without verification

 grub-core/Makefile.core.def       |   1 +
 grub-core/kern/efi/sb.c           |   1 +
 grub-core/loader/multiboot.c      |   7 +
 grub-core/loader/multiboot_mbi2.c |  11 +-
 grub-core/loader/multiboot_pe.c   | 702 ++++++++++++++++++++++++++++++
 include/grub/efi/pe32.h           |  64 +++
 include/grub/multiboot.h          |   3 +
 include/grub/multiboot2.h         |   9 +
 8 files changed, 797 insertions(+), 1 deletion(-)
 create mode 100644 grub-core/loader/multiboot_pe.c

-- 
2.43.0


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v2 1/3] multiboot2: Add support for the PE binary type
  2024-03-28 15:12 ` Ross Lagerwall via Grub-devel
@ 2024-03-28 15:13   ` Ross Lagerwall via Grub-devel
  -1 siblings, 0 replies; 9+ messages in thread
From: Ross Lagerwall @ 2024-03-28 15:13 UTC (permalink / raw)
  To: grub-devel
  Cc: xen-devel, Ross Lagerwall, Daniel Kiper, Daniel Kiper,
	Andrew Cooper, Roger Pau Monné,
	Jan Beulich

Currently, multiboot2-compatible bootloaders can load ELF binaries and
a.out binaries. The presence of the address header tag determines
how the bootloader tries to interpret the binary (a.out if the address
tag is present else ELF).

In addition to the existing address and ELF load types, specify that
boot loaders may optionally support PE binaries as well. This new type
is a useful addition since PE binaries can be signed and verified (i.e.
used with Secure Boot). Boot loaders can distinguish between ELF and PE
binaries using magic numbers since it is not possible for a binary to be
both an ELF and PE binary at the same time.

Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
---
 doc/multiboot.texi | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/doc/multiboot.texi b/doc/multiboot.texi
index df8a0d056e76..d26c35b54cd4 100644
--- a/doc/multiboot.texi
+++ b/doc/multiboot.texi
@@ -510,12 +510,15 @@ assumes that no bss segment is present.
 @end table
 
 Note: This information does not need to be provided if the kernel image
-is in @sc{elf} format, but it must be provided if the image is in a.out
-format or in some other format. When the address tag is present it must
-be used in order to load the image, regardless of whether an @sc{elf}
-header is also present. Compliant boot loaders must be able to load
-images that are either in @sc{elf} format or contain the address tag
-embedded in the Multiboot2 header.
+is in @sc{elf} or @sc{PE} format, but it must be provided if the image
+is in a.out format or in some other format. When the address tag is
+present it must be used in order to load the image, regardless of
+whether an @sc{elf} or @sc{PE} header is also present. Compliant boot
+loaders must be able to load images that are either in @sc{elf} format
+or contain the address tag embedded in the Multiboot2 header. Compliant
+boot loaders may optionally support loading images in @sc{PE} format.
+When the address tag is not present, the boot loader should use magic
+numbers to identify the type of the image to determine how to load it.
 
 @subsection The entry address tag of Multiboot2 header
 
@@ -565,7 +568,7 @@ start running EFI i386 compatible operating system code.
 
 This tag is taken into account only on EFI i386 platforms
 when Multiboot2 image header contains EFI boot services tag.
-Then entry point specified in ELF header and the entry address
+Then entry point specified in ELF or PE header and the entry address
 tag of Multiboot2 header are ignored.
 
 @subsection EFI amd64 entry address tag of Multiboot2 header
@@ -597,7 +600,7 @@ start running EFI amd64 compatible operating system code.
 
 This tag is taken into account only on EFI amd64 platforms
 when Multiboot2 image header contains EFI boot services tag.
-Then entry point specified in ELF header and the entry address
+Then entry point specified in ELF or PE header and the entry address
 tag of Multiboot2 header are ignored.
 
 @node Console header tags
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 1/3] multiboot2: Add support for the PE binary type
@ 2024-03-28 15:13   ` Ross Lagerwall via Grub-devel
  0 siblings, 0 replies; 9+ messages in thread
From: Ross Lagerwall via Grub-devel @ 2024-03-28 15:13 UTC (permalink / raw)
  To: grub-devel
  Cc: Ross Lagerwall, xen-devel, Daniel Kiper, Daniel Kiper,
	Andrew Cooper, Roger Pau Monné,
	Jan Beulich

Currently, multiboot2-compatible bootloaders can load ELF binaries and
a.out binaries. The presence of the address header tag determines
how the bootloader tries to interpret the binary (a.out if the address
tag is present else ELF).

In addition to the existing address and ELF load types, specify that
boot loaders may optionally support PE binaries as well. This new type
is a useful addition since PE binaries can be signed and verified (i.e.
used with Secure Boot). Boot loaders can distinguish between ELF and PE
binaries using magic numbers since it is not possible for a binary to be
both an ELF and PE binary at the same time.

Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
---
 doc/multiboot.texi | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/doc/multiboot.texi b/doc/multiboot.texi
index df8a0d056e76..d26c35b54cd4 100644
--- a/doc/multiboot.texi
+++ b/doc/multiboot.texi
@@ -510,12 +510,15 @@ assumes that no bss segment is present.
 @end table
 
 Note: This information does not need to be provided if the kernel image
-is in @sc{elf} format, but it must be provided if the image is in a.out
-format or in some other format. When the address tag is present it must
-be used in order to load the image, regardless of whether an @sc{elf}
-header is also present. Compliant boot loaders must be able to load
-images that are either in @sc{elf} format or contain the address tag
-embedded in the Multiboot2 header.
+is in @sc{elf} or @sc{PE} format, but it must be provided if the image
+is in a.out format or in some other format. When the address tag is
+present it must be used in order to load the image, regardless of
+whether an @sc{elf} or @sc{PE} header is also present. Compliant boot
+loaders must be able to load images that are either in @sc{elf} format
+or contain the address tag embedded in the Multiboot2 header. Compliant
+boot loaders may optionally support loading images in @sc{PE} format.
+When the address tag is not present, the boot loader should use magic
+numbers to identify the type of the image to determine how to load it.
 
 @subsection The entry address tag of Multiboot2 header
 
@@ -565,7 +568,7 @@ start running EFI i386 compatible operating system code.
 
 This tag is taken into account only on EFI i386 platforms
 when Multiboot2 image header contains EFI boot services tag.
-Then entry point specified in ELF header and the entry address
+Then entry point specified in ELF or PE header and the entry address
 tag of Multiboot2 header are ignored.
 
 @subsection EFI amd64 entry address tag of Multiboot2 header
@@ -597,7 +600,7 @@ start running EFI amd64 compatible operating system code.
 
 This tag is taken into account only on EFI amd64 platforms
 when Multiboot2 image header contains EFI boot services tag.
-Then entry point specified in ELF header and the entry address
+Then entry point specified in ELF or PE header and the entry address
 tag of Multiboot2 header are ignored.
 
 @node Console header tags
-- 
2.43.0


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 2/3] multiboot2: Add PE load support
  2024-03-28 15:12 ` Ross Lagerwall via Grub-devel
@ 2024-03-28 15:13   ` Ross Lagerwall via Grub-devel
  -1 siblings, 0 replies; 9+ messages in thread
From: Ross Lagerwall @ 2024-03-28 15:13 UTC (permalink / raw)
  To: grub-devel
  Cc: xen-devel, Ross Lagerwall, Daniel Kiper, Daniel Kiper,
	Andrew Cooper, Roger Pau Monné

Add the ability to load multiboot binaries in PE format. This allows the
binaries to be signed and verified.

Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
---
 grub-core/Makefile.core.def       |   1 +
 grub-core/loader/multiboot.c      |   7 +
 grub-core/loader/multiboot_mbi2.c |  11 +-
 grub-core/loader/multiboot_pe.c   | 702 ++++++++++++++++++++++++++++++
 include/grub/efi/pe32.h           |  64 +++
 include/grub/multiboot.h          |   3 +
 include/grub/multiboot2.h         |   9 +
 7 files changed, 796 insertions(+), 1 deletion(-)
 create mode 100644 grub-core/loader/multiboot_pe.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 1571421d7e84..34697ba58171 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1815,6 +1815,7 @@ module = {
 
   common = loader/multiboot.c;
   common = loader/multiboot_mbi2.c;
+  common = loader/multiboot_pe.c;
   enable = x86;
   enable = i386_xen_pvh;
   enable = mips;
diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c
index 94be512c4d0c..8220a74e6f19 100644
--- a/grub-core/loader/multiboot.c
+++ b/grub-core/loader/multiboot.c
@@ -220,6 +220,13 @@ static grub_uint64_t highest_load;
 #include "multiboot_elfxx.c"
 #undef MULTIBOOT_LOAD_ELF32
 
+bool
+GRUB_MULTIBOOT (is_elf) (mbi_load_data_t *mld)
+{
+  return grub_multiboot_is_elf32 (mld->buffer) ||
+         grub_multiboot_is_elf64 (mld->buffer);
+}
+
 /* Load ELF32 or ELF64.  */
 grub_err_t
 GRUB_MULTIBOOT (load_elf) (mbi_load_data_t *mld)
diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c
index 00a48413c013..601f38161e11 100644
--- a/grub-core/loader/multiboot_mbi2.c
+++ b/grub-core/loader/multiboot_mbi2.c
@@ -349,7 +349,16 @@ grub_multiboot2_load (grub_file_t file, const char *filename)
       mld.file = file;
       mld.filename = filename;
       mld.avoid_efi_boot_services = keep_bs;
-      err = grub_multiboot2_load_elf (&mld);
+      if (grub_multiboot2_is_pe (&mld))
+          err = grub_multiboot2_load_pe (&mld);
+      else if (grub_multiboot2_is_elf (&mld))
+          err = grub_multiboot2_load_elf (&mld);
+      else
+        {
+          grub_free (mld.buffer);
+          return grub_error (GRUB_ERR_UNKNOWN_OS,
+                             "Unknown image type and address tag not specified");
+        }
       if (err)
 	{
 	  grub_free (mld.buffer);
diff --git a/grub-core/loader/multiboot_pe.c b/grub-core/loader/multiboot_pe.c
new file mode 100644
index 000000000000..1c194f89b79c
--- /dev/null
+++ b/grub-core/loader/multiboot_pe.c
@@ -0,0 +1,702 @@
+/*
+ * Significant portions of this code are derived from the Fedora GRUB patch
+ * "0007-Add-secureboot-support-on-efi-chainloader.patch" which is in turn
+ * derived from the PE loading code in Shim. The license is reproduced below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Copyright Peter Jones <pjones@redhat.com>
+ *
+ * Modifications:
+ *
+ * Copyright (c) 2024. Cloud Software Group, Inc.
+ */
+
+#include <grub/multiboot2.h>
+#include <grub/i386/relocator.h>
+#include <grub/efi/pe32.h>
+
+static int
+image_is_64_bit (grub_pe_header_t *pe_hdr)
+{
+  /* .Magic is the same offset in all cases */
+  return pe_hdr->pe32plus.optional_header.magic == GRUB_PE32_PE64_MAGIC;
+}
+
+static const grub_uint16_t machine_type __attribute__((__unused__)) =
+#if defined(__x86_64__)
+  GRUB_PE32_MACHINE_X86_64;
+#elif defined(__aarch64__)
+  GRUB_PE32_MACHINE_ARM64;
+#elif defined(__arm__)
+  GRUB_PE32_MACHINE_ARMTHUMB_MIXED;
+#elif defined(__i386__) || defined(__i486__) || defined(__i686__)
+  GRUB_PE32_MACHINE_I386;
+#elif defined(__ia64__)
+  GRUB_PE32_MACHINE_IA64;
+#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
+
+static int
+image_is_loadable(grub_pe_header_t *pe_hdr)
+{
+  /*
+   * Check the machine type matches the binary and that we recognize
+   * the magic number.
+   */
+    return (pe_hdr->pe32.coff_header.machine == machine_type ||
+            (pe_hdr->pe32.coff_header.machine == GRUB_PE32_MACHINE_X86_64 &&
+             machine_type == GRUB_PE32_MACHINE_I386)) &&
+           (pe_hdr->pe32plus.optional_header.magic == GRUB_PE32_PE32_MAGIC ||
+            pe_hdr->pe32plus.optional_header.magic == GRUB_PE32_PE64_MAGIC);
+}
+
+/*
+ * Read the binary header and grab appropriate information from it
+ */
+static grub_err_t
+read_header(void *data, unsigned int datasize,
+            pe_coff_loader_image_context_t *context)
+{
+  grub_pe32_msdos_header_t *dos_hdr = data;
+  grub_pe_header_t *pe_hdr = data;
+  unsigned long header_without_data_dir, section_header_offset, opt_header_size;
+  unsigned long file_alignment = 0;
+
+  if (datasize < sizeof (pe_hdr->pe32))
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid image");
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+
+  if (dos_hdr->e_magic == GRUB_PE32_MAGIC)
+    pe_hdr = (grub_pe_header_t *)((char *)data + dos_hdr->e_lfanew);
+
+  if (!image_is_loadable(pe_hdr))
+    {
+      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "Platform does not support this image");
+      return GRUB_ERR_NOT_IMPLEMENTED_YET;
+    }
+
+  if (image_is_64_bit(pe_hdr))
+    {
+      context->number_of_rva_and_sizes = pe_hdr->pe32plus.optional_header.num_data_directories;
+      context->size_of_headers = pe_hdr->pe32plus.optional_header.header_size;
+      context->image_size = pe_hdr->pe32plus.optional_header.image_size;
+      context->section_alignment = pe_hdr->pe32plus.optional_header.section_alignment;
+      file_alignment = pe_hdr->pe32plus.optional_header.file_alignment;
+      opt_header_size = sizeof(struct grub_pe64_optional_header);
+    }
+  else
+    {
+      context->number_of_rva_and_sizes = pe_hdr->pe32.optional_header.num_data_directories;
+      context->size_of_headers = pe_hdr->pe32.optional_header.header_size;
+      context->image_size = (grub_uint64_t)pe_hdr->pe32.optional_header.image_size;
+      context->section_alignment = pe_hdr->pe32.optional_header.section_alignment;
+      file_alignment = pe_hdr->pe32.optional_header.file_alignment;
+      opt_header_size = sizeof(struct grub_pe32_optional_header);
+    }
+
+  if (file_alignment % 2 != 0)
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "File Alignment is invalid (%ld)", file_alignment);
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+  if (file_alignment == 0)
+    file_alignment = 0x200;
+  if (context->section_alignment == 0)
+    context->section_alignment = GRUB_EFI_PAGE_SIZE;
+  if (context->section_alignment < file_alignment)
+    context->section_alignment = file_alignment;
+
+  context->number_of_sections = pe_hdr->pe32.coff_header.num_sections;
+
+  if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < context->number_of_rva_and_sizes)
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "Image header too small");
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+
+  header_without_data_dir = opt_header_size
+                  - sizeof (struct grub_pe32_data_directory) * EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES;
+  if (((grub_uint32_t)pe_hdr->pe32.coff_header.optional_header_size - header_without_data_dir) !=
+                  context->number_of_rva_and_sizes * sizeof (struct grub_pe32_data_directory))
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "Image header overflows data directory");
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+
+  section_header_offset = dos_hdr->e_lfanew
+                          + sizeof (grub_uint32_t)
+                          + sizeof (struct grub_pe32_coff_header)
+                          + pe_hdr->pe32.coff_header.optional_header_size;
+  if (((grub_uint32_t)context->image_size - section_header_offset) / sizeof(struct grub_pe32_section_table)
+                  <= context->number_of_sections)
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "Image sections overflow image size");
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+
+  if ((context->size_of_headers - section_header_offset) / sizeof(struct grub_pe32_section_table)
+                  < (grub_uint32_t)context->number_of_sections)
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "Image sections overflow section headers");
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+
+  if ((((grub_uint8_t *)pe_hdr - (grub_uint8_t *)data) + sizeof(grub_pe_header_t)) > datasize)
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid image");
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+
+  if (pe_hdr->pe32.signature[0] != 'P' ||
+              pe_hdr->pe32.signature[1] != 'E' ||
+              pe_hdr->pe32.signature[2] != 0x00 ||
+              pe_hdr->pe32.signature[3] != 0x00)
+    {
+      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "Unsupported image type");
+      return GRUB_ERR_NOT_IMPLEMENTED_YET;
+    }
+
+  context->pe_hdr = pe_hdr;
+
+  if (image_is_64_bit(pe_hdr))
+    {
+      context->image_address = pe_hdr->pe32plus.optional_header.image_base;
+      context->entry_point = pe_hdr->pe32plus.optional_header.entry_addr;
+      context->reloc_dir = &pe_hdr->pe32plus.optional_header.base_relocation_table;
+      context->sec_dir = &pe_hdr->pe32plus.optional_header.certificate_table;
+    }
+  else
+    {
+      context->image_address = pe_hdr->pe32.optional_header.image_base;
+      context->entry_point = pe_hdr->pe32.optional_header.entry_addr;
+      context->reloc_dir = &pe_hdr->pe32.optional_header.base_relocation_table;
+      context->sec_dir = &pe_hdr->pe32.optional_header.certificate_table;
+    }
+
+  context->first_section = (struct grub_pe32_section_table *)((char *)pe_hdr +
+          pe_hdr->pe32.coff_header.optional_header_size +
+          sizeof(grub_uint32_t) + sizeof(struct grub_pe32_coff_header));
+
+  if (context->image_size < context->size_of_headers)
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid image");
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+
+  if ((unsigned long)((grub_uint8_t *)context->sec_dir - (grub_uint8_t *)data) >
+      (datasize - sizeof(struct grub_pe32_data_directory)))
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid image");
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+  return GRUB_ERR_NONE;
+}
+
+static grub_phys_addr_t
+image_address (grub_phys_addr_t image, grub_uint64_t sz, grub_uint64_t addr)
+{
+  if (addr > sz)
+    return 0;
+
+  return image + addr;
+}
+
+static grub_err_t
+relocate_coff (pe_coff_loader_image_context_t *context,
+               struct grub_pe32_section_table *section,
+               grub_phys_addr_t phys_addr, grub_uint8_t *virt_addr,
+               mbi_load_data_t *mld)
+{
+  struct grub_pe32_data_directory *reloc_base, *reloc_base_end;
+  grub_uint8_t *reloc_buffer;
+  grub_uint64_t adjust;
+  struct grub_pe32_fixup_block *reloc, *reloc_end;
+  grub_uint8_t *fixup, *fixup_base;
+  grub_uint16_t *fixup_16;
+  grub_uint32_t *fixup_32;
+  grub_uint64_t *fixup_64;
+  int n = 0;
+  grub_err_t ret = GRUB_ERR_NONE;
+
+  if (image_is_64_bit (context->pe_hdr))
+    context->pe_hdr->pe32plus.optional_header.image_base =
+      (grub_uint64_t)(unsigned long)phys_addr;
+  else
+    context->pe_hdr->pe32.optional_header.image_base =
+      (grub_uint32_t)(unsigned long)phys_addr;
+
+  /* Alright, so here's how this works:
+   *
+   * context->reloc_dir gives us two things:
+   * - the VA the table of base relocation blocks are (maybe) to be
+   *   mapped at (reloc_dir->rva)
+   * - the virtual size (reloc_dir->size)
+   *
+   * The .reloc section (section here) gives us some other things:
+   * - the name! kind of. (section->name)
+   * - the virtual size (section->virtual_size), which should be the same
+   *   as RelocDir->Size
+   * - the virtual address (section->virtual_address)
+   * - the file section size (section->raw_data_size), which is
+   *   a multiple of optional_header->file_alignment.  Only useful for image
+   *   validation, not really useful for iteration bounds.
+   * - the file address (section->raw_data_offset)
+   * - a bunch of stuff we don't use that's 0 in our binaries usually
+   * - Flags (section->characteristics)
+   *
+   * and then the thing that's actually at the file address is an array
+   * of struct grub_pe32_fixup_block structs with some values packed behind
+   * them.  The block_size field of this structure includes the
+   * structure itself, and adding it to that structure's address will
+   * yield the next entry in the array.
+   */
+
+  reloc_buffer = grub_malloc (section->virtual_size);
+  if (!reloc_buffer)
+      return grub_errno;
+
+  if (grub_file_seek (mld->file, section->raw_data_offset) == (grub_off_t) -1)
+    {
+      ret = grub_errno;
+      goto out;
+    }
+
+  if (grub_file_read (mld->file, reloc_buffer, section->virtual_size)
+      != (grub_ssize_t) section->virtual_size)
+    {
+      if (!grub_errno)
+        {
+          grub_error (GRUB_ERR_FILE_READ_ERROR, "premature end of file %s",
+                      mld->filename);
+          ret = GRUB_ERR_FILE_READ_ERROR;
+        }
+      else
+          ret = grub_errno;
+      goto out;
+    }
+
+  reloc_base = (struct grub_pe32_data_directory *)reloc_buffer;
+  reloc_base_end = (struct grub_pe32_data_directory *)(reloc_buffer + section->virtual_size);
+
+  grub_dprintf ("multiboot_loader", "relocate_coff(): reloc_base %p reloc_base_end %p\n",
+                reloc_base, reloc_base_end);
+
+  if (!reloc_base && !reloc_base_end)
+    return GRUB_ERR_NONE;
+
+  if (!reloc_base || !reloc_base_end)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc table overflows binary");
+      return GRUB_ERR_BAD_ARGUMENT;
+    }
+
+  adjust = (grub_uint64_t)(grub_efi_uintn_t)phys_addr - context->image_address;
+  if (adjust == 0)
+    return GRUB_ERR_NONE;
+
+  while (reloc_base < reloc_base_end)
+    {
+      grub_uint16_t *entry;
+      reloc = (struct grub_pe32_fixup_block *)((char*)reloc_base);
+
+      if ((reloc_base->size == 0) ||
+          (reloc_base->size > context->reloc_dir->size))
+        {
+          grub_error (GRUB_ERR_BAD_ARGUMENT,
+                      "Reloc %d block size %d is invalid\n", n,
+                      reloc_base->size);
+          ret = GRUB_ERR_BAD_ARGUMENT;
+          goto out;
+        }
+
+      entry = &reloc->entries[0];
+      reloc_end = (struct grub_pe32_fixup_block *)
+        ((char *)reloc_base + reloc_base->size);
+
+      if ((grub_uint8_t *)reloc_end < reloc_buffer ||
+          (grub_uint8_t *)reloc_end > (reloc_buffer + section->virtual_size))
+        {
+          grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc entry %d overflows binary",
+                      n);
+          return GRUB_ERR_BAD_ARGUMENT;
+        }
+
+      fixup_base = virt_addr + reloc_base->rva;
+      if (!fixup_base)
+        {
+          grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc %d Invalid fixupbase", n);
+          ret = GRUB_ERR_BAD_ARGUMENT;
+          goto out;
+        }
+
+      while ((void *)entry < (void *)reloc_end)
+        {
+          fixup = fixup_base + (*entry & 0xFFF);
+          switch ((*entry) >> 12)
+            {
+              case GRUB_PE32_REL_BASED_ABSOLUTE:
+                break;
+              case GRUB_PE32_REL_BASED_HIGH:
+                fixup_16 = (grub_uint16_t *)fixup;
+                *fixup_16 = (grub_uint16_t)
+                  (*fixup_16 + ((grub_uint16_t)((grub_uint32_t)adjust >> 16)));
+                break;
+              case GRUB_PE32_REL_BASED_LOW:
+                fixup_16 = (grub_uint16_t *)fixup;
+                *fixup_16 = (grub_uint16_t) (*fixup_16 + (grub_uint16_t)adjust);
+                break;
+              case GRUB_PE32_REL_BASED_HIGHLOW:
+                fixup_32 = (grub_uint32_t *)fixup;
+                *fixup_32 = *fixup_32 + (grub_uint32_t)adjust;
+                break;
+              case GRUB_PE32_REL_BASED_DIR64:
+                fixup_64 = (grub_uint64_t *)fixup;
+                *fixup_64 = *fixup_64 + (grub_uint64_t)adjust;
+                break;
+              default:
+                grub_error (GRUB_ERR_BAD_ARGUMENT,
+                            "Reloc %d unknown relocation type %d",
+                            n, (*entry) >> 12);
+                ret = GRUB_ERR_BAD_ARGUMENT;
+                goto out;
+            }
+          entry += 1;
+        }
+      reloc_base = (struct grub_pe32_data_directory *)reloc_end;
+      n++;
+    }
+
+out:
+  grub_free(reloc_buffer);
+
+  return ret;
+}
+
+grub_err_t
+grub_multiboot2_load_pe (mbi_load_data_t *mld)
+{
+  int i;
+  grub_uint8_t *virt_addr;
+  grub_phys_addr_t phys_addr, reloc_base, reloc_base_end, base, end;
+  struct grub_pe32_section_table *reloc_section = NULL, fake_reloc_section;
+  struct grub_pe32_section_table *section;
+  grub_relocator_chunk_t ch;
+  pe_coff_loader_image_context_t context;
+  grub_err_t ret = GRUB_ERR_NONE;
+  grub_uint32_t section_alignment;
+  int found_entry_point = 0;
+
+  ret = read_header(mld->buffer, MULTIBOOT_SEARCH, &context);
+  if (ret)
+      return ret;
+
+  /*
+   * The spec says, uselessly, of SectionAlignment:
+   * =====
+   * The alignment (in bytes) of sections when they are loaded into
+   * memory. It must be greater than or equal to FileAlignment. The
+   * default is the page size for the architecture.
+   * =====
+   * Which doesn't tell you whose responsibility it is to enforce the
+   * "default", or when.  It implies that the value in the field must
+   * be > FileAlignment (also poorly defined), but it appears visual
+   * studio will happily write 512 for FileAlignment (its default) and
+   * 0 for SectionAlignment, intending to imply PAGE_SIZE.
+   *
+   * We only support one page size, so if it's zero, nerf it to 4096.
+   */
+  section_alignment = context.section_alignment;
+  if (section_alignment == 0)
+    section_alignment = 4096;
+
+  section_alignment = grub_max(section_alignment, mld->align);
+
+  ret = grub_relocator_alloc_chunk_align_safe (grub_multiboot2_relocator, &ch,
+                                               mld->min_addr, mld->max_addr,
+                                               context.image_size, section_alignment,
+                                               mld->preference, mld->avoid_efi_boot_services);
+
+  if (ret)
+    {
+      grub_dprintf ("multiboot_loader", "Cannot allocate memory for OS image\n");
+      return ret;
+    }
+
+  virt_addr = get_virtual_current_address (ch);
+  phys_addr = get_physical_target_address (ch);
+
+  if (grub_file_seek (mld->file, 0) == (grub_off_t) -1) {
+      ret = grub_errno;
+      goto out;
+  }
+
+  if (grub_file_read (mld->file, virt_addr, context.size_of_headers)
+      != (grub_ssize_t) context.size_of_headers)
+    {
+      if (!grub_errno)
+        {
+          grub_error (GRUB_ERR_FILE_READ_ERROR, "premature end of file %s",
+                      mld->filename);
+          ret = GRUB_ERR_FILE_READ_ERROR;
+        }
+      else
+          ret = grub_errno;
+      goto out;
+    }
+
+  mld->load_base_addr = phys_addr;
+  mld->link_base_addr = context.image_address;
+
+  grub_dprintf ("multiboot_loader", "load_base_addr: 0x%08x link_base_addr 0x%08x\n",
+                mld->load_base_addr, mld->link_base_addr);
+
+  grub_multiboot2_payload_eip = context.entry_point;
+  grub_dprintf ("multiboot_loader", "entry_point: 0x%08x\n", grub_multiboot2_payload_eip);
+  if (!grub_multiboot2_payload_eip)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid entry point");
+      ret = GRUB_ERR_BAD_ARGUMENT;
+      goto out;
+    }
+
+  grub_dprintf ("multiboot_loader", "reloc_dir: %p reloc_size: 0x%08x\n",
+                (void *)(unsigned long)context.reloc_dir->rva,
+                context.reloc_dir->size);
+  reloc_base = image_address (phys_addr, context.image_size,
+                              context.reloc_dir->rva);
+  /* RelocBaseEnd here is the address of the last byte of the table */
+  reloc_base_end = image_address (phys_addr, context.image_size,
+                                  context.reloc_dir->rva
+                                  + context.reloc_dir->size - 1);
+  grub_dprintf ("multiboot_loader", "reloc_base: 0x%016lx reloc_base_end: 0x%016lx\n",
+                reloc_base, reloc_base_end);
+
+  section = context.first_section;
+  for (i = 0; i < context.number_of_sections; i++, section++)
+    {
+      char name[9];
+
+      base = image_address (phys_addr, context.image_size,
+                            section->virtual_address);
+      end = image_address (phys_addr, context.image_size,
+                           section->virtual_address + section->virtual_size -1);
+
+      grub_strncpy(name, section->name, 9);
+      name[8] = '\0';
+      grub_dprintf ("multiboot_loader", "Section %d \"%s\" at 0x%016lx..0x%016lx\n", i,
+                   name, base, end);
+
+      if (end < base)
+        {
+          grub_dprintf ("multiboot_loader", " base is 0x%016lx but end is 0x%016lx... bad.\n",
+                       base, end);
+          grub_error (GRUB_ERR_BAD_ARGUMENT,
+                      "Image has invalid negative size");
+          ret = GRUB_ERR_BAD_ARGUMENT;
+          goto out;
+        }
+
+      if (section->virtual_address <= context.entry_point &&
+          (section->virtual_address + section->raw_data_size - 1)
+          > context.entry_point)
+        {
+          found_entry_point++;
+          grub_dprintf ("multiboot_loader", " section contains entry point\n");
+        }
+
+      /* We do want to process .reloc, but it's often marked
+       * discardable, so we don't want to memcpy it. */
+      if (grub_memcmp (section->name, ".reloc\0\0", 8) == 0)
+        {
+          if (reloc_section)
+            {
+              grub_error (GRUB_ERR_BAD_ARGUMENT,
+                          "Image has multiple relocation sections");
+              ret = GRUB_ERR_BAD_ARGUMENT;
+              goto out;
+            }
+
+          /* If it has nonzero sizes, and our bounds check
+           * made sense, and the VA and size match RelocDir's
+           * versions, then we believe in this section table. */
+          if (section->raw_data_size && section->virtual_size &&
+              base && end && reloc_base == base)
+            {
+              if (reloc_base_end == end)
+                {
+                  grub_dprintf ("multiboot_loader", " section is relocation section\n");
+                  reloc_section = section;
+                }
+              else if (reloc_base_end && reloc_base_end < end)
+                {
+                  /* Bogus virtual size in the reloc section -- RelocDir
+                   * reported a smaller Base Relocation Directory. Decrease
+                   * the section's virtual size so that it equal RelocDir's
+                   * idea, but only for the purposes of relocate_coff(). */
+                  grub_dprintf ("multiboot_loader",
+                                " section is (overlong) relocation section\n");
+                  grub_memcpy (&fake_reloc_section, section, sizeof *section);
+                  fake_reloc_section.virtual_size -= (end - reloc_base_end);
+                  reloc_section = &fake_reloc_section;
+                }
+            }
+
+          if (!reloc_section)
+            {
+              grub_dprintf ("multiboot_loader", " section is not reloc section?\n");
+              grub_dprintf ("multiboot_loader", " rds: 0x%08x, vs: %08x\n",
+                            section->raw_data_size, section->virtual_size);
+              grub_dprintf ("multiboot_loader", " base: 0x%016lx end: 0x%016lx\n", base, end);
+              grub_dprintf ("multiboot_loader", " reloc_base: 0x%016lx reloc_base_end: 0x%016lx\n",
+                            reloc_base, reloc_base_end);
+            }
+        }
+
+      grub_dprintf ("multiboot_loader", " Section characteristics are %08x\n",
+                   section->characteristics);
+      grub_dprintf ("multiboot_loader", " Section virtual size: %08x\n",
+                   section->virtual_size);
+      grub_dprintf ("multiboot_loader", " Section raw_data size: %08x\n",
+                   section->raw_data_size);
+      if (section->characteristics & GRUB_PE32_SCN_MEM_DISCARDABLE)
+        {
+          grub_dprintf ("multiboot_loader", " Discarding section\n");
+          continue;
+        }
+
+      if (!base || !end)
+        {
+          grub_dprintf ("multiboot_loader", " section is invalid\n");
+          grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid section size");
+          ret = GRUB_ERR_BAD_ARGUMENT;
+          goto out;
+        }
+
+      if (section->characteristics & GRUB_PE32_SCN_CNT_UNINITIALIZED_DATA)
+        {
+          if (section->raw_data_size != 0)
+            grub_dprintf ("multiboot_loader", " UNINITIALIZED_DATA section has data?\n");
+        }
+      else if (section->virtual_address < context.size_of_headers ||
+               section->raw_data_offset < context.size_of_headers)
+        {
+          grub_error (GRUB_ERR_BAD_ARGUMENT,
+                      "Section %d is inside image headers", i);
+          ret = GRUB_ERR_BAD_ARGUMENT;
+          goto out;
+        }
+
+      if (section->raw_data_size > 0)
+        {
+          grub_dprintf ("multiboot_loader", " copying 0x%08x bytes to 0x%016lx\n",
+                        section->raw_data_size, base);
+
+          if (grub_file_seek (mld->file, section->raw_data_offset) == (grub_off_t) -1)
+            {
+              ret = grub_errno;
+              goto out;
+            }
+
+          if (grub_file_read (mld->file, virt_addr + (base - phys_addr), section->raw_data_size)
+              != (grub_ssize_t) section->raw_data_size)
+            {
+              if (!grub_errno)
+                {
+                  grub_error (GRUB_ERR_FILE_READ_ERROR, "premature end of file %s",
+                              mld->filename);
+                  ret = GRUB_ERR_FILE_READ_ERROR;
+                }
+              else
+                  ret = grub_errno;
+              goto out;
+            }
+        }
+
+      if (section->raw_data_size < section->virtual_size)
+        {
+          grub_dprintf ("multiboot_loader", " padding with 0x%08x bytes at 0x%016lx\n",
+                        section->virtual_size - section->raw_data_size,
+                        base + section->raw_data_size);
+          grub_memset (virt_addr + (base - phys_addr) + section->raw_data_size, 0,
+                       section->virtual_size - section->raw_data_size);
+        }
+
+      grub_dprintf ("multiboot_loader", " finished section %s\n", name);
+    }
+
+  /* 5 == EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC */
+  if (context.number_of_rva_and_sizes <= 5)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "image has no relocation entry\n");
+      ret = GRUB_ERR_BAD_ARGUMENT;
+      goto out;
+    }
+
+  if (context.reloc_dir->size && reloc_section)
+    {
+      /* run the relocation fixups */
+      ret = relocate_coff (&context, reloc_section, phys_addr, virt_addr, mld);
+
+      if (ret)
+        {
+          grub_error (GRUB_ERR_BAD_ARGUMENT, "relocation failed");
+          goto out;
+        }
+    }
+
+  if (!found_entry_point)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "entry point is not within sections");
+      ret = GRUB_ERR_BAD_ARGUMENT;
+      goto out;
+    }
+  if (found_entry_point > 1)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "%d sections contain entry point",
+                  found_entry_point);
+      ret = GRUB_ERR_BAD_ARGUMENT;
+      goto out;
+    }
+
+out:
+  return ret;
+}
+
+bool
+grub_multiboot2_is_pe (mbi_load_data_t *mld)
+{
+  grub_pe32_msdos_header_t *dos_hdr = mld->buffer;
+
+  return dos_hdr->e_magic == GRUB_PE32_MAGIC;
+}
diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h
index 4e6e9d254bd3..2c8f7c3b85a5 100644
--- a/include/grub/efi/pe32.h
+++ b/include/grub/efi/pe32.h
@@ -20,6 +20,7 @@
 #define GRUB_EFI_PE32_HEADER	1
 
 #include <grub/types.h>
+#include <grub/efi/api.h>
 #include <grub/efi/memory.h>
 
 /* The MSDOS compatibility stub. This was copied from the output of
@@ -46,6 +47,28 @@
 
 #define GRUB_PE32_MSDOS_STUB_SIZE	0x80
 
+typedef struct {
+  grub_uint16_t  e_magic;    ///< Magic number.
+  grub_uint16_t  e_cblp;     ///< Bytes on last page of file.
+  grub_uint16_t  e_cp;       ///< Pages in file.
+  grub_uint16_t  e_crlc;     ///< Relocations.
+  grub_uint16_t  e_cparhdr;  ///< Size of header in paragraphs.
+  grub_uint16_t  e_minalloc; ///< Minimum extra paragraphs needed.
+  grub_uint16_t  e_maxalloc; ///< Maximum extra paragraphs needed.
+  grub_uint16_t  e_ss;       ///< Initial (relative) SS value.
+  grub_uint16_t  e_sp;       ///< Initial SP value.
+  grub_uint16_t  e_csum;     ///< Checksum.
+  grub_uint16_t  e_ip;       ///< Initial IP value.
+  grub_uint16_t  e_cs;       ///< Initial (relative) CS value.
+  grub_uint16_t  e_lfarlc;   ///< File address of relocation table.
+  grub_uint16_t  e_ovno;     ///< Overlay number.
+  grub_uint16_t  e_res[4];   ///< Reserved words.
+  grub_uint16_t  e_oemid;    ///< OEM identifier (for e_oeminfo).
+  grub_uint16_t  e_oeminfo;  ///< OEM information; e_oemid specific.
+  grub_uint16_t  e_res2[10]; ///< Reserved words.
+  grub_uint32_t  e_lfanew;   ///< File address of new exe header.
+} grub_pe32_msdos_header_t;
+
 #define GRUB_PE32_MAGIC			0x5a4d
 
 struct grub_msdos_image_header
@@ -249,6 +272,7 @@ struct grub_pe32_section_table
 
 #define GRUB_PE32_SCN_CNT_CODE			0x00000020
 #define GRUB_PE32_SCN_CNT_INITIALIZED_DATA	0x00000040
+#define GRUB_PE32_SCN_CNT_UNINITIALIZED_DATA	0x00000080
 #define GRUB_PE32_SCN_MEM_DISCARDABLE		0x02000000
 #define GRUB_PE32_SCN_MEM_EXECUTE		0x20000000
 #define GRUB_PE32_SCN_MEM_READ			0x40000000
@@ -349,4 +373,44 @@ struct grub_pe32_reloc
 #define GRUB_PE32_REL_I386_DIR32	0x6
 #define GRUB_PE32_REL_I386_REL32	0x14
 
+struct grub_pe32_header_32
+{
+  char signature[GRUB_PE32_SIGNATURE_SIZE];
+  struct grub_pe32_coff_header coff_header;
+  struct grub_pe32_optional_header optional_header;
+};
+
+struct grub_pe32_header_64
+{
+  char signature[GRUB_PE32_SIGNATURE_SIZE];
+  struct grub_pe32_coff_header coff_header;
+  struct grub_pe64_optional_header optional_header;
+};
+
+typedef union
+{
+  struct grub_pe32_header_32 pe32;
+  struct grub_pe32_header_64 pe32plus;
+} grub_pe_header_t;
+
+struct pe_coff_loader_image_context
+{
+  grub_efi_uint64_t image_address;
+  grub_efi_uint64_t image_size;
+  grub_efi_uint64_t entry_point;
+  grub_efi_uintn_t size_of_headers;
+  grub_efi_uint16_t image_type;
+  grub_efi_uint16_t number_of_sections;
+  grub_efi_uint32_t section_alignment;
+  struct grub_pe32_section_table *first_section;
+  struct grub_pe32_data_directory *reloc_dir;
+  struct grub_pe32_data_directory *sec_dir;
+  grub_efi_uint64_t number_of_rva_and_sizes;
+  grub_pe_header_t *pe_hdr;
+};
+
+typedef struct pe_coff_loader_image_context pe_coff_loader_image_context_t;
+
+#define EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES 16
+
 #endif /* ! GRUB_EFI_PE32_HEADER */
diff --git a/include/grub/multiboot.h b/include/grub/multiboot.h
index d8847f7531d3..c6af5c71b4e8 100644
--- a/include/grub/multiboot.h
+++ b/include/grub/multiboot.h
@@ -100,6 +100,9 @@ struct mbi_load_data
 };
 typedef struct mbi_load_data mbi_load_data_t;
 
+bool
+grub_multiboot_is_elf (mbi_load_data_t *mld);
+
 /* Load ELF32 or ELF64.  */
 grub_err_t
 grub_multiboot_load_elf (mbi_load_data_t *mld);
diff --git a/include/grub/multiboot2.h b/include/grub/multiboot2.h
index b90aa6989674..6169cb4472a7 100644
--- a/include/grub/multiboot2.h
+++ b/include/grub/multiboot2.h
@@ -92,10 +92,19 @@ struct mbi_load_data
 };
 typedef struct mbi_load_data mbi_load_data_t;
 
+bool
+grub_multiboot2_is_elf (mbi_load_data_t *mld);
+
 /* Load ELF32 or ELF64.  */
 grub_err_t
 grub_multiboot2_load_elf (mbi_load_data_t *mld);
 
+grub_err_t
+grub_multiboot2_load_pe (mbi_load_data_t *mld);
+
+bool
+grub_multiboot2_is_pe (mbi_load_data_t *mld);
+
 extern grub_size_t grub_multiboot2_pure_size;
 extern grub_size_t grub_multiboot2_alloc_mbi;
 extern grub_uint32_t grub_multiboot2_payload_eip;
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 2/3] multiboot2: Add PE load support
@ 2024-03-28 15:13   ` Ross Lagerwall via Grub-devel
  0 siblings, 0 replies; 9+ messages in thread
From: Ross Lagerwall via Grub-devel @ 2024-03-28 15:13 UTC (permalink / raw)
  To: grub-devel
  Cc: Ross Lagerwall, xen-devel, Daniel Kiper, Daniel Kiper,
	Andrew Cooper, Roger Pau Monné

Add the ability to load multiboot binaries in PE format. This allows the
binaries to be signed and verified.

Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
---
 grub-core/Makefile.core.def       |   1 +
 grub-core/loader/multiboot.c      |   7 +
 grub-core/loader/multiboot_mbi2.c |  11 +-
 grub-core/loader/multiboot_pe.c   | 702 ++++++++++++++++++++++++++++++
 include/grub/efi/pe32.h           |  64 +++
 include/grub/multiboot.h          |   3 +
 include/grub/multiboot2.h         |   9 +
 7 files changed, 796 insertions(+), 1 deletion(-)
 create mode 100644 grub-core/loader/multiboot_pe.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 1571421d7e84..34697ba58171 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1815,6 +1815,7 @@ module = {
 
   common = loader/multiboot.c;
   common = loader/multiboot_mbi2.c;
+  common = loader/multiboot_pe.c;
   enable = x86;
   enable = i386_xen_pvh;
   enable = mips;
diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c
index 94be512c4d0c..8220a74e6f19 100644
--- a/grub-core/loader/multiboot.c
+++ b/grub-core/loader/multiboot.c
@@ -220,6 +220,13 @@ static grub_uint64_t highest_load;
 #include "multiboot_elfxx.c"
 #undef MULTIBOOT_LOAD_ELF32
 
+bool
+GRUB_MULTIBOOT (is_elf) (mbi_load_data_t *mld)
+{
+  return grub_multiboot_is_elf32 (mld->buffer) ||
+         grub_multiboot_is_elf64 (mld->buffer);
+}
+
 /* Load ELF32 or ELF64.  */
 grub_err_t
 GRUB_MULTIBOOT (load_elf) (mbi_load_data_t *mld)
diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c
index 00a48413c013..601f38161e11 100644
--- a/grub-core/loader/multiboot_mbi2.c
+++ b/grub-core/loader/multiboot_mbi2.c
@@ -349,7 +349,16 @@ grub_multiboot2_load (grub_file_t file, const char *filename)
       mld.file = file;
       mld.filename = filename;
       mld.avoid_efi_boot_services = keep_bs;
-      err = grub_multiboot2_load_elf (&mld);
+      if (grub_multiboot2_is_pe (&mld))
+          err = grub_multiboot2_load_pe (&mld);
+      else if (grub_multiboot2_is_elf (&mld))
+          err = grub_multiboot2_load_elf (&mld);
+      else
+        {
+          grub_free (mld.buffer);
+          return grub_error (GRUB_ERR_UNKNOWN_OS,
+                             "Unknown image type and address tag not specified");
+        }
       if (err)
 	{
 	  grub_free (mld.buffer);
diff --git a/grub-core/loader/multiboot_pe.c b/grub-core/loader/multiboot_pe.c
new file mode 100644
index 000000000000..1c194f89b79c
--- /dev/null
+++ b/grub-core/loader/multiboot_pe.c
@@ -0,0 +1,702 @@
+/*
+ * Significant portions of this code are derived from the Fedora GRUB patch
+ * "0007-Add-secureboot-support-on-efi-chainloader.patch" which is in turn
+ * derived from the PE loading code in Shim. The license is reproduced below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Copyright Peter Jones <pjones@redhat.com>
+ *
+ * Modifications:
+ *
+ * Copyright (c) 2024. Cloud Software Group, Inc.
+ */
+
+#include <grub/multiboot2.h>
+#include <grub/i386/relocator.h>
+#include <grub/efi/pe32.h>
+
+static int
+image_is_64_bit (grub_pe_header_t *pe_hdr)
+{
+  /* .Magic is the same offset in all cases */
+  return pe_hdr->pe32plus.optional_header.magic == GRUB_PE32_PE64_MAGIC;
+}
+
+static const grub_uint16_t machine_type __attribute__((__unused__)) =
+#if defined(__x86_64__)
+  GRUB_PE32_MACHINE_X86_64;
+#elif defined(__aarch64__)
+  GRUB_PE32_MACHINE_ARM64;
+#elif defined(__arm__)
+  GRUB_PE32_MACHINE_ARMTHUMB_MIXED;
+#elif defined(__i386__) || defined(__i486__) || defined(__i686__)
+  GRUB_PE32_MACHINE_I386;
+#elif defined(__ia64__)
+  GRUB_PE32_MACHINE_IA64;
+#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
+
+static int
+image_is_loadable(grub_pe_header_t *pe_hdr)
+{
+  /*
+   * Check the machine type matches the binary and that we recognize
+   * the magic number.
+   */
+    return (pe_hdr->pe32.coff_header.machine == machine_type ||
+            (pe_hdr->pe32.coff_header.machine == GRUB_PE32_MACHINE_X86_64 &&
+             machine_type == GRUB_PE32_MACHINE_I386)) &&
+           (pe_hdr->pe32plus.optional_header.magic == GRUB_PE32_PE32_MAGIC ||
+            pe_hdr->pe32plus.optional_header.magic == GRUB_PE32_PE64_MAGIC);
+}
+
+/*
+ * Read the binary header and grab appropriate information from it
+ */
+static grub_err_t
+read_header(void *data, unsigned int datasize,
+            pe_coff_loader_image_context_t *context)
+{
+  grub_pe32_msdos_header_t *dos_hdr = data;
+  grub_pe_header_t *pe_hdr = data;
+  unsigned long header_without_data_dir, section_header_offset, opt_header_size;
+  unsigned long file_alignment = 0;
+
+  if (datasize < sizeof (pe_hdr->pe32))
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid image");
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+
+  if (dos_hdr->e_magic == GRUB_PE32_MAGIC)
+    pe_hdr = (grub_pe_header_t *)((char *)data + dos_hdr->e_lfanew);
+
+  if (!image_is_loadable(pe_hdr))
+    {
+      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "Platform does not support this image");
+      return GRUB_ERR_NOT_IMPLEMENTED_YET;
+    }
+
+  if (image_is_64_bit(pe_hdr))
+    {
+      context->number_of_rva_and_sizes = pe_hdr->pe32plus.optional_header.num_data_directories;
+      context->size_of_headers = pe_hdr->pe32plus.optional_header.header_size;
+      context->image_size = pe_hdr->pe32plus.optional_header.image_size;
+      context->section_alignment = pe_hdr->pe32plus.optional_header.section_alignment;
+      file_alignment = pe_hdr->pe32plus.optional_header.file_alignment;
+      opt_header_size = sizeof(struct grub_pe64_optional_header);
+    }
+  else
+    {
+      context->number_of_rva_and_sizes = pe_hdr->pe32.optional_header.num_data_directories;
+      context->size_of_headers = pe_hdr->pe32.optional_header.header_size;
+      context->image_size = (grub_uint64_t)pe_hdr->pe32.optional_header.image_size;
+      context->section_alignment = pe_hdr->pe32.optional_header.section_alignment;
+      file_alignment = pe_hdr->pe32.optional_header.file_alignment;
+      opt_header_size = sizeof(struct grub_pe32_optional_header);
+    }
+
+  if (file_alignment % 2 != 0)
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "File Alignment is invalid (%ld)", file_alignment);
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+  if (file_alignment == 0)
+    file_alignment = 0x200;
+  if (context->section_alignment == 0)
+    context->section_alignment = GRUB_EFI_PAGE_SIZE;
+  if (context->section_alignment < file_alignment)
+    context->section_alignment = file_alignment;
+
+  context->number_of_sections = pe_hdr->pe32.coff_header.num_sections;
+
+  if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < context->number_of_rva_and_sizes)
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "Image header too small");
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+
+  header_without_data_dir = opt_header_size
+                  - sizeof (struct grub_pe32_data_directory) * EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES;
+  if (((grub_uint32_t)pe_hdr->pe32.coff_header.optional_header_size - header_without_data_dir) !=
+                  context->number_of_rva_and_sizes * sizeof (struct grub_pe32_data_directory))
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "Image header overflows data directory");
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+
+  section_header_offset = dos_hdr->e_lfanew
+                          + sizeof (grub_uint32_t)
+                          + sizeof (struct grub_pe32_coff_header)
+                          + pe_hdr->pe32.coff_header.optional_header_size;
+  if (((grub_uint32_t)context->image_size - section_header_offset) / sizeof(struct grub_pe32_section_table)
+                  <= context->number_of_sections)
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "Image sections overflow image size");
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+
+  if ((context->size_of_headers - section_header_offset) / sizeof(struct grub_pe32_section_table)
+                  < (grub_uint32_t)context->number_of_sections)
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "Image sections overflow section headers");
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+
+  if ((((grub_uint8_t *)pe_hdr - (grub_uint8_t *)data) + sizeof(grub_pe_header_t)) > datasize)
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid image");
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+
+  if (pe_hdr->pe32.signature[0] != 'P' ||
+              pe_hdr->pe32.signature[1] != 'E' ||
+              pe_hdr->pe32.signature[2] != 0x00 ||
+              pe_hdr->pe32.signature[3] != 0x00)
+    {
+      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "Unsupported image type");
+      return GRUB_ERR_NOT_IMPLEMENTED_YET;
+    }
+
+  context->pe_hdr = pe_hdr;
+
+  if (image_is_64_bit(pe_hdr))
+    {
+      context->image_address = pe_hdr->pe32plus.optional_header.image_base;
+      context->entry_point = pe_hdr->pe32plus.optional_header.entry_addr;
+      context->reloc_dir = &pe_hdr->pe32plus.optional_header.base_relocation_table;
+      context->sec_dir = &pe_hdr->pe32plus.optional_header.certificate_table;
+    }
+  else
+    {
+      context->image_address = pe_hdr->pe32.optional_header.image_base;
+      context->entry_point = pe_hdr->pe32.optional_header.entry_addr;
+      context->reloc_dir = &pe_hdr->pe32.optional_header.base_relocation_table;
+      context->sec_dir = &pe_hdr->pe32.optional_header.certificate_table;
+    }
+
+  context->first_section = (struct grub_pe32_section_table *)((char *)pe_hdr +
+          pe_hdr->pe32.coff_header.optional_header_size +
+          sizeof(grub_uint32_t) + sizeof(struct grub_pe32_coff_header));
+
+  if (context->image_size < context->size_of_headers)
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid image");
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+
+  if ((unsigned long)((grub_uint8_t *)context->sec_dir - (grub_uint8_t *)data) >
+      (datasize - sizeof(struct grub_pe32_data_directory)))
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid image");
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+  return GRUB_ERR_NONE;
+}
+
+static grub_phys_addr_t
+image_address (grub_phys_addr_t image, grub_uint64_t sz, grub_uint64_t addr)
+{
+  if (addr > sz)
+    return 0;
+
+  return image + addr;
+}
+
+static grub_err_t
+relocate_coff (pe_coff_loader_image_context_t *context,
+               struct grub_pe32_section_table *section,
+               grub_phys_addr_t phys_addr, grub_uint8_t *virt_addr,
+               mbi_load_data_t *mld)
+{
+  struct grub_pe32_data_directory *reloc_base, *reloc_base_end;
+  grub_uint8_t *reloc_buffer;
+  grub_uint64_t adjust;
+  struct grub_pe32_fixup_block *reloc, *reloc_end;
+  grub_uint8_t *fixup, *fixup_base;
+  grub_uint16_t *fixup_16;
+  grub_uint32_t *fixup_32;
+  grub_uint64_t *fixup_64;
+  int n = 0;
+  grub_err_t ret = GRUB_ERR_NONE;
+
+  if (image_is_64_bit (context->pe_hdr))
+    context->pe_hdr->pe32plus.optional_header.image_base =
+      (grub_uint64_t)(unsigned long)phys_addr;
+  else
+    context->pe_hdr->pe32.optional_header.image_base =
+      (grub_uint32_t)(unsigned long)phys_addr;
+
+  /* Alright, so here's how this works:
+   *
+   * context->reloc_dir gives us two things:
+   * - the VA the table of base relocation blocks are (maybe) to be
+   *   mapped at (reloc_dir->rva)
+   * - the virtual size (reloc_dir->size)
+   *
+   * The .reloc section (section here) gives us some other things:
+   * - the name! kind of. (section->name)
+   * - the virtual size (section->virtual_size), which should be the same
+   *   as RelocDir->Size
+   * - the virtual address (section->virtual_address)
+   * - the file section size (section->raw_data_size), which is
+   *   a multiple of optional_header->file_alignment.  Only useful for image
+   *   validation, not really useful for iteration bounds.
+   * - the file address (section->raw_data_offset)
+   * - a bunch of stuff we don't use that's 0 in our binaries usually
+   * - Flags (section->characteristics)
+   *
+   * and then the thing that's actually at the file address is an array
+   * of struct grub_pe32_fixup_block structs with some values packed behind
+   * them.  The block_size field of this structure includes the
+   * structure itself, and adding it to that structure's address will
+   * yield the next entry in the array.
+   */
+
+  reloc_buffer = grub_malloc (section->virtual_size);
+  if (!reloc_buffer)
+      return grub_errno;
+
+  if (grub_file_seek (mld->file, section->raw_data_offset) == (grub_off_t) -1)
+    {
+      ret = grub_errno;
+      goto out;
+    }
+
+  if (grub_file_read (mld->file, reloc_buffer, section->virtual_size)
+      != (grub_ssize_t) section->virtual_size)
+    {
+      if (!grub_errno)
+        {
+          grub_error (GRUB_ERR_FILE_READ_ERROR, "premature end of file %s",
+                      mld->filename);
+          ret = GRUB_ERR_FILE_READ_ERROR;
+        }
+      else
+          ret = grub_errno;
+      goto out;
+    }
+
+  reloc_base = (struct grub_pe32_data_directory *)reloc_buffer;
+  reloc_base_end = (struct grub_pe32_data_directory *)(reloc_buffer + section->virtual_size);
+
+  grub_dprintf ("multiboot_loader", "relocate_coff(): reloc_base %p reloc_base_end %p\n",
+                reloc_base, reloc_base_end);
+
+  if (!reloc_base && !reloc_base_end)
+    return GRUB_ERR_NONE;
+
+  if (!reloc_base || !reloc_base_end)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc table overflows binary");
+      return GRUB_ERR_BAD_ARGUMENT;
+    }
+
+  adjust = (grub_uint64_t)(grub_efi_uintn_t)phys_addr - context->image_address;
+  if (adjust == 0)
+    return GRUB_ERR_NONE;
+
+  while (reloc_base < reloc_base_end)
+    {
+      grub_uint16_t *entry;
+      reloc = (struct grub_pe32_fixup_block *)((char*)reloc_base);
+
+      if ((reloc_base->size == 0) ||
+          (reloc_base->size > context->reloc_dir->size))
+        {
+          grub_error (GRUB_ERR_BAD_ARGUMENT,
+                      "Reloc %d block size %d is invalid\n", n,
+                      reloc_base->size);
+          ret = GRUB_ERR_BAD_ARGUMENT;
+          goto out;
+        }
+
+      entry = &reloc->entries[0];
+      reloc_end = (struct grub_pe32_fixup_block *)
+        ((char *)reloc_base + reloc_base->size);
+
+      if ((grub_uint8_t *)reloc_end < reloc_buffer ||
+          (grub_uint8_t *)reloc_end > (reloc_buffer + section->virtual_size))
+        {
+          grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc entry %d overflows binary",
+                      n);
+          return GRUB_ERR_BAD_ARGUMENT;
+        }
+
+      fixup_base = virt_addr + reloc_base->rva;
+      if (!fixup_base)
+        {
+          grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc %d Invalid fixupbase", n);
+          ret = GRUB_ERR_BAD_ARGUMENT;
+          goto out;
+        }
+
+      while ((void *)entry < (void *)reloc_end)
+        {
+          fixup = fixup_base + (*entry & 0xFFF);
+          switch ((*entry) >> 12)
+            {
+              case GRUB_PE32_REL_BASED_ABSOLUTE:
+                break;
+              case GRUB_PE32_REL_BASED_HIGH:
+                fixup_16 = (grub_uint16_t *)fixup;
+                *fixup_16 = (grub_uint16_t)
+                  (*fixup_16 + ((grub_uint16_t)((grub_uint32_t)adjust >> 16)));
+                break;
+              case GRUB_PE32_REL_BASED_LOW:
+                fixup_16 = (grub_uint16_t *)fixup;
+                *fixup_16 = (grub_uint16_t) (*fixup_16 + (grub_uint16_t)adjust);
+                break;
+              case GRUB_PE32_REL_BASED_HIGHLOW:
+                fixup_32 = (grub_uint32_t *)fixup;
+                *fixup_32 = *fixup_32 + (grub_uint32_t)adjust;
+                break;
+              case GRUB_PE32_REL_BASED_DIR64:
+                fixup_64 = (grub_uint64_t *)fixup;
+                *fixup_64 = *fixup_64 + (grub_uint64_t)adjust;
+                break;
+              default:
+                grub_error (GRUB_ERR_BAD_ARGUMENT,
+                            "Reloc %d unknown relocation type %d",
+                            n, (*entry) >> 12);
+                ret = GRUB_ERR_BAD_ARGUMENT;
+                goto out;
+            }
+          entry += 1;
+        }
+      reloc_base = (struct grub_pe32_data_directory *)reloc_end;
+      n++;
+    }
+
+out:
+  grub_free(reloc_buffer);
+
+  return ret;
+}
+
+grub_err_t
+grub_multiboot2_load_pe (mbi_load_data_t *mld)
+{
+  int i;
+  grub_uint8_t *virt_addr;
+  grub_phys_addr_t phys_addr, reloc_base, reloc_base_end, base, end;
+  struct grub_pe32_section_table *reloc_section = NULL, fake_reloc_section;
+  struct grub_pe32_section_table *section;
+  grub_relocator_chunk_t ch;
+  pe_coff_loader_image_context_t context;
+  grub_err_t ret = GRUB_ERR_NONE;
+  grub_uint32_t section_alignment;
+  int found_entry_point = 0;
+
+  ret = read_header(mld->buffer, MULTIBOOT_SEARCH, &context);
+  if (ret)
+      return ret;
+
+  /*
+   * The spec says, uselessly, of SectionAlignment:
+   * =====
+   * The alignment (in bytes) of sections when they are loaded into
+   * memory. It must be greater than or equal to FileAlignment. The
+   * default is the page size for the architecture.
+   * =====
+   * Which doesn't tell you whose responsibility it is to enforce the
+   * "default", or when.  It implies that the value in the field must
+   * be > FileAlignment (also poorly defined), but it appears visual
+   * studio will happily write 512 for FileAlignment (its default) and
+   * 0 for SectionAlignment, intending to imply PAGE_SIZE.
+   *
+   * We only support one page size, so if it's zero, nerf it to 4096.
+   */
+  section_alignment = context.section_alignment;
+  if (section_alignment == 0)
+    section_alignment = 4096;
+
+  section_alignment = grub_max(section_alignment, mld->align);
+
+  ret = grub_relocator_alloc_chunk_align_safe (grub_multiboot2_relocator, &ch,
+                                               mld->min_addr, mld->max_addr,
+                                               context.image_size, section_alignment,
+                                               mld->preference, mld->avoid_efi_boot_services);
+
+  if (ret)
+    {
+      grub_dprintf ("multiboot_loader", "Cannot allocate memory for OS image\n");
+      return ret;
+    }
+
+  virt_addr = get_virtual_current_address (ch);
+  phys_addr = get_physical_target_address (ch);
+
+  if (grub_file_seek (mld->file, 0) == (grub_off_t) -1) {
+      ret = grub_errno;
+      goto out;
+  }
+
+  if (grub_file_read (mld->file, virt_addr, context.size_of_headers)
+      != (grub_ssize_t) context.size_of_headers)
+    {
+      if (!grub_errno)
+        {
+          grub_error (GRUB_ERR_FILE_READ_ERROR, "premature end of file %s",
+                      mld->filename);
+          ret = GRUB_ERR_FILE_READ_ERROR;
+        }
+      else
+          ret = grub_errno;
+      goto out;
+    }
+
+  mld->load_base_addr = phys_addr;
+  mld->link_base_addr = context.image_address;
+
+  grub_dprintf ("multiboot_loader", "load_base_addr: 0x%08x link_base_addr 0x%08x\n",
+                mld->load_base_addr, mld->link_base_addr);
+
+  grub_multiboot2_payload_eip = context.entry_point;
+  grub_dprintf ("multiboot_loader", "entry_point: 0x%08x\n", grub_multiboot2_payload_eip);
+  if (!grub_multiboot2_payload_eip)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid entry point");
+      ret = GRUB_ERR_BAD_ARGUMENT;
+      goto out;
+    }
+
+  grub_dprintf ("multiboot_loader", "reloc_dir: %p reloc_size: 0x%08x\n",
+                (void *)(unsigned long)context.reloc_dir->rva,
+                context.reloc_dir->size);
+  reloc_base = image_address (phys_addr, context.image_size,
+                              context.reloc_dir->rva);
+  /* RelocBaseEnd here is the address of the last byte of the table */
+  reloc_base_end = image_address (phys_addr, context.image_size,
+                                  context.reloc_dir->rva
+                                  + context.reloc_dir->size - 1);
+  grub_dprintf ("multiboot_loader", "reloc_base: 0x%016lx reloc_base_end: 0x%016lx\n",
+                reloc_base, reloc_base_end);
+
+  section = context.first_section;
+  for (i = 0; i < context.number_of_sections; i++, section++)
+    {
+      char name[9];
+
+      base = image_address (phys_addr, context.image_size,
+                            section->virtual_address);
+      end = image_address (phys_addr, context.image_size,
+                           section->virtual_address + section->virtual_size -1);
+
+      grub_strncpy(name, section->name, 9);
+      name[8] = '\0';
+      grub_dprintf ("multiboot_loader", "Section %d \"%s\" at 0x%016lx..0x%016lx\n", i,
+                   name, base, end);
+
+      if (end < base)
+        {
+          grub_dprintf ("multiboot_loader", " base is 0x%016lx but end is 0x%016lx... bad.\n",
+                       base, end);
+          grub_error (GRUB_ERR_BAD_ARGUMENT,
+                      "Image has invalid negative size");
+          ret = GRUB_ERR_BAD_ARGUMENT;
+          goto out;
+        }
+
+      if (section->virtual_address <= context.entry_point &&
+          (section->virtual_address + section->raw_data_size - 1)
+          > context.entry_point)
+        {
+          found_entry_point++;
+          grub_dprintf ("multiboot_loader", " section contains entry point\n");
+        }
+
+      /* We do want to process .reloc, but it's often marked
+       * discardable, so we don't want to memcpy it. */
+      if (grub_memcmp (section->name, ".reloc\0\0", 8) == 0)
+        {
+          if (reloc_section)
+            {
+              grub_error (GRUB_ERR_BAD_ARGUMENT,
+                          "Image has multiple relocation sections");
+              ret = GRUB_ERR_BAD_ARGUMENT;
+              goto out;
+            }
+
+          /* If it has nonzero sizes, and our bounds check
+           * made sense, and the VA and size match RelocDir's
+           * versions, then we believe in this section table. */
+          if (section->raw_data_size && section->virtual_size &&
+              base && end && reloc_base == base)
+            {
+              if (reloc_base_end == end)
+                {
+                  grub_dprintf ("multiboot_loader", " section is relocation section\n");
+                  reloc_section = section;
+                }
+              else if (reloc_base_end && reloc_base_end < end)
+                {
+                  /* Bogus virtual size in the reloc section -- RelocDir
+                   * reported a smaller Base Relocation Directory. Decrease
+                   * the section's virtual size so that it equal RelocDir's
+                   * idea, but only for the purposes of relocate_coff(). */
+                  grub_dprintf ("multiboot_loader",
+                                " section is (overlong) relocation section\n");
+                  grub_memcpy (&fake_reloc_section, section, sizeof *section);
+                  fake_reloc_section.virtual_size -= (end - reloc_base_end);
+                  reloc_section = &fake_reloc_section;
+                }
+            }
+
+          if (!reloc_section)
+            {
+              grub_dprintf ("multiboot_loader", " section is not reloc section?\n");
+              grub_dprintf ("multiboot_loader", " rds: 0x%08x, vs: %08x\n",
+                            section->raw_data_size, section->virtual_size);
+              grub_dprintf ("multiboot_loader", " base: 0x%016lx end: 0x%016lx\n", base, end);
+              grub_dprintf ("multiboot_loader", " reloc_base: 0x%016lx reloc_base_end: 0x%016lx\n",
+                            reloc_base, reloc_base_end);
+            }
+        }
+
+      grub_dprintf ("multiboot_loader", " Section characteristics are %08x\n",
+                   section->characteristics);
+      grub_dprintf ("multiboot_loader", " Section virtual size: %08x\n",
+                   section->virtual_size);
+      grub_dprintf ("multiboot_loader", " Section raw_data size: %08x\n",
+                   section->raw_data_size);
+      if (section->characteristics & GRUB_PE32_SCN_MEM_DISCARDABLE)
+        {
+          grub_dprintf ("multiboot_loader", " Discarding section\n");
+          continue;
+        }
+
+      if (!base || !end)
+        {
+          grub_dprintf ("multiboot_loader", " section is invalid\n");
+          grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid section size");
+          ret = GRUB_ERR_BAD_ARGUMENT;
+          goto out;
+        }
+
+      if (section->characteristics & GRUB_PE32_SCN_CNT_UNINITIALIZED_DATA)
+        {
+          if (section->raw_data_size != 0)
+            grub_dprintf ("multiboot_loader", " UNINITIALIZED_DATA section has data?\n");
+        }
+      else if (section->virtual_address < context.size_of_headers ||
+               section->raw_data_offset < context.size_of_headers)
+        {
+          grub_error (GRUB_ERR_BAD_ARGUMENT,
+                      "Section %d is inside image headers", i);
+          ret = GRUB_ERR_BAD_ARGUMENT;
+          goto out;
+        }
+
+      if (section->raw_data_size > 0)
+        {
+          grub_dprintf ("multiboot_loader", " copying 0x%08x bytes to 0x%016lx\n",
+                        section->raw_data_size, base);
+
+          if (grub_file_seek (mld->file, section->raw_data_offset) == (grub_off_t) -1)
+            {
+              ret = grub_errno;
+              goto out;
+            }
+
+          if (grub_file_read (mld->file, virt_addr + (base - phys_addr), section->raw_data_size)
+              != (grub_ssize_t) section->raw_data_size)
+            {
+              if (!grub_errno)
+                {
+                  grub_error (GRUB_ERR_FILE_READ_ERROR, "premature end of file %s",
+                              mld->filename);
+                  ret = GRUB_ERR_FILE_READ_ERROR;
+                }
+              else
+                  ret = grub_errno;
+              goto out;
+            }
+        }
+
+      if (section->raw_data_size < section->virtual_size)
+        {
+          grub_dprintf ("multiboot_loader", " padding with 0x%08x bytes at 0x%016lx\n",
+                        section->virtual_size - section->raw_data_size,
+                        base + section->raw_data_size);
+          grub_memset (virt_addr + (base - phys_addr) + section->raw_data_size, 0,
+                       section->virtual_size - section->raw_data_size);
+        }
+
+      grub_dprintf ("multiboot_loader", " finished section %s\n", name);
+    }
+
+  /* 5 == EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC */
+  if (context.number_of_rva_and_sizes <= 5)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "image has no relocation entry\n");
+      ret = GRUB_ERR_BAD_ARGUMENT;
+      goto out;
+    }
+
+  if (context.reloc_dir->size && reloc_section)
+    {
+      /* run the relocation fixups */
+      ret = relocate_coff (&context, reloc_section, phys_addr, virt_addr, mld);
+
+      if (ret)
+        {
+          grub_error (GRUB_ERR_BAD_ARGUMENT, "relocation failed");
+          goto out;
+        }
+    }
+
+  if (!found_entry_point)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "entry point is not within sections");
+      ret = GRUB_ERR_BAD_ARGUMENT;
+      goto out;
+    }
+  if (found_entry_point > 1)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "%d sections contain entry point",
+                  found_entry_point);
+      ret = GRUB_ERR_BAD_ARGUMENT;
+      goto out;
+    }
+
+out:
+  return ret;
+}
+
+bool
+grub_multiboot2_is_pe (mbi_load_data_t *mld)
+{
+  grub_pe32_msdos_header_t *dos_hdr = mld->buffer;
+
+  return dos_hdr->e_magic == GRUB_PE32_MAGIC;
+}
diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h
index 4e6e9d254bd3..2c8f7c3b85a5 100644
--- a/include/grub/efi/pe32.h
+++ b/include/grub/efi/pe32.h
@@ -20,6 +20,7 @@
 #define GRUB_EFI_PE32_HEADER	1
 
 #include <grub/types.h>
+#include <grub/efi/api.h>
 #include <grub/efi/memory.h>
 
 /* The MSDOS compatibility stub. This was copied from the output of
@@ -46,6 +47,28 @@
 
 #define GRUB_PE32_MSDOS_STUB_SIZE	0x80
 
+typedef struct {
+  grub_uint16_t  e_magic;    ///< Magic number.
+  grub_uint16_t  e_cblp;     ///< Bytes on last page of file.
+  grub_uint16_t  e_cp;       ///< Pages in file.
+  grub_uint16_t  e_crlc;     ///< Relocations.
+  grub_uint16_t  e_cparhdr;  ///< Size of header in paragraphs.
+  grub_uint16_t  e_minalloc; ///< Minimum extra paragraphs needed.
+  grub_uint16_t  e_maxalloc; ///< Maximum extra paragraphs needed.
+  grub_uint16_t  e_ss;       ///< Initial (relative) SS value.
+  grub_uint16_t  e_sp;       ///< Initial SP value.
+  grub_uint16_t  e_csum;     ///< Checksum.
+  grub_uint16_t  e_ip;       ///< Initial IP value.
+  grub_uint16_t  e_cs;       ///< Initial (relative) CS value.
+  grub_uint16_t  e_lfarlc;   ///< File address of relocation table.
+  grub_uint16_t  e_ovno;     ///< Overlay number.
+  grub_uint16_t  e_res[4];   ///< Reserved words.
+  grub_uint16_t  e_oemid;    ///< OEM identifier (for e_oeminfo).
+  grub_uint16_t  e_oeminfo;  ///< OEM information; e_oemid specific.
+  grub_uint16_t  e_res2[10]; ///< Reserved words.
+  grub_uint32_t  e_lfanew;   ///< File address of new exe header.
+} grub_pe32_msdos_header_t;
+
 #define GRUB_PE32_MAGIC			0x5a4d
 
 struct grub_msdos_image_header
@@ -249,6 +272,7 @@ struct grub_pe32_section_table
 
 #define GRUB_PE32_SCN_CNT_CODE			0x00000020
 #define GRUB_PE32_SCN_CNT_INITIALIZED_DATA	0x00000040
+#define GRUB_PE32_SCN_CNT_UNINITIALIZED_DATA	0x00000080
 #define GRUB_PE32_SCN_MEM_DISCARDABLE		0x02000000
 #define GRUB_PE32_SCN_MEM_EXECUTE		0x20000000
 #define GRUB_PE32_SCN_MEM_READ			0x40000000
@@ -349,4 +373,44 @@ struct grub_pe32_reloc
 #define GRUB_PE32_REL_I386_DIR32	0x6
 #define GRUB_PE32_REL_I386_REL32	0x14
 
+struct grub_pe32_header_32
+{
+  char signature[GRUB_PE32_SIGNATURE_SIZE];
+  struct grub_pe32_coff_header coff_header;
+  struct grub_pe32_optional_header optional_header;
+};
+
+struct grub_pe32_header_64
+{
+  char signature[GRUB_PE32_SIGNATURE_SIZE];
+  struct grub_pe32_coff_header coff_header;
+  struct grub_pe64_optional_header optional_header;
+};
+
+typedef union
+{
+  struct grub_pe32_header_32 pe32;
+  struct grub_pe32_header_64 pe32plus;
+} grub_pe_header_t;
+
+struct pe_coff_loader_image_context
+{
+  grub_efi_uint64_t image_address;
+  grub_efi_uint64_t image_size;
+  grub_efi_uint64_t entry_point;
+  grub_efi_uintn_t size_of_headers;
+  grub_efi_uint16_t image_type;
+  grub_efi_uint16_t number_of_sections;
+  grub_efi_uint32_t section_alignment;
+  struct grub_pe32_section_table *first_section;
+  struct grub_pe32_data_directory *reloc_dir;
+  struct grub_pe32_data_directory *sec_dir;
+  grub_efi_uint64_t number_of_rva_and_sizes;
+  grub_pe_header_t *pe_hdr;
+};
+
+typedef struct pe_coff_loader_image_context pe_coff_loader_image_context_t;
+
+#define EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES 16
+
 #endif /* ! GRUB_EFI_PE32_HEADER */
diff --git a/include/grub/multiboot.h b/include/grub/multiboot.h
index d8847f7531d3..c6af5c71b4e8 100644
--- a/include/grub/multiboot.h
+++ b/include/grub/multiboot.h
@@ -100,6 +100,9 @@ struct mbi_load_data
 };
 typedef struct mbi_load_data mbi_load_data_t;
 
+bool
+grub_multiboot_is_elf (mbi_load_data_t *mld);
+
 /* Load ELF32 or ELF64.  */
 grub_err_t
 grub_multiboot_load_elf (mbi_load_data_t *mld);
diff --git a/include/grub/multiboot2.h b/include/grub/multiboot2.h
index b90aa6989674..6169cb4472a7 100644
--- a/include/grub/multiboot2.h
+++ b/include/grub/multiboot2.h
@@ -92,10 +92,19 @@ struct mbi_load_data
 };
 typedef struct mbi_load_data mbi_load_data_t;
 
+bool
+grub_multiboot2_is_elf (mbi_load_data_t *mld);
+
 /* Load ELF32 or ELF64.  */
 grub_err_t
 grub_multiboot2_load_elf (mbi_load_data_t *mld);
 
+grub_err_t
+grub_multiboot2_load_pe (mbi_load_data_t *mld);
+
+bool
+grub_multiboot2_is_pe (mbi_load_data_t *mld);
+
 extern grub_size_t grub_multiboot2_pure_size;
 extern grub_size_t grub_multiboot2_alloc_mbi;
 extern grub_uint32_t grub_multiboot2_payload_eip;
-- 
2.43.0


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 3/3] efi: Allow loading multiboot modules without verification
  2024-03-28 15:12 ` Ross Lagerwall via Grub-devel
@ 2024-03-28 15:13   ` Ross Lagerwall via Grub-devel
  -1 siblings, 0 replies; 9+ messages in thread
From: Ross Lagerwall @ 2024-03-28 15:13 UTC (permalink / raw)
  To: grub-devel
  Cc: xen-devel, Ross Lagerwall, Daniel Kiper, Daniel Kiper,
	Andrew Cooper, Roger Pau Monné

GRUB doesn't do anything with multiboot modules except loading them and
passing a pointer to the multiboot kernel. Therefore GRUB itself doesn't
need to verify the module. Multiboot modules may contain code that needs
to be verified. If this is the case, the expectation is that the
multiboot kernel verifies the modules. For example, with Xen, the first
multiboot module contains the dom0 kernel binary and Xen verifies it
before starting it.

Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
---
 grub-core/kern/efi/sb.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/grub-core/kern/efi/sb.c b/grub-core/kern/efi/sb.c
index 8d3e413608bb..f76290d65e9f 100644
--- a/grub-core/kern/efi/sb.c
+++ b/grub-core/kern/efi/sb.c
@@ -171,6 +171,7 @@ shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)),
     case GRUB_FILE_TYPE_LOADENV:
     case GRUB_FILE_TYPE_SAVEENV:
     case GRUB_FILE_TYPE_VERIFY_SIGNATURE:
+    case GRUB_FILE_TYPE_MULTIBOOT_MODULE:
       *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
       return GRUB_ERR_NONE;
 
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 3/3] efi: Allow loading multiboot modules without verification
@ 2024-03-28 15:13   ` Ross Lagerwall via Grub-devel
  0 siblings, 0 replies; 9+ messages in thread
From: Ross Lagerwall via Grub-devel @ 2024-03-28 15:13 UTC (permalink / raw)
  To: grub-devel
  Cc: Ross Lagerwall, xen-devel, Daniel Kiper, Daniel Kiper,
	Andrew Cooper, Roger Pau Monné

GRUB doesn't do anything with multiboot modules except loading them and
passing a pointer to the multiboot kernel. Therefore GRUB itself doesn't
need to verify the module. Multiboot modules may contain code that needs
to be verified. If this is the case, the expectation is that the
multiboot kernel verifies the modules. For example, with Xen, the first
multiboot module contains the dom0 kernel binary and Xen verifies it
before starting it.

Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
---
 grub-core/kern/efi/sb.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/grub-core/kern/efi/sb.c b/grub-core/kern/efi/sb.c
index 8d3e413608bb..f76290d65e9f 100644
--- a/grub-core/kern/efi/sb.c
+++ b/grub-core/kern/efi/sb.c
@@ -171,6 +171,7 @@ shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)),
     case GRUB_FILE_TYPE_LOADENV:
     case GRUB_FILE_TYPE_SAVEENV:
     case GRUB_FILE_TYPE_VERIFY_SIGNATURE:
+    case GRUB_FILE_TYPE_MULTIBOOT_MODULE:
       *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
       return GRUB_ERR_NONE;
 
-- 
2.43.0


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 2/3] multiboot2: Add PE load support
       [not found] <mailman.9622.1711638669.7406.grub-devel@gnu.org>
@ 2024-04-01  9:59 ` avnish
  0 siblings, 0 replies; 9+ messages in thread
From: avnish @ 2024-04-01  9:59 UTC (permalink / raw)
  To: grub-devel, ross.lagerwall; +Cc: grub-devel-request, dkiper

> Date: Thu, 28 Mar 2024 15:13:01 +0000
> From: Ross Lagerwall <ross.lagerwall@citrix.com>
> To: grub-devel@gnu.org
> Cc: xen-devel@lists.xenproject.org, Ross Lagerwall
> 	<ross.lagerwall@citrix.com>, Daniel Kiper <daniel.kiper@oracle.com>,
> 	Daniel Kiper <dkiper@net-space.pl>, Andrew Cooper
> 	<andrew.cooper3@citrix.com>, Roger Pau Monné <roger.pau@citrix.com>
> Subject: [PATCH v2 2/3] multiboot2: Add PE load support
> Message-ID: <20240328151302.1451158-3-ross.lagerwall@citrix.com>
> 
> Add the ability to load multiboot binaries in PE format. This allows 
> the
> binaries to be signed and verified.
> 
> Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
> ---
>  grub-core/Makefile.core.def       |   1 +
>  grub-core/loader/multiboot.c      |   7 +
>  grub-core/loader/multiboot_mbi2.c |  11 +-
>  grub-core/loader/multiboot_pe.c   | 702 ++++++++++++++++++++++++++++++
>  include/grub/efi/pe32.h           |  64 +++
>  include/grub/multiboot.h          |   3 +
>  include/grub/multiboot2.h         |   9 +
>  7 files changed, 796 insertions(+), 1 deletion(-)
>  create mode 100644 grub-core/loader/multiboot_pe.c
> 
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index 1571421d7e84..34697ba58171 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -1815,6 +1815,7 @@ module = {
> 
>    common = loader/multiboot.c;
>    common = loader/multiboot_mbi2.c;
> +  common = loader/multiboot_pe.c;
>    enable = x86;
>    enable = i386_xen_pvh;
>    enable = mips;
> diff --git a/grub-core/loader/multiboot.c 
> b/grub-core/loader/multiboot.c
> index 94be512c4d0c..8220a74e6f19 100644
> --- a/grub-core/loader/multiboot.c
> +++ b/grub-core/loader/multiboot.c
> @@ -220,6 +220,13 @@ static grub_uint64_t highest_load;
>  #include "multiboot_elfxx.c"
>  #undef MULTIBOOT_LOAD_ELF32
> 
> +bool
> +GRUB_MULTIBOOT (is_elf) (mbi_load_data_t *mld)
> +{
> +  return grub_multiboot_is_elf32 (mld->buffer) ||
> +         grub_multiboot_is_elf64 (mld->buffer);
> +}
> +
>  /* Load ELF32 or ELF64.  */
>  grub_err_t
>  GRUB_MULTIBOOT (load_elf) (mbi_load_data_t *mld)
> diff --git a/grub-core/loader/multiboot_mbi2.c
> b/grub-core/loader/multiboot_mbi2.c
> index 00a48413c013..601f38161e11 100644
> --- a/grub-core/loader/multiboot_mbi2.c
> +++ b/grub-core/loader/multiboot_mbi2.c
> @@ -349,7 +349,16 @@ grub_multiboot2_load (grub_file_t file, const
> char *filename)
>        mld.file = file;
>        mld.filename = filename;
>        mld.avoid_efi_boot_services = keep_bs;
> -      err = grub_multiboot2_load_elf (&mld);
> +      if (grub_multiboot2_is_pe (&mld))
> +          err = grub_multiboot2_load_pe (&mld);
> +      else if (grub_multiboot2_is_elf (&mld))
> +          err = grub_multiboot2_load_elf (&mld);
> +      else
> +        {
> +          grub_free (mld.buffer);
> +          return grub_error (GRUB_ERR_UNKNOWN_OS,
> +                             "Unknown image type and address tag not
> specified");
> +        }
>        if (err)
>  	{
>  	  grub_free (mld.buffer);
> diff --git a/grub-core/loader/multiboot_pe.c 
> b/grub-core/loader/multiboot_pe.c
> new file mode 100644
> index 000000000000..1c194f89b79c
> --- /dev/null
> +++ b/grub-core/loader/multiboot_pe.c
> @@ -0,0 +1,702 @@
> +/*
> + * Significant portions of this code are derived from the Fedora GRUB 
> patch
> + * "0007-Add-secureboot-support-on-efi-chainloader.patch" which is in 
> turn
> + * derived from the PE loading code in Shim. The license is reproduced 
> below:
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + *
> + * Redistributions of source code must retain the above copyright
> + * notice, this list of conditions and the following disclaimer.
> + *
> + * Redistributions in binary form must reproduce the above copyright
> + * notice, this list of conditions and the following disclaimer in the
> + * documentation and/or other materials provided with the
> + * distribution.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
> + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
> + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
> + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * Copyright Peter Jones <pjones@redhat.com>
> + *
> + * Modifications:
> + *
> + * Copyright (c) 2024. Cloud Software Group, Inc.
> + */
> +
> +#include <grub/multiboot2.h>
> +#include <grub/i386/relocator.h>
> +#include <grub/efi/pe32.h>
> +
> +static int
> +image_is_64_bit (grub_pe_header_t *pe_hdr)
> +{
> +  /* .Magic is the same offset in all cases */
> +  return pe_hdr->pe32plus.optional_header.magic == 
> GRUB_PE32_PE64_MAGIC;
> +}
> +
> +static const grub_uint16_t machine_type __attribute__((__unused__)) =
> +#if defined(__x86_64__)
> +  GRUB_PE32_MACHINE_X86_64;
> +#elif defined(__aarch64__)
> +  GRUB_PE32_MACHINE_ARM64;
> +#elif defined(__arm__)
> +  GRUB_PE32_MACHINE_ARMTHUMB_MIXED;
> +#elif defined(__i386__) || defined(__i486__) || defined(__i686__)
> +  GRUB_PE32_MACHINE_I386;
> +#elif defined(__ia64__)
> +  GRUB_PE32_MACHINE_IA64;
> +#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
> +
> +static int
> +image_is_loadable(grub_pe_header_t *pe_hdr)
> +{
> +  /*
> +   * Check the machine type matches the binary and that we recognize
> +   * the magic number.
> +   */
> +    return (pe_hdr->pe32.coff_header.machine == machine_type ||
> +            (pe_hdr->pe32.coff_header.machine == 
> GRUB_PE32_MACHINE_X86_64 &&
> +             machine_type == GRUB_PE32_MACHINE_I386)) &&
> +           (pe_hdr->pe32plus.optional_header.magic == 
> GRUB_PE32_PE32_MAGIC ||
> +            pe_hdr->pe32plus.optional_header.magic == 
> GRUB_PE32_PE64_MAGIC);
> +}
> +
> +/*
> + * Read the binary header and grab appropriate information from it
> + */
> +static grub_err_t
> +read_header(void *data, unsigned int datasize,
> +            pe_coff_loader_image_context_t *context)
> +{
> +  grub_pe32_msdos_header_t *dos_hdr = data;
> +  grub_pe_header_t *pe_hdr = data;
> +  unsigned long header_without_data_dir, section_header_offset,
> opt_header_size;
> +  unsigned long file_alignment = 0;
> +
> +  if (datasize < sizeof (pe_hdr->pe32))
> +    {
> +      grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid image");
> +      return GRUB_ERR_OUT_OF_RANGE;
> +    }
> +
> +  if (dos_hdr->e_magic == GRUB_PE32_MAGIC)
> +    pe_hdr = (grub_pe_header_t *)((char *)data + dos_hdr->e_lfanew);
> +
> +  if (!image_is_loadable(pe_hdr))
> +    {
> +      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "Platform does not
> support this image");
> +      return GRUB_ERR_NOT_IMPLEMENTED_YET;
> +    }
> +
> +  if (image_is_64_bit(pe_hdr))
> +    {
> +      context->number_of_rva_and_sizes =
> pe_hdr->pe32plus.optional_header.num_data_directories;
> +      context->size_of_headers = 
> pe_hdr->pe32plus.optional_header.header_size;
> +      context->image_size = 
> pe_hdr->pe32plus.optional_header.image_size;
> +      context->section_alignment =
> pe_hdr->pe32plus.optional_header.section_alignment;
> +      file_alignment = 
> pe_hdr->pe32plus.optional_header.file_alignment;
> +      opt_header_size = sizeof(struct grub_pe64_optional_header);
> +    }
> +  else
> +    {
> +      context->number_of_rva_and_sizes =
> pe_hdr->pe32.optional_header.num_data_directories;
> +      context->size_of_headers = 
> pe_hdr->pe32.optional_header.header_size;
> +      context->image_size =
> (grub_uint64_t)pe_hdr->pe32.optional_header.image_size;
> +      context->section_alignment =
> pe_hdr->pe32.optional_header.section_alignment;
> +      file_alignment = pe_hdr->pe32.optional_header.file_alignment;
> +      opt_header_size = sizeof(struct grub_pe32_optional_header);
> +    }
> +
> +  if (file_alignment % 2 != 0)
> +    {
> +      grub_error (GRUB_ERR_OUT_OF_RANGE, "File Alignment is invalid
> (%ld)", file_alignment);
> +      return GRUB_ERR_OUT_OF_RANGE;
> +    }
> +  if (file_alignment == 0)
> +    file_alignment = 0x200;
> +  if (context->section_alignment == 0)
> +    context->section_alignment = GRUB_EFI_PAGE_SIZE;
> +  if (context->section_alignment < file_alignment)
> +    context->section_alignment = file_alignment;
> +
> +  context->number_of_sections = pe_hdr->pe32.coff_header.num_sections;
> +
> +  if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < 
> context->number_of_rva_and_sizes)
> +    {
> +      grub_error (GRUB_ERR_OUT_OF_RANGE, "Image header too small");
> +      return GRUB_ERR_OUT_OF_RANGE;
> +    }
> +
> +  header_without_data_dir = opt_header_size
> +                  - sizeof (struct grub_pe32_data_directory) *
> EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES;
> +  if (((grub_uint32_t)pe_hdr->pe32.coff_header.optional_header_size -
> header_without_data_dir) !=
> +                  context->number_of_rva_and_sizes * sizeof (struct
> grub_pe32_data_directory))
> +    {
> +      grub_error (GRUB_ERR_OUT_OF_RANGE, "Image header overflows data
> directory");
> +      return GRUB_ERR_OUT_OF_RANGE;
> +    }
> +
> +  section_header_offset = dos_hdr->e_lfanew
> +                          + sizeof (grub_uint32_t)
> +                          + sizeof (struct grub_pe32_coff_header)
> +                          + 
> pe_hdr->pe32.coff_header.optional_header_size;
> +  if (((grub_uint32_t)context->image_size - section_header_offset) /
> sizeof(struct grub_pe32_section_table)
> +                  <= context->number_of_sections)
> +    {
> +      grub_error (GRUB_ERR_OUT_OF_RANGE, "Image sections overflow 
> image size");
> +      return GRUB_ERR_OUT_OF_RANGE;
> +    }
> +
> +  if ((context->size_of_headers - section_header_offset) /
> sizeof(struct grub_pe32_section_table)
> +                  < (grub_uint32_t)context->number_of_sections)
> +    {
> +      grub_error (GRUB_ERR_OUT_OF_RANGE, "Image sections overflow
> section headers");
> +      return GRUB_ERR_OUT_OF_RANGE;
> +    }
> +
> +  if ((((grub_uint8_t *)pe_hdr - (grub_uint8_t *)data) +
> sizeof(grub_pe_header_t)) > datasize)
> +    {
> +      grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid image");
> +      return GRUB_ERR_OUT_OF_RANGE;
> +    }
> +
> +  if (pe_hdr->pe32.signature[0] != 'P' ||
> +              pe_hdr->pe32.signature[1] != 'E' ||
> +              pe_hdr->pe32.signature[2] != 0x00 ||
> +              pe_hdr->pe32.signature[3] != 0x00)
> +    {
> +      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "Unsupported image 
> type");
> +      return GRUB_ERR_NOT_IMPLEMENTED_YET;
> +    }
> +
> +  context->pe_hdr = pe_hdr;
> +
> +  if (image_is_64_bit(pe_hdr))
> +    {
> +      context->image_address = 
> pe_hdr->pe32plus.optional_header.image_base;
> +      context->entry_point = 
> pe_hdr->pe32plus.optional_header.entry_addr;
> +      context->reloc_dir =
> &pe_hdr->pe32plus.optional_header.base_relocation_table;
> +      context->sec_dir = 
> &pe_hdr->pe32plus.optional_header.certificate_table;
> +    }
> +  else
> +    {
> +      context->image_address = 
> pe_hdr->pe32.optional_header.image_base;
> +      context->entry_point = pe_hdr->pe32.optional_header.entry_addr;
> +      context->reloc_dir = 
> &pe_hdr->pe32.optional_header.base_relocation_table;
> +      context->sec_dir = 
> &pe_hdr->pe32.optional_header.certificate_table;
> +    }
> +
> +  context->first_section = (struct grub_pe32_section_table *)((char 
> *)pe_hdr +
> +          pe_hdr->pe32.coff_header.optional_header_size +
> +          sizeof(grub_uint32_t) + sizeof(struct 
> grub_pe32_coff_header));
> +
> +  if (context->image_size < context->size_of_headers)
> +    {
> +      grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid image");
> +      return GRUB_ERR_OUT_OF_RANGE;
> +    }
> +
> +  if ((unsigned long)((grub_uint8_t *)context->sec_dir -
> (grub_uint8_t *)data) >
> +      (datasize - sizeof(struct grub_pe32_data_directory)))
> +    {
> +      grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid image");
> +      return GRUB_ERR_OUT_OF_RANGE;
> +    }
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_phys_addr_t
> +image_address (grub_phys_addr_t image, grub_uint64_t sz, grub_uint64_t 
> addr)
> +{
> +  if (addr > sz)
> +    return 0;
> +
> +  return image + addr;
> +}
> +
> +static grub_err_t
> +relocate_coff (pe_coff_loader_image_context_t *context,
> +               struct grub_pe32_section_table *section,
> +               grub_phys_addr_t phys_addr, grub_uint8_t *virt_addr,
> +               mbi_load_data_t *mld)
> +{
> +  struct grub_pe32_data_directory *reloc_base, *reloc_base_end;
> +  grub_uint8_t *reloc_buffer;
> +  grub_uint64_t adjust;
> +  struct grub_pe32_fixup_block *reloc, *reloc_end;
> +  grub_uint8_t *fixup, *fixup_base;
> +  grub_uint16_t *fixup_16;
> +  grub_uint32_t *fixup_32;
> +  grub_uint64_t *fixup_64;
> +  int n = 0;
> +  grub_err_t ret = GRUB_ERR_NONE;
> +
> +  if (image_is_64_bit (context->pe_hdr))
> +    context->pe_hdr->pe32plus.optional_header.image_base =
> +      (grub_uint64_t)(unsigned long)phys_addr;
> +  else
> +    context->pe_hdr->pe32.optional_header.image_base =
> +      (grub_uint32_t)(unsigned long)phys_addr;
> +
> +  /* Alright, so here's how this works:
> +   *
> +   * context->reloc_dir gives us two things:
> +   * - the VA the table of base relocation blocks are (maybe) to be
> +   *   mapped at (reloc_dir->rva)
> +   * - the virtual size (reloc_dir->size)
> +   *
> +   * The .reloc section (section here) gives us some other things:
> +   * - the name! kind of. (section->name)
> +   * - the virtual size (section->virtual_size), which should be the 
> same
> +   *   as RelocDir->Size
> +   * - the virtual address (section->virtual_address)
> +   * - the file section size (section->raw_data_size), which is
> +   *   a multiple of optional_header->file_alignment.  Only useful for 
> image
> +   *   validation, not really useful for iteration bounds.
> +   * - the file address (section->raw_data_offset)
> +   * - a bunch of stuff we don't use that's 0 in our binaries usually
> +   * - Flags (section->characteristics)
> +   *
> +   * and then the thing that's actually at the file address is an 
> array
> +   * of struct grub_pe32_fixup_block structs with some values packed 
> behind
> +   * them.  The block_size field of this structure includes the
> +   * structure itself, and adding it to that structure's address will
> +   * yield the next entry in the array.
> +   */
> +
> +  reloc_buffer = grub_malloc (section->virtual_size);
> +  if (!reloc_buffer)
> +      return grub_errno;
> +
> +  if (grub_file_seek (mld->file, section->raw_data_offset) == 
> (grub_off_t) -1)
> +    {
> +      ret = grub_errno;
> +      goto out;
> +    }
> +
> +  if (grub_file_read (mld->file, reloc_buffer, section->virtual_size)
> +      != (grub_ssize_t) section->virtual_size)
> +    {
> +      if (!grub_errno)
> +        {
> +          grub_error (GRUB_ERR_FILE_READ_ERROR, "premature end of file 
> %s",
> +                      mld->filename);
> +          ret = GRUB_ERR_FILE_READ_ERROR;
> +        }
> +      else
> +          ret = grub_errno;
> +      goto out;
> +    }
> +
> +  reloc_base = (struct grub_pe32_data_directory *)reloc_buffer;
> +  reloc_base_end = (struct grub_pe32_data_directory *)(reloc_buffer +
> section->virtual_size);
> +
> +  grub_dprintf ("multiboot_loader", "relocate_coff(): reloc_base %p
> reloc_base_end %p\n",
> +                reloc_base, reloc_base_end);
> +
> +  if (!reloc_base && !reloc_base_end)
> +    return GRUB_ERR_NONE;
> +
> +  if (!reloc_base || !reloc_base_end)
> +    {
> +      grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc table overflows 
> binary");
> +      return GRUB_ERR_BAD_ARGUMENT;

Hi Ross,

Is reloc_base memory going to be used after returning from here? If not, 
then we need to free reloc_buffer!
Something like below, similarily used in the other parts of the code:

ret = GRUB_ERR_BAD_ARGUMENT;
     goto out;

> +    }
> +
> +  adjust = (grub_uint64_t)(grub_efi_uintn_t)phys_addr - 
> context->image_address;
> +  if (adjust == 0)
> +    return GRUB_ERR_NONE;
> +
> +  while (reloc_base < reloc_base_end)
> +    {
> +      grub_uint16_t *entry;
> +      reloc = (struct grub_pe32_fixup_block *)((char*)reloc_base);
> +
> +      if ((reloc_base->size == 0) ||
> +          (reloc_base->size > context->reloc_dir->size))
> +        {
> +          grub_error (GRUB_ERR_BAD_ARGUMENT,
> +                      "Reloc %d block size %d is invalid\n", n,
> +                      reloc_base->size);
> +          ret = GRUB_ERR_BAD_ARGUMENT;
> +          goto out;
> +        }
> +
> +      entry = &reloc->entries[0];
> +      reloc_end = (struct grub_pe32_fixup_block *)
> +        ((char *)reloc_base + reloc_base->size);
> +
> +      if ((grub_uint8_t *)reloc_end < reloc_buffer ||
> +          (grub_uint8_t *)reloc_end > (reloc_buffer + 
> section->virtual_size))
> +        {
> +          grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc entry %d overflows 
> binary",
> +                      n);
> +          return GRUB_ERR_BAD_ARGUMENT;

Same as first comment! we need to free reloc_buffer!

Thank you,
Avnish Chouhan

> +        }
> +
> +      fixup_base = virt_addr + reloc_base->rva;
> +      if (!fixup_base)
> +        {
> +          grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc %d Invalid 
> fixupbase", n);
> +          ret = GRUB_ERR_BAD_ARGUMENT;
> +          goto out;
> +        }
> +
> +      while ((void *)entry < (void *)reloc_end)
> +        {
> +          fixup = fixup_base + (*entry & 0xFFF);
> +          switch ((*entry) >> 12)
> +            {
> +              case GRUB_PE32_REL_BASED_ABSOLUTE:
> +                break;
> +              case GRUB_PE32_REL_BASED_HIGH:
> +                fixup_16 = (grub_uint16_t *)fixup;
> +                *fixup_16 = (grub_uint16_t)
> +                  (*fixup_16 + ((grub_uint16_t)((grub_uint32_t)adjust 
> >> 16)));
> +                break;
> +              case GRUB_PE32_REL_BASED_LOW:
> +                fixup_16 = (grub_uint16_t *)fixup;
> +                *fixup_16 = (grub_uint16_t) (*fixup_16 +
> (grub_uint16_t)adjust);
> +                break;
> +              case GRUB_PE32_REL_BASED_HIGHLOW:
> +                fixup_32 = (grub_uint32_t *)fixup;
> +                *fixup_32 = *fixup_32 + (grub_uint32_t)adjust;
> +                break;
> +              case GRUB_PE32_REL_BASED_DIR64:
> +                fixup_64 = (grub_uint64_t *)fixup;
> +                *fixup_64 = *fixup_64 + (grub_uint64_t)adjust;
> +                break;
> +              default:
> +                grub_error (GRUB_ERR_BAD_ARGUMENT,
> +                            "Reloc %d unknown relocation type %d",
> +                            n, (*entry) >> 12);
> +                ret = GRUB_ERR_BAD_ARGUMENT;
> +                goto out;
> +            }
> +          entry += 1;
> +        }
> +      reloc_base = (struct grub_pe32_data_directory *)reloc_end;
> +      n++;
> +    }
> +
> +out:
> +  grub_free(reloc_buffer);
> +
> +  return ret;
> +}
> +
> +grub_err_t
> +grub_multiboot2_load_pe (mbi_load_data_t *mld)
> +{
> +  int i;
> +  grub_uint8_t *virt_addr;
> +  grub_phys_addr_t phys_addr, reloc_base, reloc_base_end, base, end;
> +  struct grub_pe32_section_table *reloc_section = NULL, 
> fake_reloc_section;
> +  struct grub_pe32_section_table *section;
> +  grub_relocator_chunk_t ch;
> +  pe_coff_loader_image_context_t context;
> +  grub_err_t ret = GRUB_ERR_NONE;
> +  grub_uint32_t section_alignment;
> +  int found_entry_point = 0;
> +
> +  ret = read_header(mld->buffer, MULTIBOOT_SEARCH, &context);
> +  if (ret)
> +      return ret;
> +
> +  /*
> +   * The spec says, uselessly, of SectionAlignment:
> +   * =====
> +   * The alignment (in bytes) of sections when they are loaded into
> +   * memory. It must be greater than or equal to FileAlignment. The
> +   * default is the page size for the architecture.
> +   * =====
> +   * Which doesn't tell you whose responsibility it is to enforce the
> +   * "default", or when.  It implies that the value in the field must
> +   * be > FileAlignment (also poorly defined), but it appears visual
> +   * studio will happily write 512 for FileAlignment (its default) and
> +   * 0 for SectionAlignment, intending to imply PAGE_SIZE.
> +   *
> +   * We only support one page size, so if it's zero, nerf it to 4096.
> +   */
> +  section_alignment = context.section_alignment;
> +  if (section_alignment == 0)
> +    section_alignment = 4096;
> +
> +  section_alignment = grub_max(section_alignment, mld->align);
> +
> +  ret = grub_relocator_alloc_chunk_align_safe 
> (grub_multiboot2_relocator, &ch,
> +                                               mld->min_addr, 
> mld->max_addr,
> +                                               context.image_size,
> section_alignment,
> +                                               mld->preference,
> mld->avoid_efi_boot_services);
> +
> +  if (ret)
> +    {
> +      grub_dprintf ("multiboot_loader", "Cannot allocate memory for
> OS image\n");
> +      return ret;
> +    }
> +
> +  virt_addr = get_virtual_current_address (ch);
> +  phys_addr = get_physical_target_address (ch);
> +
> +  if (grub_file_seek (mld->file, 0) == (grub_off_t) -1) {
> +      ret = grub_errno;
> +      goto out;
> +  }
> +
> +  if (grub_file_read (mld->file, virt_addr, context.size_of_headers)
> +      != (grub_ssize_t) context.size_of_headers)
> +    {
> +      if (!grub_errno)
> +        {
> +          grub_error (GRUB_ERR_FILE_READ_ERROR, "premature end of file 
> %s",
> +                      mld->filename);
> +          ret = GRUB_ERR_FILE_READ_ERROR;
> +        }
> +      else
> +          ret = grub_errno;
> +      goto out;
> +    }
> +
> +  mld->load_base_addr = phys_addr;
> +  mld->link_base_addr = context.image_address;
> +
> +  grub_dprintf ("multiboot_loader", "load_base_addr: 0x%08x
> link_base_addr 0x%08x\n",
> +                mld->load_base_addr, mld->link_base_addr);
> +
> +  grub_multiboot2_payload_eip = context.entry_point;
> +  grub_dprintf ("multiboot_loader", "entry_point: 0x%08x\n",
> grub_multiboot2_payload_eip);
> +  if (!grub_multiboot2_payload_eip)
> +    {
> +      grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid entry point");
> +      ret = GRUB_ERR_BAD_ARGUMENT;
> +      goto out;
> +    }
> +
> +  grub_dprintf ("multiboot_loader", "reloc_dir: %p reloc_size: 
> 0x%08x\n",
> +                (void *)(unsigned long)context.reloc_dir->rva,
> +                context.reloc_dir->size);
> +  reloc_base = image_address (phys_addr, context.image_size,
> +                              context.reloc_dir->rva);
> +  /* RelocBaseEnd here is the address of the last byte of the table */
> +  reloc_base_end = image_address (phys_addr, context.image_size,
> +                                  context.reloc_dir->rva
> +                                  + context.reloc_dir->size - 1);
> +  grub_dprintf ("multiboot_loader", "reloc_base: 0x%016lx
> reloc_base_end: 0x%016lx\n",
> +                reloc_base, reloc_base_end);
> +
> +  section = context.first_section;
> +  for (i = 0; i < context.number_of_sections; i++, section++)
> +    {
> +      char name[9];
> +
> +      base = image_address (phys_addr, context.image_size,
> +                            section->virtual_address);
> +      end = image_address (phys_addr, context.image_size,
> +                           section->virtual_address +
> section->virtual_size -1);
> +
> +      grub_strncpy(name, section->name, 9);
> +      name[8] = '\0';
> +      grub_dprintf ("multiboot_loader", "Section %d \"%s\" at
> 0x%016lx..0x%016lx\n", i,
> +                   name, base, end);
> +
> +      if (end < base)
> +        {
> +          grub_dprintf ("multiboot_loader", " base is 0x%016lx but
> end is 0x%016lx... bad.\n",
> +                       base, end);
> +          grub_error (GRUB_ERR_BAD_ARGUMENT,
> +                      "Image has invalid negative size");
> +          ret = GRUB_ERR_BAD_ARGUMENT;
> +          goto out;
> +        }
> +
> +      if (section->virtual_address <= context.entry_point &&
> +          (section->virtual_address + section->raw_data_size - 1)
> +          > context.entry_point)
> +        {
> +          found_entry_point++;
> +          grub_dprintf ("multiboot_loader", " section contains entry 
> point\n");
> +        }
> +
> +      /* We do want to process .reloc, but it's often marked
> +       * discardable, so we don't want to memcpy it. */
> +      if (grub_memcmp (section->name, ".reloc\0\0", 8) == 0)
> +        {
> +          if (reloc_section)
> +            {
> +              grub_error (GRUB_ERR_BAD_ARGUMENT,
> +                          "Image has multiple relocation sections");
> +              ret = GRUB_ERR_BAD_ARGUMENT;
> +              goto out;
> +            }
> +
> +          /* If it has nonzero sizes, and our bounds check
> +           * made sense, and the VA and size match RelocDir's
> +           * versions, then we believe in this section table. */
> +          if (section->raw_data_size && section->virtual_size &&
> +              base && end && reloc_base == base)
> +            {
> +              if (reloc_base_end == end)
> +                {
> +                  grub_dprintf ("multiboot_loader", " section is
> relocation section\n");
> +                  reloc_section = section;
> +                }
> +              else if (reloc_base_end && reloc_base_end < end)
> +                {
> +                  /* Bogus virtual size in the reloc section -- 
> RelocDir
> +                   * reported a smaller Base Relocation Directory. 
> Decrease
> +                   * the section's virtual size so that it equal 
> RelocDir's
> +                   * idea, but only for the purposes of 
> relocate_coff(). */
> +                  grub_dprintf ("multiboot_loader",
> +                                " section is (overlong) relocation 
> section\n");
> +                  grub_memcpy (&fake_reloc_section, section, sizeof 
> *section);
> +                  fake_reloc_section.virtual_size -= (end - 
> reloc_base_end);
> +                  reloc_section = &fake_reloc_section;
> +                }
> +            }
> +
> +          if (!reloc_section)
> +            {
> +              grub_dprintf ("multiboot_loader", " section is not
> reloc section?\n");
> +              grub_dprintf ("multiboot_loader", " rds: 0x%08x, vs: 
> %08x\n",
> +                            section->raw_data_size, 
> section->virtual_size);
> +              grub_dprintf ("multiboot_loader", " base: 0x%016lx end:
> 0x%016lx\n", base, end);
> +              grub_dprintf ("multiboot_loader", " reloc_base:
> 0x%016lx reloc_base_end: 0x%016lx\n",
> +                            reloc_base, reloc_base_end);
> +            }
> +        }
> +
> +      grub_dprintf ("multiboot_loader", " Section characteristics are 
> %08x\n",
> +                   section->characteristics);
> +      grub_dprintf ("multiboot_loader", " Section virtual size: 
> %08x\n",
> +                   section->virtual_size);
> +      grub_dprintf ("multiboot_loader", " Section raw_data size: 
> %08x\n",
> +                   section->raw_data_size);
> +      if (section->characteristics & GRUB_PE32_SCN_MEM_DISCARDABLE)
> +        {
> +          grub_dprintf ("multiboot_loader", " Discarding section\n");
> +          continue;
> +        }
> +
> +      if (!base || !end)
> +        {
> +          grub_dprintf ("multiboot_loader", " section is invalid\n");
> +          grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid section size");
> +          ret = GRUB_ERR_BAD_ARGUMENT;
> +          goto out;
> +        }
> +
> +      if (section->characteristics & 
> GRUB_PE32_SCN_CNT_UNINITIALIZED_DATA)
> +        {
> +          if (section->raw_data_size != 0)
> +            grub_dprintf ("multiboot_loader", " UNINITIALIZED_DATA
> section has data?\n");
> +        }
> +      else if (section->virtual_address < context.size_of_headers ||
> +               section->raw_data_offset < context.size_of_headers)
> +        {
> +          grub_error (GRUB_ERR_BAD_ARGUMENT,
> +                      "Section %d is inside image headers", i);
> +          ret = GRUB_ERR_BAD_ARGUMENT;
> +          goto out;
> +        }
> +
> +      if (section->raw_data_size > 0)
> +        {
> +          grub_dprintf ("multiboot_loader", " copying 0x%08x bytes to
> 0x%016lx\n",
> +                        section->raw_data_size, base);
> +
> +          if (grub_file_seek (mld->file, section->raw_data_offset) ==
> (grub_off_t) -1)
> +            {
> +              ret = grub_errno;
> +              goto out;
> +            }
> +
> +          if (grub_file_read (mld->file, virt_addr + (base -
> phys_addr), section->raw_data_size)
> +              != (grub_ssize_t) section->raw_data_size)
> +            {
> +              if (!grub_errno)
> +                {
> +                  grub_error (GRUB_ERR_FILE_READ_ERROR, "premature
> end of file %s",
> +                              mld->filename);
> +                  ret = GRUB_ERR_FILE_READ_ERROR;
> +                }
> +              else
> +                  ret = grub_errno;
> +              goto out;
> +            }
> +        }
> +
> +      if (section->raw_data_size < section->virtual_size)
> +        {
> +          grub_dprintf ("multiboot_loader", " padding with 0x%08x
> bytes at 0x%016lx\n",
> +                        section->virtual_size - 
> section->raw_data_size,
> +                        base + section->raw_data_size);
> +          grub_memset (virt_addr + (base - phys_addr) +
> section->raw_data_size, 0,
> +                       section->virtual_size - 
> section->raw_data_size);
> +        }
> +
> +      grub_dprintf ("multiboot_loader", " finished section %s\n", 
> name);
> +    }
> +
> +  /* 5 == EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC */
> +  if (context.number_of_rva_and_sizes <= 5)
> +    {
> +      grub_error (GRUB_ERR_BAD_ARGUMENT, "image has no relocation 
> entry\n");
> +      ret = GRUB_ERR_BAD_ARGUMENT;
> +      goto out;
> +    }
> +
> +  if (context.reloc_dir->size && reloc_section)
> +    {
> +      /* run the relocation fixups */
> +      ret = relocate_coff (&context, reloc_section, phys_addr, 
> virt_addr, mld);
> +
> +      if (ret)
> +        {
> +          grub_error (GRUB_ERR_BAD_ARGUMENT, "relocation failed");
> +          goto out;
> +        }
> +    }
> +
> +  if (!found_entry_point)
> +    {
> +      grub_error (GRUB_ERR_BAD_ARGUMENT, "entry point is not within 
> sections");
> +      ret = GRUB_ERR_BAD_ARGUMENT;
> +      goto out;
> +    }
> +  if (found_entry_point > 1)
> +    {
> +      grub_error (GRUB_ERR_BAD_ARGUMENT, "%d sections contain entry 
> point",
> +                  found_entry_point);
> +      ret = GRUB_ERR_BAD_ARGUMENT;
> +      goto out;
> +    }
> +
> +out:
> +  return ret;
> +}
> +
> +bool
> +grub_multiboot2_is_pe (mbi_load_data_t *mld)
> +{
> +  grub_pe32_msdos_header_t *dos_hdr = mld->buffer;
> +
> +  return dos_hdr->e_magic == GRUB_PE32_MAGIC;
> +}
> diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h
> index 4e6e9d254bd3..2c8f7c3b85a5 100644
> --- a/include/grub/efi/pe32.h
> +++ b/include/grub/efi/pe32.h
> @@ -20,6 +20,7 @@
>  #define GRUB_EFI_PE32_HEADER	1
> 
>  #include <grub/types.h>
> +#include <grub/efi/api.h>
>  #include <grub/efi/memory.h>
> 
>  /* The MSDOS compatibility stub. This was copied from the output of
> @@ -46,6 +47,28 @@
> 
>  #define GRUB_PE32_MSDOS_STUB_SIZE	0x80
> 
> +typedef struct {
> +  grub_uint16_t  e_magic;    ///< Magic number.
> +  grub_uint16_t  e_cblp;     ///< Bytes on last page of file.
> +  grub_uint16_t  e_cp;       ///< Pages in file.
> +  grub_uint16_t  e_crlc;     ///< Relocations.
> +  grub_uint16_t  e_cparhdr;  ///< Size of header in paragraphs.
> +  grub_uint16_t  e_minalloc; ///< Minimum extra paragraphs needed.
> +  grub_uint16_t  e_maxalloc; ///< Maximum extra paragraphs needed.
> +  grub_uint16_t  e_ss;       ///< Initial (relative) SS value.
> +  grub_uint16_t  e_sp;       ///< Initial SP value.
> +  grub_uint16_t  e_csum;     ///< Checksum.
> +  grub_uint16_t  e_ip;       ///< Initial IP value.
> +  grub_uint16_t  e_cs;       ///< Initial (relative) CS value.
> +  grub_uint16_t  e_lfarlc;   ///< File address of relocation table.
> +  grub_uint16_t  e_ovno;     ///< Overlay number.
> +  grub_uint16_t  e_res[4];   ///< Reserved words.
> +  grub_uint16_t  e_oemid;    ///< OEM identifier (for e_oeminfo).
> +  grub_uint16_t  e_oeminfo;  ///< OEM information; e_oemid specific.
> +  grub_uint16_t  e_res2[10]; ///< Reserved words.
> +  grub_uint32_t  e_lfanew;   ///< File address of new exe header.
> +} grub_pe32_msdos_header_t;
> +
>  #define GRUB_PE32_MAGIC			0x5a4d
> 
>  struct grub_msdos_image_header
> @@ -249,6 +272,7 @@ struct grub_pe32_section_table
> 
>  #define GRUB_PE32_SCN_CNT_CODE			0x00000020
>  #define GRUB_PE32_SCN_CNT_INITIALIZED_DATA	0x00000040
> +#define GRUB_PE32_SCN_CNT_UNINITIALIZED_DATA	0x00000080
>  #define GRUB_PE32_SCN_MEM_DISCARDABLE		0x02000000
>  #define GRUB_PE32_SCN_MEM_EXECUTE		0x20000000
>  #define GRUB_PE32_SCN_MEM_READ			0x40000000
> @@ -349,4 +373,44 @@ struct grub_pe32_reloc
>  #define GRUB_PE32_REL_I386_DIR32	0x6
>  #define GRUB_PE32_REL_I386_REL32	0x14
> 
> +struct grub_pe32_header_32
> +{
> +  char signature[GRUB_PE32_SIGNATURE_SIZE];
> +  struct grub_pe32_coff_header coff_header;
> +  struct grub_pe32_optional_header optional_header;
> +};
> +
> +struct grub_pe32_header_64
> +{
> +  char signature[GRUB_PE32_SIGNATURE_SIZE];
> +  struct grub_pe32_coff_header coff_header;
> +  struct grub_pe64_optional_header optional_header;
> +};
> +
> +typedef union
> +{
> +  struct grub_pe32_header_32 pe32;
> +  struct grub_pe32_header_64 pe32plus;
> +} grub_pe_header_t;
> +
> +struct pe_coff_loader_image_context
> +{
> +  grub_efi_uint64_t image_address;
> +  grub_efi_uint64_t image_size;
> +  grub_efi_uint64_t entry_point;
> +  grub_efi_uintn_t size_of_headers;
> +  grub_efi_uint16_t image_type;
> +  grub_efi_uint16_t number_of_sections;
> +  grub_efi_uint32_t section_alignment;
> +  struct grub_pe32_section_table *first_section;
> +  struct grub_pe32_data_directory *reloc_dir;
> +  struct grub_pe32_data_directory *sec_dir;
> +  grub_efi_uint64_t number_of_rva_and_sizes;
> +  grub_pe_header_t *pe_hdr;
> +};
> +
> +typedef struct pe_coff_loader_image_context 
> pe_coff_loader_image_context_t;
> +
> +#define EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES 16
> +
>  #endif /* ! GRUB_EFI_PE32_HEADER */
> diff --git a/include/grub/multiboot.h b/include/grub/multiboot.h
> index d8847f7531d3..c6af5c71b4e8 100644
> --- a/include/grub/multiboot.h
> +++ b/include/grub/multiboot.h
> @@ -100,6 +100,9 @@ struct mbi_load_data
>  };
>  typedef struct mbi_load_data mbi_load_data_t;
> 
> +bool
> +grub_multiboot_is_elf (mbi_load_data_t *mld);
> +
>  /* Load ELF32 or ELF64.  */
>  grub_err_t
>  grub_multiboot_load_elf (mbi_load_data_t *mld);
> diff --git a/include/grub/multiboot2.h b/include/grub/multiboot2.h
> index b90aa6989674..6169cb4472a7 100644
> --- a/include/grub/multiboot2.h
> +++ b/include/grub/multiboot2.h
> @@ -92,10 +92,19 @@ struct mbi_load_data
>  };
>  typedef struct mbi_load_data mbi_load_data_t;
> 
> +bool
> +grub_multiboot2_is_elf (mbi_load_data_t *mld);
> +
>  /* Load ELF32 or ELF64.  */
>  grub_err_t
>  grub_multiboot2_load_elf (mbi_load_data_t *mld);
> 
> +grub_err_t
> +grub_multiboot2_load_pe (mbi_load_data_t *mld);
> +
> +bool
> +grub_multiboot2_is_pe (mbi_load_data_t *mld);
> +
>  extern grub_size_t grub_multiboot2_pure_size;
>  extern grub_size_t grub_multiboot2_alloc_mbi;
>  extern grub_uint32_t grub_multiboot2_payload_eip;
> --
> 2.43.0
> 
> 
> 
> 
> ------------------------------
> 
> Subject: Digest Footer
> 
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> https://lists.gnu.org/mailman/listinfo/grub-devel
> 
> 
> ------------------------------
> 
> End of Grub-devel Digest, Vol 241, Issue 28
> *******************************************

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2024-04-01 10:24 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-28 15:12 [PATCH v2 0/3] GRUB: Supporting Secure Boot of xen Ross Lagerwall
2024-03-28 15:12 ` Ross Lagerwall via Grub-devel
2024-03-28 15:13 ` [PATCH v2 1/3] multiboot2: Add support for the PE binary type Ross Lagerwall
2024-03-28 15:13   ` Ross Lagerwall via Grub-devel
2024-03-28 15:13 ` [PATCH v2 2/3] multiboot2: Add PE load support Ross Lagerwall
2024-03-28 15:13   ` Ross Lagerwall via Grub-devel
2024-03-28 15:13 ` [PATCH v2 3/3] efi: Allow loading multiboot modules without verification Ross Lagerwall
2024-03-28 15:13   ` Ross Lagerwall via Grub-devel
     [not found] <mailman.9622.1711638669.7406.grub-devel@gnu.org>
2024-04-01  9:59 ` [PATCH v2 2/3] multiboot2: Add PE load support avnish

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.