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
next parent reply other threads:[~2024-04-01 10:24 UTC|newest]
Thread overview: 2+ 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 via Grub-devel
2024-03-28 15:13 ` [PATCH v2 2/3] multiboot2: Add PE load support 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).