From nobody Mon Feb 9 21:20:42 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1711638678; cv=none; d=zohomail.com; s=zohoarc; b=XXwOmvONJRjOcP6TMxOLoEpxBtbUCnPqhPkIqE35VOJ/STlX1IttIslXICLfpUjmfpSucdB7ynASry3rypeA2gKSABmg6wY5oFREae2ctRfJu7SDvGmMY8f4Siid3ffXZQbqg+36X+44Q+jMRy7JlgtllnixKqRBakVGUfwwXAw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1711638678; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=G9324KJmT9LrWuNsOI70J8iDdayMGHOlIuajPPrTHQg=; b=eYmPKm2cSk8uYNheBM2Q+tG2lDdxq79znfG/DNcZM/u3O5nQ3yjnDr6msqRbD5SmIdQdNKKxJWo2Pz0EO73VLtpOVkc9Gzqn1A56Wksy9sugFmTSB6dMI1OtiByChvjnpjyR1SB7zcNTR50Q0YTQjPb1bFa+JLrgRf30om/BiKU= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1711638678830539.4346956368249; Thu, 28 Mar 2024 08:11:18 -0700 (PDT) Received: from list by lists.xenproject.org with outflank-mailman.698956.1091267 (Exim 4.92) (envelope-from ) id 1rprPD-000263-Te; Thu, 28 Mar 2024 15:11:03 +0000 Received: by outflank-mailman (output) from mailman id 698956.1091267; Thu, 28 Mar 2024 15:11:03 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1rprPD-00025u-Qh; Thu, 28 Mar 2024 15:11:03 +0000 Received: by outflank-mailman (input) for mailman id 698956; Thu, 28 Mar 2024 15:11:01 +0000 Received: from se1-gles-sth1-in.inumbo.com ([159.253.27.254] helo=se1-gles-sth1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1rprPB-0001mV-OV for xen-devel@lists.xenproject.org; Thu, 28 Mar 2024 15:11:01 +0000 Received: from mail-oi1-x22c.google.com (mail-oi1-x22c.google.com [2607:f8b0:4864:20::22c]) by se1-gles-sth1.inumbo.com (Halon) with ESMTPS id 62123545-ed15-11ee-afe3-a90da7624cb6; Thu, 28 Mar 2024 16:10:59 +0100 (CET) Received: by mail-oi1-x22c.google.com with SMTP id 5614622812f47-3c3daebabe4so691997b6e.2 for ; Thu, 28 Mar 2024 08:10:59 -0700 (PDT) Received: from rossla-lxenia.eng.citrite.net ([185.25.67.249]) by smtp.gmail.com with ESMTPSA id cr7-20020a05622a428700b004313f54aaa9sm696300qtb.5.2024.03.28.08.10.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 28 Mar 2024 08:10:57 -0700 (PDT) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 62123545-ed15-11ee-afe3-a90da7624cb6 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=citrix.com; s=google; t=1711638658; x=1712243458; darn=lists.xenproject.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=G9324KJmT9LrWuNsOI70J8iDdayMGHOlIuajPPrTHQg=; b=QhTnawdBpwos6nJaeDRhoX3I0xR7YMKNoc7zAxtzOtOdLTu5j1LPm7NPUddxCtZOl8 1jTYdYFw5WTJzvcLVqo8sQm1tRJFDzudK5MKE1RIzl1NaKiNxDR+gEHPxmkUsy2LXpdM wEUrymLrW46vcfwCNc8AMwOrAGxSdxm33+iMY= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711638658; x=1712243458; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=G9324KJmT9LrWuNsOI70J8iDdayMGHOlIuajPPrTHQg=; b=t9xKdvbRXnpmN6oAd882vzrtJkqGTV7ZpmKeX3X40c++E2EPRnDthRPSgThubRQxB7 Ip3XktrMUEBU6BGs0Gmj1JR0FxQDHdjyOpiCEiGDYS+xmOIGbveJ1qFmQJn6jDHEiKgz Qdu87uvT0EjVdodY0rYvqrhiOyasRSVE8o0Kk6UxDFxJAYGiGNO/oNPfAa39ilNcZahO NFlUzVCPPvk2hUfnezJkP0wFw0xcKvz0+XB0ReKnMogSHhF+vG2WsqLJE8LjszDCG3hR upDCKDakAtXpdu22zQE7unDe6JEogQMY+hSoBiTiOhgdGEZv+p00GWe6mn88bc0w0AQz oYhg== X-Gm-Message-State: AOJu0YyL0Vf37gxjMOC9gUb09a1jxwLQI9bUz8NiZ0AWQ76T4vzSnpgT CnXPNYSO1uRQ+S//9stuO+1w+u8p+TyO1QwghUdzJxpbndTZD9GQriOgQt/8EA== X-Google-Smtp-Source: AGHT+IH83M+cutSzSOy7EFKpOqpOFWmoSh7nMpOmiqpACnDM3dbkrJh6cxtOxpbi9ljRH2TasfwM6w== X-Received: by 2002:a05:6808:291:b0:3c3:d497:b750 with SMTP id z17-20020a056808029100b003c3d497b750mr3064373oic.55.1711638658256; Thu, 28 Mar 2024 08:10:58 -0700 (PDT) From: Ross Lagerwall To: grub-devel@gnu.org Cc: xen-devel@lists.xenproject.org, Ross Lagerwall , Daniel Kiper , Daniel Kiper , Andrew Cooper , =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= Subject: [PATCH v2 2/3] multiboot2: Add PE load support Date: Thu, 28 Mar 2024 15:13:01 +0000 Message-ID: <20240328151302.1451158-3-ross.lagerwall@citrix.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240328151302.1451158-1-ross.lagerwall@citrix.com> References: <20240328151302.1451158-1-ross.lagerwall@citrix.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) X-ZM-MESSAGEID: 1711638679109100001 Content-Type: text/plain; charset="utf-8" Add the ability to load multiboot binaries in PE format. This allows the binaries to be signed and verified. Signed-off-by: Ross Lagerwall --- 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 =3D { =20 common =3D loader/multiboot.c; common =3D loader/multiboot_mbi2.c; + common =3D loader/multiboot_pe.c; enable =3D x86; enable =3D i386_xen_pvh; enable =3D 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 =20 +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 *fi= lename) mld.file =3D file; mld.filename =3D filename; mld.avoid_efi_boot_services =3D keep_bs; - err =3D grub_multiboot2_load_elf (&mld); + if (grub_multiboot2_is_pe (&mld)) + err =3D grub_multiboot2_load_pe (&mld); + else if (grub_multiboot2_is_elf (&mld)) + err =3D grub_multiboot2_load_elf (&mld); + else + { + grub_free (mld.buffer); + return grub_error (GRUB_ERR_UNKNOWN_OS, + "Unknown image type and address tag not speci= fied"); + } if (err) { grub_free (mld.buffer); diff --git a/grub-core/loader/multiboot_pe.c b/grub-core/loader/multiboot_p= e.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 bel= ow: + * + * 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 + * + * Modifications: + * + * Copyright (c) 2024. Cloud Software Group, Inc. + */ + +#include +#include +#include + +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 =3D=3D GRUB_PE32_PE64_MAGI= C; +} + +static const grub_uint16_t machine_type __attribute__((__unused__)) =3D +#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 =3D=3D 32) + GRUB_PE32_MACHINE_RISCV32; +#elif defined(__riscv) && (__riscv_xlen =3D=3D 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 =3D=3D machine_type || + (pe_hdr->pe32.coff_header.machine =3D=3D GRUB_PE32_MACHINE_X86= _64 && + machine_type =3D=3D GRUB_PE32_MACHINE_I386)) && + (pe_hdr->pe32plus.optional_header.magic =3D=3D GRUB_PE32_PE32_M= AGIC || + pe_hdr->pe32plus.optional_header.magic =3D=3D GRUB_PE32_PE64_M= AGIC); +} + +/* + * 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 =3D data; + grub_pe_header_t *pe_hdr =3D data; + unsigned long header_without_data_dir, section_header_offset, opt_header= _size; + unsigned long file_alignment =3D 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 =3D=3D GRUB_PE32_MAGIC) + pe_hdr =3D (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 =3D pe_hdr->pe32plus.optional_heade= r.num_data_directories; + context->size_of_headers =3D pe_hdr->pe32plus.optional_header.header= _size; + context->image_size =3D pe_hdr->pe32plus.optional_header.image_size; + context->section_alignment =3D pe_hdr->pe32plus.optional_header.sect= ion_alignment; + file_alignment =3D pe_hdr->pe32plus.optional_header.file_alignment; + opt_header_size =3D sizeof(struct grub_pe64_optional_header); + } + else + { + context->number_of_rva_and_sizes =3D pe_hdr->pe32.optional_header.nu= m_data_directories; + context->size_of_headers =3D pe_hdr->pe32.optional_header.header_siz= e; + context->image_size =3D (grub_uint64_t)pe_hdr->pe32.optional_header.= image_size; + context->section_alignment =3D pe_hdr->pe32.optional_header.section_= alignment; + file_alignment =3D pe_hdr->pe32.optional_header.file_alignment; + opt_header_size =3D sizeof(struct grub_pe32_optional_header); + } + + if (file_alignment % 2 !=3D 0) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "File Alignment is invalid (%ld)"= , file_alignment); + return GRUB_ERR_OUT_OF_RANGE; + } + if (file_alignment =3D=3D 0) + file_alignment =3D 0x200; + if (context->section_alignment =3D=3D 0) + context->section_alignment =3D GRUB_EFI_PAGE_SIZE; + if (context->section_alignment < file_alignment) + context->section_alignment =3D file_alignment; + + context->number_of_sections =3D pe_hdr->pe32.coff_header.num_sections; + + if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < context->number_of_rva_and_s= izes) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "Image header too small"); + return GRUB_ERR_OUT_OF_RANGE; + } + + header_without_data_dir =3D opt_header_size + - sizeof (struct grub_pe32_data_directory) * EFI_IMAGE_N= UMBER_OF_DIRECTORY_ENTRIES; + if (((grub_uint32_t)pe_hdr->pe32.coff_header.optional_header_size - head= er_without_data_dir) !=3D + context->number_of_rva_and_sizes * sizeof (struct grub_p= e32_data_directory)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "Image header overflows data dire= ctory"); + return GRUB_ERR_OUT_OF_RANGE; + } + + section_header_offset =3D 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) / sizeo= f(struct grub_pe32_section_table) + <=3D context->number_of_sections) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "Image sections overflow image si= ze"); + return GRUB_ERR_OUT_OF_RANGE; + } + + if ((context->size_of_headers - section_header_offset) / sizeof(struct g= rub_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_he= ader_t)) > datasize) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid image"); + return GRUB_ERR_OUT_OF_RANGE; + } + + if (pe_hdr->pe32.signature[0] !=3D 'P' || + pe_hdr->pe32.signature[1] !=3D 'E' || + pe_hdr->pe32.signature[2] !=3D 0x00 || + pe_hdr->pe32.signature[3] !=3D 0x00) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "Unsupported image type"); + return GRUB_ERR_NOT_IMPLEMENTED_YET; + } + + context->pe_hdr =3D pe_hdr; + + if (image_is_64_bit(pe_hdr)) + { + context->image_address =3D pe_hdr->pe32plus.optional_header.image_ba= se; + context->entry_point =3D pe_hdr->pe32plus.optional_header.entry_addr; + context->reloc_dir =3D &pe_hdr->pe32plus.optional_header.base_reloca= tion_table; + context->sec_dir =3D &pe_hdr->pe32plus.optional_header.certificate_t= able; + } + else + { + context->image_address =3D pe_hdr->pe32.optional_header.image_base; + context->entry_point =3D pe_hdr->pe32.optional_header.entry_addr; + context->reloc_dir =3D &pe_hdr->pe32.optional_header.base_relocation= _table; + context->sec_dir =3D &pe_hdr->pe32.optional_header.certificate_table; + } + + context->first_section =3D (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 *)d= ata) > + (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 add= r) +{ + 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 =3D 0; + grub_err_t ret =3D GRUB_ERR_NONE; + + if (image_is_64_bit (context->pe_hdr)) + context->pe_hdr->pe32plus.optional_header.image_base =3D + (grub_uint64_t)(unsigned long)phys_addr; + else + context->pe_hdr->pe32.optional_header.image_base =3D + (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 ima= ge + * 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 =3D grub_malloc (section->virtual_size); + if (!reloc_buffer) + return grub_errno; + + if (grub_file_seek (mld->file, section->raw_data_offset) =3D=3D (grub_of= f_t) -1) + { + ret =3D grub_errno; + goto out; + } + + if (grub_file_read (mld->file, reloc_buffer, section->virtual_size) + !=3D (grub_ssize_t) section->virtual_size) + { + if (!grub_errno) + { + grub_error (GRUB_ERR_FILE_READ_ERROR, "premature end of file %s", + mld->filename); + ret =3D GRUB_ERR_FILE_READ_ERROR; + } + else + ret =3D grub_errno; + goto out; + } + + reloc_base =3D (struct grub_pe32_data_directory *)reloc_buffer; + reloc_base_end =3D (struct grub_pe32_data_directory *)(reloc_buffer + se= ction->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 =3D (grub_uint64_t)(grub_efi_uintn_t)phys_addr - context->image_a= ddress; + if (adjust =3D=3D 0) + return GRUB_ERR_NONE; + + while (reloc_base < reloc_base_end) + { + grub_uint16_t *entry; + reloc =3D (struct grub_pe32_fixup_block *)((char*)reloc_base); + + if ((reloc_base->size =3D=3D 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 =3D GRUB_ERR_BAD_ARGUMENT; + goto out; + } + + entry =3D &reloc->entries[0]; + reloc_end =3D (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_siz= e)) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc entry %d overflows bin= ary", + n); + return GRUB_ERR_BAD_ARGUMENT; + } + + fixup_base =3D virt_addr + reloc_base->rva; + if (!fixup_base) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc %d Invalid fixupbase",= n); + ret =3D GRUB_ERR_BAD_ARGUMENT; + goto out; + } + + while ((void *)entry < (void *)reloc_end) + { + fixup =3D fixup_base + (*entry & 0xFFF); + switch ((*entry) >> 12) + { + case GRUB_PE32_REL_BASED_ABSOLUTE: + break; + case GRUB_PE32_REL_BASED_HIGH: + fixup_16 =3D (grub_uint16_t *)fixup; + *fixup_16 =3D (grub_uint16_t) + (*fixup_16 + ((grub_uint16_t)((grub_uint32_t)adjust >> 1= 6))); + break; + case GRUB_PE32_REL_BASED_LOW: + fixup_16 =3D (grub_uint16_t *)fixup; + *fixup_16 =3D (grub_uint16_t) (*fixup_16 + (grub_uint16_t)= adjust); + break; + case GRUB_PE32_REL_BASED_HIGHLOW: + fixup_32 =3D (grub_uint32_t *)fixup; + *fixup_32 =3D *fixup_32 + (grub_uint32_t)adjust; + break; + case GRUB_PE32_REL_BASED_DIR64: + fixup_64 =3D (grub_uint64_t *)fixup; + *fixup_64 =3D *fixup_64 + (grub_uint64_t)adjust; + break; + default: + grub_error (GRUB_ERR_BAD_ARGUMENT, + "Reloc %d unknown relocation type %d", + n, (*entry) >> 12); + ret =3D GRUB_ERR_BAD_ARGUMENT; + goto out; + } + entry +=3D 1; + } + reloc_base =3D (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 =3D NULL, fake_reloc_secti= on; + struct grub_pe32_section_table *section; + grub_relocator_chunk_t ch; + pe_coff_loader_image_context_t context; + grub_err_t ret =3D GRUB_ERR_NONE; + grub_uint32_t section_alignment; + int found_entry_point =3D 0; + + ret =3D read_header(mld->buffer, MULTIBOOT_SEARCH, &context); + if (ret) + return ret; + + /* + * The spec says, uselessly, of SectionAlignment: + * =3D=3D=3D=3D=3D + * 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. + * =3D=3D=3D=3D=3D + * 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 =3D context.section_alignment; + if (section_alignment =3D=3D 0) + section_alignment =3D 4096; + + section_alignment =3D grub_max(section_alignment, mld->align); + + ret =3D grub_relocator_alloc_chunk_align_safe (grub_multiboot2_relocator= , &ch, + mld->min_addr, mld->max_add= r, + context.image_size, section= _alignment, + mld->preference, mld->avoid= _efi_boot_services); + + if (ret) + { + grub_dprintf ("multiboot_loader", "Cannot allocate memory for OS ima= ge\n"); + return ret; + } + + virt_addr =3D get_virtual_current_address (ch); + phys_addr =3D get_physical_target_address (ch); + + if (grub_file_seek (mld->file, 0) =3D=3D (grub_off_t) -1) { + ret =3D grub_errno; + goto out; + } + + if (grub_file_read (mld->file, virt_addr, context.size_of_headers) + !=3D (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 =3D GRUB_ERR_FILE_READ_ERROR; + } + else + ret =3D grub_errno; + goto out; + } + + mld->load_base_addr =3D phys_addr; + mld->link_base_addr =3D 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 =3D context.entry_point; + grub_dprintf ("multiboot_loader", "entry_point: 0x%08x\n", grub_multiboo= t2_payload_eip); + if (!grub_multiboot2_payload_eip) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid entry point"); + ret =3D 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 =3D 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 =3D 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 =3D context.first_section; + for (i =3D 0; i < context.number_of_sections; i++, section++) + { + char name[9]; + + base =3D image_address (phys_addr, context.image_size, + section->virtual_address); + end =3D image_address (phys_addr, context.image_size, + section->virtual_address + section->virtual_siz= e -1); + + grub_strncpy(name, section->name, 9); + name[8] =3D '\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 =3D GRUB_ERR_BAD_ARGUMENT; + goto out; + } + + if (section->virtual_address <=3D 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) =3D=3D 0) + { + if (reloc_section) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, + "Image has multiple relocation sections"); + ret =3D 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 =3D=3D base) + { + if (reloc_base_end =3D=3D end) + { + grub_dprintf ("multiboot_loader", " section is relocatio= n section\n"); + reloc_section =3D 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 *secti= on); + fake_reloc_section.virtual_size -=3D (end - reloc_base_e= nd); + reloc_section =3D &fake_reloc_section; + } + } + + if (!reloc_section) + { + grub_dprintf ("multiboot_loader", " section is not reloc sec= tion?\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%0= 16lx\n", base, end); + grub_dprintf ("multiboot_loader", " reloc_base: 0x%016lx rel= oc_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 =3D GRUB_ERR_BAD_ARGUMENT; + goto out; + } + + if (section->characteristics & GRUB_PE32_SCN_CNT_UNINITIALIZED_DATA) + { + if (section->raw_data_size !=3D 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 =3D GRUB_ERR_BAD_ARGUMENT; + goto out; + } + + if (section->raw_data_size > 0) + { + grub_dprintf ("multiboot_loader", " copying 0x%08x bytes to 0x%0= 16lx\n", + section->raw_data_size, base); + + if (grub_file_seek (mld->file, section->raw_data_offset) =3D=3D = (grub_off_t) -1) + { + ret =3D grub_errno; + goto out; + } + + if (grub_file_read (mld->file, virt_addr + (base - phys_addr), s= ection->raw_data_size) + !=3D (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 =3D GRUB_ERR_FILE_READ_ERROR; + } + else + ret =3D 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 =3D=3D EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC */ + if (context.number_of_rva_and_sizes <=3D 5) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "image has no relocation entry\n"= ); + ret =3D GRUB_ERR_BAD_ARGUMENT; + goto out; + } + + if (context.reloc_dir->size && reloc_section) + { + /* run the relocation fixups */ + ret =3D 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 sectio= ns"); + ret =3D 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 =3D 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 =3D mld->buffer; + + return dos_hdr->e_magic =3D=3D 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 =20 #include +#include #include =20 /* The MSDOS compatibility stub. This was copied from the output of @@ -46,6 +47,28 @@ =20 #define GRUB_PE32_MSDOS_STUB_SIZE 0x80 =20 +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 =20 struct grub_msdos_image_header @@ -249,6 +272,7 @@ struct grub_pe32_section_table =20 #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 =20 +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; =20 +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; =20 +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); =20 +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; --=20 2.43.0