All of lore.kernel.org
 help / color / mirror / Atom feed
From: avnish <avnish@imap.linux.ibm.com>
To: grub-devel@gnu.org, ross.lagerwall@citrix.com
Cc: grub-devel-request@gnu.org, dkiper@net-space.pl
Subject: Re: [PATCH v2 2/3] multiboot2: Add PE load support
Date: Mon, 01 Apr 2024 15:29:12 +0530	[thread overview]
Message-ID: <425905b94d81bb40953eacdf57627230@imap.linux.ibm.com> (raw)
In-Reply-To: <mailman.9622.1711638669.7406.grub-devel@gnu.org>

> 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

       reply	other threads:[~2024-04-01 10:24 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <mailman.9622.1711638669.7406.grub-devel@gnu.org>
2024-04-01  9:59 ` avnish [this message]
2024-03-28 15:12 [PATCH v2 0/3] GRUB: Supporting Secure Boot of xen Ross Lagerwall
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=425905b94d81bb40953eacdf57627230@imap.linux.ibm.com \
    --to=avnish@imap.linux.ibm.com \
    --cc=dkiper@net-space.pl \
    --cc=grub-devel-request@gnu.org \
    --cc=grub-devel@gnu.org \
    --cc=ross.lagerwall@citrix.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.