From b7a175741808794002537f24c1c911587b8967ed Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 11 Jun 2021 12:10:45 +0200 Subject: [PATCH 001/367] Revert "templates: Fix user-facing typo with an incorrect use of "it's"" This reverts commit 722737630889607c3b5761f1f5a48f1674cd2821. --- util/grub.d/30_os-prober.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in index 5984e92d29..9462248128 100644 --- a/util/grub.d/30_os-prober.in +++ b/util/grub.d/30_os-prober.in @@ -36,7 +36,7 @@ if ! command -v os-prober > /dev/null || ! command -v linux-boot-prober > /dev/n exit 0 fi -grub_warn "$(gettext_printf "os-prober will be executed to detect other bootable partitions.\nIts output will be used to detect bootable binaries on them and create new boot entries.")" +grub_warn "$(gettext_printf "os-prober will be executed to detect other bootable partitions.\nIt's output will be used to detect bootable binaries on them and create new boot entries.")" OSPROBED="`os-prober | tr ' ' '^' | paste -s -d ' '`" if [ -z "${OSPROBED}" ] ; then From f9128222b0301d0955abf22567d2ace39f0757ce Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 11 Jun 2021 12:10:54 +0200 Subject: [PATCH 002/367] Revert "templates: Properly disable the os-prober by default" This reverts commit 54e0a1bbf1e9106901a557195bb35e5e20fb3925. --- util/grub-mkconfig.in | 5 +---- util/grub.d/30_os-prober.in | 8 ++++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index f8cbb8d7a2..d3e879b8e5 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -140,9 +140,6 @@ GRUB_DEVICE_PARTUUID="`${grub_probe} --device ${GRUB_DEVICE} --target=partuuid 2 GRUB_DEVICE_BOOT="`${grub_probe} --target=device /boot`" GRUB_DEVICE_BOOT_UUID="`${grub_probe} --device ${GRUB_DEVICE_BOOT} --target=fs_uuid 2> /dev/null`" || true -# Disable os-prober by default due to security reasons. -GRUB_DISABLE_OS_PROBER="true" - # Filesystem for the device containing our userland. Used for stuff like # choosing Hurd filesystem module. GRUB_FS="`${grub_probe} --device ${GRUB_DEVICE} --target=fs 2> /dev/null || echo unknown`" @@ -204,7 +201,6 @@ export GRUB_DEVICE \ GRUB_DEVICE_PARTUUID \ GRUB_DEVICE_BOOT \ GRUB_DEVICE_BOOT_UUID \ - GRUB_DISABLE_OS_PROBER \ GRUB_FS \ GRUB_FONT \ GRUB_PRELOAD_MODULES \ @@ -246,6 +242,7 @@ export GRUB_DEFAULT \ GRUB_BACKGROUND \ GRUB_THEME \ GRUB_GFXPAYLOAD_LINUX \ + GRUB_DISABLE_OS_PROBER \ GRUB_INIT_TUNE \ GRUB_SAVEDEFAULT \ GRUB_ENABLE_CRYPTODISK \ diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in index 9462248128..80685b15f4 100644 --- a/util/grub.d/30_os-prober.in +++ b/util/grub.d/30_os-prober.in @@ -26,8 +26,8 @@ export TEXTDOMAINDIR="@localedir@" . "$pkgdatadir/grub-mkconfig_lib" -if [ "x${GRUB_DISABLE_OS_PROBER}" = "xtrue" ]; then - grub_warn "$(gettext_printf "os-prober will not be executed to detect other bootable partitions.\nSystems on them will not be added to the GRUB boot configuration.\nCheck GRUB_DISABLE_OS_PROBER documentation entry.")" +if [ "x${GRUB_DISABLE_OS_PROBER}" = "xfalse" ]; then + gettext_printf "os-prober will not be executed to detect other bootable partitions.\nSystems on them will not be added to the GRUB boot configuration.\nCheck GRUB_DISABLE_OS_PROBER documentation entry.\n" exit 0 fi @@ -36,12 +36,12 @@ if ! command -v os-prober > /dev/null || ! command -v linux-boot-prober > /dev/n exit 0 fi -grub_warn "$(gettext_printf "os-prober will be executed to detect other bootable partitions.\nIt's output will be used to detect bootable binaries on them and create new boot entries.")" - OSPROBED="`os-prober | tr ' ' '^' | paste -s -d ' '`" if [ -z "${OSPROBED}" ] ; then # empty os-prober output, nothing doing exit 0 +else + grub_warn "$(gettext_printf "os-prober was executed to detect other bootable partitions.\nIt's output will be used to detect bootable binaries on them and create new boot entries.")" fi osx_entry() { From 1d6bfd60421dc7c49c09830ae426fd9f74cfcd0b Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 11 Jun 2021 12:10:58 +0200 Subject: [PATCH 003/367] Revert "templates: Disable the os-prober by default" This reverts commit e346414725a70e5c74ee87ca14e580c66f517666. --- docs/grub.texi | 18 ++++++++---------- util/grub.d/30_os-prober.in | 5 +---- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index f8b4b3b21a..69f08d289f 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -1519,13 +1519,10 @@ boot sequence. If you have problems, set this option to @samp{text} and GRUB will tell Linux to boot in normal text mode. @item GRUB_DISABLE_OS_PROBER -The @command{grub-mkconfig} has a feature to use the external -@command{os-prober} program to discover other operating systems installed on -the same machine and generate appropriate menu entries for them. It is disabled -by default since automatic and silent execution of @command{os-prober}, and -creating boot entries based on that data, is a potential attack vector. Set -this option to @samp{false} to enable this feature in the -@command{grub-mkconfig} command. +Normally, @command{grub-mkconfig} will try to use the external +@command{os-prober} program, if installed, to discover other operating +systems installed on the same system and generate appropriate menu entries +for them. Set this option to @samp{true} to disable this. @item GRUB_OS_PROBER_SKIP_LIST List of space-separated FS UUIDs of filesystems to be ignored from os-prober @@ -1853,9 +1850,10 @@ than zero; otherwise 0. @section Multi-boot manual config Currently autogenerating config files for multi-boot environments depends on -os-prober and has several shortcomings. Due to that it is disabled by default. -It is advised to use the power of GRUB syntax and do it yourself. A possible -configuration is detailed here, feel free to adjust to your needs. +os-prober and has several shortcomings. While fixing it is scheduled for the +next release, meanwhile you can make use of the power of GRUB syntax and do it +yourself. A possible configuration is detailed here, feel free to adjust to your +needs. First create a separate GRUB partition, big enough to hold GRUB. Some of the following entries show how to load OS installer images from this same partition, diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in index 80685b15f4..1b91c102f3 100644 --- a/util/grub.d/30_os-prober.in +++ b/util/grub.d/30_os-prober.in @@ -26,8 +26,7 @@ export TEXTDOMAINDIR="@localedir@" . "$pkgdatadir/grub-mkconfig_lib" -if [ "x${GRUB_DISABLE_OS_PROBER}" = "xfalse" ]; then - gettext_printf "os-prober will not be executed to detect other bootable partitions.\nSystems on them will not be added to the GRUB boot configuration.\nCheck GRUB_DISABLE_OS_PROBER documentation entry.\n" +if [ "x${GRUB_DISABLE_OS_PROBER}" = "xtrue" ]; then exit 0 fi @@ -40,8 +39,6 @@ OSPROBED="`os-prober | tr ' ' '^' | paste -s -d ' '`" if [ -z "${OSPROBED}" ] ; then # empty os-prober output, nothing doing exit 0 -else - grub_warn "$(gettext_printf "os-prober was executed to detect other bootable partitions.\nIt's output will be used to detect bootable binaries on them and create new boot entries.")" fi osx_entry() { From 8e07346957d26fb76f424cf58667b45121d5fb5a Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 10 Jul 2012 11:58:52 -0400 Subject: [PATCH 004/367] Add support for Linux EFI stub loading. Also: commit 71c843745f22f81e16d259e2e19c99bf3c1855c1 Author: Colin Watson Date: Tue Oct 23 10:40:49 2012 -0400 Don't allow insmod when secure boot is enabled. Hi, Fedora's patch to forbid insmod in UEFI Secure Boot environments is fine as far as it goes. However, the insmod command is not the only way that modules can be loaded. In particular, the 'normal' command, which implements the usual GRUB menu and the fully-featured command prompt, will implicitly load commands not currently loaded into memory. This permits trivial Secure Boot violations by writing commands implementing whatever you want to do and pointing $prefix at the malicious code. I'm currently test-building this patch (replacing your current grub-2.00-no-insmod-on-sb.patch), but this should be more correct. It moves the check into grub_dl_load_file. --- grub-core/Makefile.core.def | 16 +- grub-core/kern/dl.c | 21 ++ grub-core/kern/efi/efi.c | 28 +++ grub-core/kern/efi/mm.c | 32 +++ grub-core/loader/arm64/linux.c | 112 +++++----- grub-core/loader/arm64/xen_boot.c | 1 - grub-core/loader/efi/linux.c | 70 +++++++ grub-core/loader/i386/efi/linux.c | 335 ++++++++++++++++++++++++++++++ grub-core/loader/i386/pc/linux.c | 10 +- include/grub/arm/linux.h | 9 + include/grub/arm64/linux.h | 9 + include/grub/efi/efi.h | 7 +- include/grub/efi/linux.h | 31 +++ 13 files changed, 615 insertions(+), 66 deletions(-) create mode 100644 grub-core/loader/efi/linux.c create mode 100644 grub-core/loader/i386/efi/linux.c create mode 100644 include/grub/efi/linux.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 8022e1c0a7..45d3edaa4d 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1734,13 +1734,6 @@ module = { enable = i386_pc; }; - -module = { - name = linux16; - common = loader/i386/pc/linux.c; - enable = x86; -}; - module = { name = ntldr; i386_pc = loader/i386/pc/ntldr.c; @@ -1796,7 +1789,9 @@ module = { module = { name = linux; - x86 = loader/i386/linux.c; + i386_pc = loader/i386/pc/linux.c; + x86_64_efi = loader/i386/efi/linux.c; + i386_efi = loader/i386/efi/linux.c; i386_xen_pvh = loader/i386/linux.c; xen = loader/i386/xen.c; i386_pc = lib/i386/pc/vesa_modes_table.c; @@ -1811,9 +1806,14 @@ module = { arm64 = loader/arm64/linux.c; riscv32 = loader/riscv/linux.c; riscv64 = loader/riscv/linux.c; + emu = loader/emu/linux.c; + fdt = lib/fdt.c; + common = loader/linux.c; common = lib/cmdline.c; enable = noemu; + + efi = loader/efi/linux.c; }; module = { diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index 48f8a79073..b714937095 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -38,6 +38,14 @@ #define GRUB_MODULES_MACHINE_READONLY #endif +#ifdef GRUB_MACHINE_EMU +#include +#endif + +#ifdef GRUB_MACHINE_EFI +#include +#endif + #pragma GCC diagnostic ignored "-Wcast-align" @@ -695,6 +703,19 @@ grub_dl_load_file (const char *filename) void *core = 0; grub_dl_t mod = 0; +#ifdef GRUB_MACHINE_EFI + if (grub_efi_secure_boot ()) + { +#if 0 + /* This is an error, but grub2-mkconfig still generates a pile of + * insmod commands, so emitting it would be mostly just obnoxious. */ + grub_error (GRUB_ERR_ACCESS_DENIED, + "Secure Boot forbids loading module from %s", filename); +#endif + return 0; + } +#endif + grub_boot_time ("Loading module %s", filename); file = grub_file_open (filename, GRUB_FILE_TYPE_GRUB_MODULE); diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index 8cff7be028..35b8f67060 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -286,6 +286,34 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid, return grub_efi_get_variable_with_attributes (var, guid, datasize_out, data_out, NULL); } +grub_efi_boolean_t +grub_efi_secure_boot (void) +{ + grub_efi_guid_t efi_var_guid = GRUB_EFI_GLOBAL_VARIABLE_GUID; + grub_size_t datasize; + char *secure_boot = NULL; + char *setup_mode = NULL; + grub_efi_boolean_t ret = 0; + + secure_boot = grub_efi_get_variable("SecureBoot", &efi_var_guid, &datasize); + + if (datasize != 1 || !secure_boot) + goto out; + + setup_mode = grub_efi_get_variable("SetupMode", &efi_var_guid, &datasize); + + if (datasize != 1 || !setup_mode) + goto out; + + if (*secure_boot && !*setup_mode) + ret = 1; + + out: + grub_free (secure_boot); + grub_free (setup_mode); + return ret; +} + #pragma GCC diagnostic ignored "-Wcast-align" /* Search the mods section from the PE32/PE32+ image. This code uses diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index 9838fb2f50..f6aef0ef64 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -113,6 +113,38 @@ grub_efi_drop_alloc (grub_efi_physical_address_t address, } } +/* Allocate pages below a specified address */ +void * +grub_efi_allocate_pages_max (grub_efi_physical_address_t max, + grub_efi_uintn_t pages) +{ + grub_efi_status_t status; + grub_efi_boot_services_t *b; + grub_efi_physical_address_t address = max; + + if (max > 0xffffffff) + return 0; + + b = grub_efi_system_table->boot_services; + status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS, GRUB_EFI_LOADER_DATA, pages, &address); + + if (status != GRUB_EFI_SUCCESS) + return 0; + + if (address == 0) + { + /* Uggh, the address 0 was allocated... This is too annoying, + so reallocate another one. */ + address = max; + status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS, GRUB_EFI_LOADER_DATA, pages, &address); + grub_efi_free_pages (0, pages); + if (status != GRUB_EFI_SUCCESS) + return 0; + } + + return (void *) ((grub_addr_t) address); +} + /* Allocate pages. Return the pointer to the first of allocated pages. */ void * grub_efi_allocate_pages_real (grub_efi_physical_address_t address, diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c index ef3e9f9444..a312c66868 100644 --- a/grub-core/loader/arm64/linux.c +++ b/grub-core/loader/arm64/linux.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ static int loaded; static void *kernel_addr; static grub_uint64_t kernel_size; +static grub_uint32_t handover_offset; static char *linux_args; static grub_uint32_t cmdline_size; @@ -67,7 +69,8 @@ grub_arch_efi_linux_check_image (struct linux_arch_kernel_header * lh) static grub_err_t finalize_params_linux (void) { - int node, retval; + grub_efi_loaded_image_t *loaded_image = NULL; + int node, retval, len; void *fdt; @@ -102,6 +105,25 @@ finalize_params_linux (void) if (grub_fdt_install() != GRUB_ERR_NONE) goto failure; + grub_dprintf ("linux", "Installed/updated FDT configuration table @ %p\n", + fdt); + + /* Convert command line to UCS-2 */ + loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle); + if (!loaded_image) + goto failure; + + loaded_image->load_options_size = len = + (grub_strlen (linux_args) + 1) * sizeof (grub_efi_char16_t); + loaded_image->load_options = + grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size)); + if (!loaded_image->load_options) + return grub_error(GRUB_ERR_BAD_OS, "failed to create kernel parameters"); + + loaded_image->load_options_size = + 2 * grub_utf8_to_utf16 (loaded_image->load_options, len, + (grub_uint8_t *) linux_args, len, NULL); + return GRUB_ERR_NONE; failure: @@ -109,72 +131,44 @@ finalize_params_linux (void) return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT"); } -grub_err_t -grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args) +static void +free_params (void) { - grub_efi_memory_mapped_device_path_t *mempath; - grub_efi_handle_t image_handle; - grub_efi_boot_services_t *b; - grub_efi_status_t status; - grub_efi_loaded_image_t *loaded_image; - int len; - - mempath = grub_malloc (2 * sizeof (grub_efi_memory_mapped_device_path_t)); - if (!mempath) - return grub_errno; - - mempath[0].header.type = GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE; - mempath[0].header.subtype = GRUB_EFI_MEMORY_MAPPED_DEVICE_PATH_SUBTYPE; - mempath[0].header.length = grub_cpu_to_le16_compile_time (sizeof (*mempath)); - mempath[0].memory_type = GRUB_EFI_LOADER_DATA; - mempath[0].start_address = addr; - mempath[0].end_address = addr + size; + grub_efi_loaded_image_t *loaded_image = NULL; - mempath[1].header.type = GRUB_EFI_END_DEVICE_PATH_TYPE; - mempath[1].header.subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; - mempath[1].header.length = sizeof (grub_efi_device_path_t); - - b = grub_efi_system_table->boot_services; - status = b->load_image (0, grub_efi_image_handle, - (grub_efi_device_path_t *) mempath, - (void *) addr, size, &image_handle); - if (status != GRUB_EFI_SUCCESS) - return grub_error (GRUB_ERR_BAD_OS, "cannot load image"); + loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle); + if (loaded_image) + { + if (loaded_image->load_options) + grub_efi_free_pages ((grub_efi_physical_address_t)(grub_efi_uintn_t)loaded_image->load_options, + GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size)); + loaded_image->load_options = NULL; + loaded_image->load_options_size = 0; + } +} - grub_dprintf ("linux", "linux command line: '%s'\n", args); +grub_err_t +grub_arch_efi_linux_boot_image (grub_addr_t addr, char *args) +{ + grub_err_t retval; - /* Convert command line to UCS-2 */ - loaded_image = grub_efi_get_loaded_image (image_handle); - loaded_image->load_options_size = len = - (grub_strlen (args) + 1) * sizeof (grub_efi_char16_t); - loaded_image->load_options = - grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size)); - if (!loaded_image->load_options) + retval = finalize_params_linux (); + if (retval != GRUB_ERR_NONE) return grub_errno; - loaded_image->load_options_size = - 2 * grub_utf8_to_utf16 (loaded_image->load_options, len, - (grub_uint8_t *) args, len, NULL); - - grub_dprintf ("linux", "starting image %p\n", image_handle); - status = b->start_image (image_handle, 0, NULL); + grub_dprintf ("linux", "linux command line: '%s'\n", args); - /* When successful, not reached */ - b->unload_image (image_handle); - grub_efi_free_pages ((grub_addr_t) loaded_image->load_options, - GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size)); + retval = grub_efi_linux_boot ((char *)addr, handover_offset, (void *)addr); - return grub_errno; + /* Never reached... */ + free_params(); + return retval; } static grub_err_t grub_linux_boot (void) { - if (finalize_params_linux () != GRUB_ERR_NONE) - return grub_errno; - - return (grub_arch_efi_linux_boot_image((grub_addr_t)kernel_addr, - kernel_size, linux_args)); + return (grub_arch_efi_linux_boot_image((grub_addr_t)kernel_addr, linux_args)); } static grub_err_t @@ -288,6 +282,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), { grub_file_t file = 0; struct linux_arch_kernel_header lh; + struct grub_armxx_linux_pe_header *pe; grub_err_t err; grub_dl_ref (my_mod); @@ -333,6 +328,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_dprintf ("linux", "kernel @ %p\n", kernel_addr); + if (!grub_linuxefi_secure_validate (kernel_addr, kernel_size)) + { + grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), argv[0]); + goto fail; + } + + pe = (void *)((unsigned long)kernel_addr + lh.hdr_offset); + handover_offset = pe->opt.entry_addr; + cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE); linux_args = grub_malloc (cmdline_size); if (!linux_args) diff --git a/grub-core/loader/arm64/xen_boot.c b/grub-core/loader/arm64/xen_boot.c index 22cc25eccd..d9b7a9ba40 100644 --- a/grub-core/loader/arm64/xen_boot.c +++ b/grub-core/loader/arm64/xen_boot.c @@ -266,7 +266,6 @@ xen_boot (void) return err; return grub_arch_efi_linux_boot_image (xen_hypervisor->start, - xen_hypervisor->size, xen_hypervisor->cmdline); } diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c new file mode 100644 index 0000000000..c24202a5dd --- /dev/null +++ b/grub-core/loader/efi/linux.c @@ -0,0 +1,70 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2014 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define SHIM_LOCK_GUID \ + { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} } + +struct grub_efi_shim_lock +{ + grub_efi_status_t (*verify) (void *buffer, grub_uint32_t size); +}; +typedef struct grub_efi_shim_lock grub_efi_shim_lock_t; + +grub_efi_boolean_t +grub_linuxefi_secure_validate (void *data, grub_uint32_t size) +{ + grub_efi_guid_t guid = SHIM_LOCK_GUID; + grub_efi_shim_lock_t *shim_lock; + + shim_lock = grub_efi_locate_protocol(&guid, NULL); + + if (!shim_lock) + return 1; + + if (shim_lock->verify(data, size) == GRUB_EFI_SUCCESS) + return 1; + + return 0; +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + +typedef void (*handover_func) (void *, grub_efi_system_table_t *, void *); + +grub_err_t +grub_efi_linux_boot (void *kernel_addr, grub_off_t offset, + void *kernel_params) +{ + handover_func hf; + + hf = (handover_func)((char *)kernel_addr + offset); + hf (grub_efi_image_handle, grub_efi_system_table, kernel_params); + + return GRUB_ERR_BUG; +} + +#pragma GCC diagnostic pop diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c new file mode 100644 index 0000000000..bb2616a809 --- /dev/null +++ b/grub-core/loader/i386/efi/linux.c @@ -0,0 +1,335 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2012 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_dl_t my_mod; +static int loaded; +static void *kernel_mem; +static grub_uint64_t kernel_size; +static grub_uint8_t *initrd_mem; +static grub_uint32_t handover_offset; +struct linux_kernel_params *params; +static char *linux_cmdline; + +#define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12) + +static grub_err_t +grub_linuxefi_boot (void) +{ + int offset = 0; + +#ifdef __x86_64__ + offset = 512; +#endif + asm volatile ("cli"); + + return grub_efi_linux_boot ((char *)kernel_mem, handover_offset + offset, + params); +} + +static grub_err_t +grub_linuxefi_unload (void) +{ + grub_dl_unref (my_mod); + loaded = 0; + if (initrd_mem) + grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)initrd_mem, + BYTES_TO_PAGES(params->ramdisk_size)); + if (linux_cmdline) + grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t) + linux_cmdline, + BYTES_TO_PAGES(params->cmdline_size + 1)); + if (kernel_mem) + grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)kernel_mem, + BYTES_TO_PAGES(kernel_size)); + if (params) + grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)params, + BYTES_TO_PAGES(16384)); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t *files = 0; + int i, nfiles = 0; + grub_size_t size = 0; + grub_uint8_t *ptr; + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + goto fail; + } + + if (!loaded) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first")); + goto fail; + } + + files = grub_zalloc (argc * sizeof (files[0])); + if (!files) + goto fail; + + for (i = 0; i < argc; i++) + { + files[i] = grub_file_open (argv[i], GRUB_FILE_TYPE_LINUX_INITRD | GRUB_FILE_TYPE_NO_DECOMPRESS); + if (! files[i]) + goto fail; + nfiles++; + size += ALIGN_UP (grub_file_size (files[i]), 4); + } + + initrd_mem = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(size)); + if (!initrd_mem) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate initrd")); + goto fail; + } + + params->ramdisk_size = size; + params->ramdisk_image = (grub_uint32_t)(grub_addr_t) initrd_mem; + + ptr = initrd_mem; + + for (i = 0; i < nfiles; i++) + { + grub_ssize_t cursize = grub_file_size (files[i]); + if (grub_file_read (files[i], ptr, cursize) != cursize) + { + if (!grub_errno) + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), + argv[i]); + goto fail; + } + ptr += cursize; + grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4)); + ptr += ALIGN_UP_OVERHEAD (cursize, 4); + } + + params->ramdisk_size = size; + + fail: + for (i = 0; i < nfiles; i++) + grub_file_close (files[i]); + grub_free (files); + + if (initrd_mem && grub_errno) + grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)initrd_mem, + BYTES_TO_PAGES(size)); + + return grub_errno; +} + +static grub_err_t +grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file = 0; + struct linux_i386_kernel_header lh; + grub_ssize_t len, start, filelen; + void *kernel = NULL; + + grub_dl_ref (my_mod); + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + goto fail; + } + + file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL); + if (! file) + goto fail; + + filelen = grub_file_size (file); + + kernel = grub_malloc(filelen); + + if (!kernel) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer")); + goto fail; + } + + if (grub_file_read (file, kernel, filelen) != filelen) + { + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"), argv[0]); + goto fail; + } + + if (! grub_linuxefi_secure_validate (kernel, filelen)) + { + grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), + argv[0]); + goto fail; + } + + params = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(16384)); + + if (! params) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters"); + goto fail; + } + + grub_memset (params, 0, 16384); + + grub_memcpy (&lh, kernel, sizeof (lh)); + + if (lh.boot_flag != grub_cpu_to_le16 (0xaa55)) + { + grub_error (GRUB_ERR_BAD_OS, N_("invalid magic number")); + goto fail; + } + + if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS) + { + grub_error (GRUB_ERR_BAD_OS, N_("too many setup sectors")); + goto fail; + } + + if (lh.version < grub_cpu_to_le16 (0x020b)) + { + grub_error (GRUB_ERR_BAD_OS, N_("kernel too old")); + goto fail; + } + + if (!lh.handover_offset) + { + grub_error (GRUB_ERR_BAD_OS, N_("kernel doesn't support EFI handover")); + goto fail; + } + + grub_dprintf ("linux", "setting up cmdline\n"); + linux_cmdline = grub_efi_allocate_pages_max(0x3fffffff, + BYTES_TO_PAGES(lh.cmdline_size + 1)); + + if (!linux_cmdline) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate cmdline")); + goto fail; + } + + grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE)); + grub_create_loader_cmdline (argc, argv, + linux_cmdline + sizeof (LINUX_IMAGE) - 1, + lh.cmdline_size - (sizeof (LINUX_IMAGE) - 1), + GRUB_VERIFY_KERNEL_CMDLINE); + + lh.cmd_line_ptr = (grub_uint32_t)(grub_addr_t)linux_cmdline; + + handover_offset = lh.handover_offset; + + start = (lh.setup_sects + 1) * 512; + len = grub_file_size(file) - start; + + kernel_mem = grub_efi_allocate_pages_max(lh.pref_address, + BYTES_TO_PAGES(lh.init_size)); + + if (!kernel_mem) + kernel_mem = grub_efi_allocate_pages_max(0x3fffffff, + BYTES_TO_PAGES(lh.init_size)); + + if (!kernel_mem) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate kernel")); + goto fail; + } + + grub_memcpy (kernel_mem, (char *)kernel + start, len); + grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0); + loaded=1; + + lh.code32_start = (grub_uint32_t)(grub_uint64_t) kernel_mem; + grub_memcpy (params, &lh, 2 * 512); + + params->type_of_loader = 0x21; + + fail: + + if (file) + grub_file_close (file); + + if (kernel) + grub_free (kernel); + + if (grub_errno != GRUB_ERR_NONE) + { + grub_dl_unref (my_mod); + loaded = 0; + } + + if (linux_cmdline && !loaded) + grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t) + linux_cmdline, + BYTES_TO_PAGES(lh.cmdline_size + 1)); + + if (kernel_mem && !loaded) + grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)kernel_mem, + BYTES_TO_PAGES(kernel_size)); + + if (params && !loaded) + grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)params, + BYTES_TO_PAGES(16384)); + + return grub_errno; +} + +static grub_command_t cmd_linux, cmd_initrd; +static grub_command_t cmd_linuxefi, cmd_initrdefi; + +GRUB_MOD_INIT(linux) +{ + cmd_linux = + grub_register_command ("linux", grub_cmd_linux, + 0, N_("Load Linux.")); + cmd_linuxefi = + grub_register_command ("linuxefi", grub_cmd_linux, + 0, N_("Load Linux.")); + cmd_initrd = + grub_register_command ("initrd", grub_cmd_initrd, + 0, N_("Load initrd.")); + cmd_initrdefi = + grub_register_command ("initrdefi", grub_cmd_initrd, + 0, N_("Load initrd.")); + my_mod = mod; +} + +GRUB_MOD_FINI(linux) +{ + grub_unregister_command (cmd_linux); + grub_unregister_command (cmd_linuxefi); + grub_unregister_command (cmd_initrd); + grub_unregister_command (cmd_initrdefi); +} diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c index 2a29952016..8be4c3b3f4 100644 --- a/grub-core/loader/i386/pc/linux.c +++ b/grub-core/loader/i386/pc/linux.c @@ -474,14 +474,20 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), return grub_errno; } -static grub_command_t cmd_linux, cmd_initrd; +static grub_command_t cmd_linux, cmd_linux16, cmd_initrd, cmd_initrd16; GRUB_MOD_INIT(linux16) { cmd_linux = + grub_register_command ("linux", grub_cmd_linux, + 0, N_("Load Linux.")); + cmd_linux16 = grub_register_command ("linux16", grub_cmd_linux, 0, N_("Load Linux.")); cmd_initrd = + grub_register_command ("initrd", grub_cmd_initrd, + 0, N_("Load initrd.")); + cmd_initrd16 = grub_register_command ("initrd16", grub_cmd_initrd, 0, N_("Load initrd.")); my_mod = mod; @@ -490,5 +496,7 @@ GRUB_MOD_INIT(linux16) GRUB_MOD_FINI(linux16) { grub_unregister_command (cmd_linux); + grub_unregister_command (cmd_linux16); grub_unregister_command (cmd_initrd); + grub_unregister_command (cmd_initrd16); } diff --git a/include/grub/arm/linux.h b/include/grub/arm/linux.h index bcd5a7eb18..b582f67f66 100644 --- a/include/grub/arm/linux.h +++ b/include/grub/arm/linux.h @@ -20,6 +20,7 @@ #ifndef GRUB_ARM_LINUX_HEADER #define GRUB_ARM_LINUX_HEADER 1 +#include #include "system.h" #define GRUB_LINUX_ARM_MAGIC_SIGNATURE 0x016f2818 @@ -34,9 +35,17 @@ struct linux_arm_kernel_header { grub_uint32_t hdr_offset; }; +struct grub_arm_linux_pe_header +{ + grub_uint32_t magic; + struct grub_pe32_coff_header coff; + struct grub_pe32_optional_header opt; +}; + #if defined(__arm__) # define GRUB_LINUX_ARMXX_MAGIC_SIGNATURE GRUB_LINUX_ARM_MAGIC_SIGNATURE # define linux_arch_kernel_header linux_arm_kernel_header +# define grub_armxx_linux_pe_header grub_arm_linux_pe_header #endif #if defined GRUB_MACHINE_UBOOT diff --git a/include/grub/arm64/linux.h b/include/grub/arm64/linux.h index 7e22b4ab69..ea030312df 100644 --- a/include/grub/arm64/linux.h +++ b/include/grub/arm64/linux.h @@ -19,6 +19,7 @@ #ifndef GRUB_ARM64_LINUX_HEADER #define GRUB_ARM64_LINUX_HEADER 1 +#include #include #define GRUB_LINUX_ARM64_MAGIC_SIGNATURE 0x644d5241 /* 'ARM\x64' */ @@ -38,9 +39,17 @@ struct linux_arm64_kernel_header grub_uint32_t hdr_offset; /* Offset of PE/COFF header */ }; +struct grub_arm64_linux_pe_header +{ + grub_uint32_t magic; + struct grub_pe32_coff_header coff; + struct grub_pe64_optional_header opt; +}; + #if defined(__aarch64__) # define GRUB_LINUX_ARMXX_MAGIC_SIGNATURE GRUB_LINUX_ARM64_MAGIC_SIGNATURE # define linux_arch_kernel_header linux_arm64_kernel_header +# define grub_armxx_linux_pe_header grub_arm64_linux_pe_header #endif #endif /* ! GRUB_ARM64_LINUX_HEADER */ diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index 83d958f994..6295df85f3 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -47,6 +47,9 @@ EXPORT_FUNC(grub_efi_allocate_fixed) (grub_efi_physical_address_t address, grub_efi_uintn_t pages); void * EXPORT_FUNC(grub_efi_allocate_any_pages) (grub_efi_uintn_t pages); +void * +EXPORT_FUNC(grub_efi_allocate_pages_max) (grub_efi_physical_address_t max, + grub_efi_uintn_t pages); void EXPORT_FUNC(grub_efi_free_pages) (grub_efi_physical_address_t address, grub_efi_uintn_t pages); grub_efi_uintn_t EXPORT_FUNC(grub_efi_find_mmap_size) (void); @@ -88,6 +91,7 @@ EXPORT_FUNC (grub_efi_set_variable) (const char *var, const grub_efi_guid_t *guid, void *data, grub_size_t datasize); +grub_efi_boolean_t EXPORT_FUNC (grub_efi_secure_boot) (void); int EXPORT_FUNC (grub_efi_compare_device_paths) (const grub_efi_device_path_t *dp1, const grub_efi_device_path_t *dp2); @@ -101,8 +105,7 @@ void *EXPORT_FUNC(grub_efi_get_firmware_fdt)(void); grub_err_t EXPORT_FUNC(grub_efi_get_ram_base)(grub_addr_t *); #include grub_err_t grub_arch_efi_linux_check_image(struct linux_arch_kernel_header *lh); -grub_err_t grub_arch_efi_linux_boot_image(grub_addr_t addr, grub_size_t size, - char *args); +grub_err_t grub_arch_efi_linux_boot_image(grub_addr_t addr, char *args); #endif grub_addr_t grub_efi_modules_addr (void); diff --git a/include/grub/efi/linux.h b/include/grub/efi/linux.h new file mode 100644 index 0000000000..d9ede36773 --- /dev/null +++ b/include/grub/efi/linux.h @@ -0,0 +1,31 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2014 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ +#ifndef GRUB_EFI_LINUX_HEADER +#define GRUB_EFI_LINUX_HEADER 1 + +#include +#include +#include + +grub_efi_boolean_t +EXPORT_FUNC(grub_linuxefi_secure_validate) (void *data, grub_uint32_t size); +grub_err_t +EXPORT_FUNC(grub_efi_linux_boot) (void *kernel_address, grub_off_t offset, + void *kernel_param); + +#endif /* ! GRUB_EFI_LINUX_HEADER */ From 7a1b164146729ee3a81ef000f691c4b912d1ed3e Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Sun, 9 Aug 2015 16:12:39 -0700 Subject: [PATCH 005/367] Rework linux command We want a single buffer that contains the entire kernel image in order to perform a TPM measurement. Allocate one and copy the entire kernel into it before pulling out the individual blocks later on. Signed-off-by: Matthew Garrett --- grub-core/loader/i386/linux.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 9f74a96b19..dccf3bb300 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -649,13 +649,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), { grub_file_t file = 0; struct linux_i386_kernel_header lh; + grub_uint8_t *linux_params_ptr; grub_uint8_t setup_sects; - grub_size_t real_size, prot_size, prot_file_size; + grub_size_t real_size, prot_size, prot_file_size, kernel_offset; grub_ssize_t len; int i; grub_size_t align, min_align; int relocatable; grub_uint64_t preferred_address = GRUB_LINUX_BZIMAGE_ADDR; + grub_uint8_t *kernel = NULL; grub_dl_ref (my_mod); @@ -669,7 +671,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), if (! file) goto fail; - if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) + len = grub_file_size (file); + kernel = grub_malloc (len); + if (!kernel) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer")); + goto fail; + } + + if (grub_file_read (file, kernel, len) != len) { if (!grub_errno) grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), @@ -677,6 +687,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } + grub_memcpy (&lh, kernel, sizeof (lh)); + kernel_offset = sizeof (lh); + if (lh.boot_flag != grub_cpu_to_le16_compile_time (0xaa55)) { grub_error (GRUB_ERR_BAD_OS, "invalid magic number"); @@ -784,13 +797,11 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), /* We've already read lh so there is no need to read it second time. */ len -= sizeof(lh); - if ((len > 0) && - (grub_file_read (file, (char *) &linux_params + sizeof (lh), len) != len)) + linux_params_ptr = (void *)&linux_params; + if (len > 0) { - if (!grub_errno) - grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), - argv[0]); - goto fail; + grub_memcpy (linux_params_ptr + sizeof (lh), kernel + kernel_offset, len); + kernel_offset += len; } linux_params.code32_start = prot_mode_target + lh.code32_start - GRUB_LINUX_BZIMAGE_ADDR; @@ -853,7 +864,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), /* The other parameters are filled when booting. */ - grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE); + kernel_offset = real_size + GRUB_DISK_SECTOR_SIZE; grub_dprintf ("linux", "bzImage, setup=0x%x, size=0x%x\n", (unsigned) real_size, (unsigned) prot_size); @@ -1007,9 +1018,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } len = prot_file_size; - if (grub_file_read (file, prot_mode_mem, len) != len && !grub_errno) - grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), - argv[0]); + grub_memcpy (prot_mode_mem, kernel + kernel_offset, len); if (grub_errno == GRUB_ERR_NONE) { @@ -1020,6 +1029,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), fail: + grub_free (kernel); + if (file) grub_file_close (file); From ca9c00ad13e81000e5f95c9a9a085614da13d49d Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Sun, 9 Aug 2015 16:20:58 -0700 Subject: [PATCH 006/367] Rework linux16 command We want a single buffer that contains the entire kernel image in order to perform a TPM measurement. Allocate one and copy the entire kernel int it before pulling out the individual blocks later on. Signed-off-by: Matthew Garrett --- grub-core/loader/i386/pc/linux.c | 33 ++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c index 8be4c3b3f4..4b1750e360 100644 --- a/grub-core/loader/i386/pc/linux.c +++ b/grub-core/loader/i386/pc/linux.c @@ -124,13 +124,14 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_file_t file = 0; struct linux_i386_kernel_header lh; grub_uint8_t setup_sects; - grub_size_t real_size; + grub_size_t real_size, kernel_offset = 0; grub_ssize_t len; int i; char *grub_linux_prot_chunk; int grub_linux_is_bzimage; grub_addr_t grub_linux_prot_target; grub_err_t err; + grub_uint8_t *kernel = NULL; grub_dl_ref (my_mod); @@ -144,7 +145,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), if (! file) goto fail; - if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) + len = grub_file_size (file); + kernel = grub_malloc (len); + if (!kernel) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer")); + goto fail; + } + + if (grub_file_read (file, kernel, len) != len) { if (!grub_errno) grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), @@ -152,6 +161,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } + grub_memcpy (&lh, kernel, sizeof (lh)); + kernel_offset = sizeof (lh); + if (lh.boot_flag != grub_cpu_to_le16_compile_time (0xaa55)) { grub_error (GRUB_ERR_BAD_OS, "invalid magic number"); @@ -320,13 +332,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_memmove (grub_linux_real_chunk, &lh, sizeof (lh)); len = real_size + GRUB_DISK_SECTOR_SIZE - sizeof (lh); - if (grub_file_read (file, grub_linux_real_chunk + sizeof (lh), len) != len) - { - if (!grub_errno) - grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), - argv[0]); - goto fail; - } + grub_memcpy (grub_linux_real_chunk + sizeof (lh), kernel + kernel_offset, + len); + kernel_offset += len; if (lh.header != grub_cpu_to_le32_compile_time (GRUB_LINUX_I386_MAGIC_SIGNATURE) || grub_le_to_cpu16 (lh.version) < 0x0200) @@ -364,9 +372,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } len = grub_linux16_prot_size; - if (grub_file_read (file, grub_linux_prot_chunk, len) != len && !grub_errno) - grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), - argv[0]); + grub_memcpy (grub_linux_prot_chunk, kernel + kernel_offset, len); + kernel_offset += len; if (grub_errno == GRUB_ERR_NONE) { @@ -376,6 +383,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), fail: + grub_free (kernel); + if (file) grub_file_close (file); From b3c53b6e2fba330884b4797ee81c2cea6a03395e Mon Sep 17 00:00:00 2001 From: Raymund Will Date: Mon, 8 Jul 2019 11:55:18 +0200 Subject: [PATCH 007/367] Add secureboot support on efi chainloader Expand the chainloader to be able to verify the image by means of shim lock protocol. The PE/COFF image is loaded and relocated by the chainloader instead of calling LoadImage and StartImage UEFI boot Service as they require positive verification result from keys enrolled in KEK or DB. The shim will use MOK in addition to firmware enrolled keys to verify the image. The chainloader module could be used to load other UEFI bootloaders, such as xen.efi, and could be signed by any of MOK, KEK or DB. Based on https://build.opensuse.org/package/view_file/openSUSE:Factory/grub2/grub2-secureboot-chainloader.patch Signed-off-by: Peter Jones Also: commit cd7a8984d4fda905877b5bfe466339100156b3bc Author: Raymund Will Date: Fri Apr 10 01:45:02 2015 -0400 Use device part of chainloader target, if present. Otherwise chainloading is restricted to '$root', which might not even be readable by EFI! v1. use grub_file_get_device_name() to get device name Signed-off-by: Michael Chang Signed-off-by: Peter Jones Also: commit 0872a2310a0eeac4ecfe9e1b49dd2d72ab373039 Author: Peter Jones Date: Fri Jun 10 14:06:15 2016 -0400 Rework even more of efi chainload so non-sb cases work right. This ensures that if shim protocol is not loaded, or is loaded but shim is disabled, we will fall back to a correct load method for the efi chain loader. Here's what I tested with this version: results expected actual ------------------------------------------------------------ sb + enabled + shim + fedora success success sb + enabled + shim + win success success sb + enabled + grub + fedora fail fail sb + enabled + grub + win fail fail sb + mokdisabled + shim + fedora success success sb + mokdisabled + shim + win success success sb + mokdisabled + grub + fedora fail fail sb + mokdisabled + grub + win fail fail sb disabled + shim + fedora success success* sb disabled + shim + win success success* sb disabled + grub + fedora success success sb disabled + grub + win success success nosb + shim + fedora success success* nosb + shim + win success success* nosb + grub + fedora success success nosb + grub + win success success * for some reason shim protocol is being installed in these cases, and I can't see why, but I think it may be this firmware build returning an erroneous value. But this effectively falls back to the mokdisabled behavior, which works correctly, and the presence of the "grub" (i.e. no shim) tests effectively tests the desired behavior here. Resolves: rhbz#1344512 Signed-off-by: Peter Jones Also: commit ff7b1cb7f69487870211aeb69ff4f54470fbcb58 Author: Laszlo Ersek Date: Mon Nov 21 15:34:00 2016 +0100 efi/chainloader: fix wrong sanity check in relocate_coff() In relocate_coff(), the relocation entries are parsed from the original image (not the section-wise copied image). The original image is pointed-to by the "orig" pointer. The current check (void *)reloc_end < data compares the addresses of independent memory allocations. "data" is a typo here, it should be "orig". Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1347291 Signed-off-by: Laszlo Ersek Tested-by: Bogdan Costescu Tested-by: Juan Orti Also: commit ab4ba9997ad4832449e54d930fa2aac6a160d0e9 Author: Laszlo Ersek Date: Wed Nov 23 06:27:09 2016 +0100 efi/chainloader: truncate overlong relocation section The UEFI Windows 7 boot loader ("EFI/Microsoft/Boot/bootmgfw.efi", SHA1 31b410e029bba87d2068c65a80b88882f9f8ea25) has inconsistent headers. Compare: > The Data Directory > ... > Entry 5 00000000000d9000 00000574 Base Relocation Directory [.reloc] Versus: > Sections: > Idx Name Size VMA LMA File off ... > ... > 10 .reloc 00000e22 00000000100d9000 00000000100d9000 000a1800 ... That is, the size reported by the RelocDir entry (0x574) is smaller than the virtual size of the .reloc section (0xe22). Quoting the grub2 debug log for the same: > chainloader.c:595: reloc_dir: 0xd9000 reloc_size: 0x00000574 > chainloader.c:603: reloc_base: 0x7d208000 reloc_base_end: 0x7d208573 > ... > chainloader.c:620: Section 10 ".reloc" at 0x7d208000..0x7d208e21 > chainloader.c:661: section is not reloc section? > chainloader.c:663: rds: 0x00001000, vs: 00000e22 > chainloader.c:664: base: 0x7d208000 end: 0x7d208e21 > chainloader.c:666: reloc_base: 0x7d208000 reloc_base_end: 0x7d208573 > chainloader.c:671: Section characteristics are 42000040 > chainloader.c:673: Section virtual size: 00000e22 > chainloader.c:675: Section raw_data size: 00001000 > chainloader.c:678: Discarding section After hexdumping "bootmgfw.efi" and manually walking its relocation blocks (yes, really), I determined that the (smaller) RelocDir value is correct. The remaining area that extends up to the .reloc section size (== 0xe22 - 0x574 == 0x8ae bytes) exists as zero padding in the file. This zero padding shouldn't be passed to relocate_coff() for parsing. In order to cope with it, split the handling of .reloc sections into the following branches: - original case (equal size): original behavior (--> relocation attempted), - overlong .reloc section (longer than reported by RelocDir): truncate the section to the RelocDir size for the purposes of relocate_coff(), and attempt relocation, - .reloc section is too short, or other checks fail: original behavior (--> relocation not attempted). Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1347291 Signed-off-by: Laszlo Ersek Also: commit cc06f149fbd2d8c1da1e83173d21629ba97e0d92 Author: Raymund Will chainloader: Define machine types for RISC-V The commit "Add secureboot support on efi chainloader" didn't add machine types for RISC-V, so this patch adds them. Note, that grub-core/loader/riscv/linux.c is skipped because Linux is not supported yet. This patch might need a new revision once that's the case. Signed-off-by: David Abdurachmanov --- grub-core/kern/efi/efi.c | 14 +- grub-core/loader/arm64/linux.c | 4 +- grub-core/loader/efi/chainloader.c | 820 ++++++++++++++++++++++++++--- grub-core/loader/efi/linux.c | 25 +- grub-core/loader/i386/efi/linux.c | 17 +- include/grub/efi/linux.h | 2 +- include/grub/efi/pe32.h | 52 +- 7 files changed, 844 insertions(+), 90 deletions(-) diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index 35b8f67060..4a2259aa1c 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -296,14 +296,20 @@ grub_efi_secure_boot (void) grub_efi_boolean_t ret = 0; secure_boot = grub_efi_get_variable("SecureBoot", &efi_var_guid, &datasize); - if (datasize != 1 || !secure_boot) - goto out; + { + grub_dprintf ("secureboot", "No SecureBoot variable\n"); + goto out; + } + grub_dprintf ("secureboot", "SecureBoot: %d\n", *secure_boot); setup_mode = grub_efi_get_variable("SetupMode", &efi_var_guid, &datasize); - if (datasize != 1 || !setup_mode) - goto out; + { + grub_dprintf ("secureboot", "No SetupMode variable\n"); + goto out; + } + grub_dprintf ("secureboot", "SetupMode: %d\n", *setup_mode); if (*secure_boot && !*setup_mode) ret = 1; diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c index a312c66868..04994d5c67 100644 --- a/grub-core/loader/arm64/linux.c +++ b/grub-core/loader/arm64/linux.c @@ -284,6 +284,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), struct linux_arch_kernel_header lh; struct grub_armxx_linux_pe_header *pe; grub_err_t err; + int rc; grub_dl_ref (my_mod); @@ -328,7 +329,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_dprintf ("linux", "kernel @ %p\n", kernel_addr); - if (!grub_linuxefi_secure_validate (kernel_addr, kernel_size)) + rc = grub_linuxefi_secure_validate (kernel_addr, kernel_size); + if (rc < 0) { grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), argv[0]); goto fail; diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index 2bd80f4db3..e6a8d4ad0e 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include #include @@ -46,9 +48,14 @@ static grub_dl_t my_mod; static grub_efi_physical_address_t address; static grub_efi_uintn_t pages; +static grub_ssize_t fsize; static grub_efi_device_path_t *file_path; static grub_efi_handle_t image_handle; static grub_efi_char16_t *cmdline; +static grub_ssize_t cmdline_len; +static grub_efi_handle_t dev_handle; + +static grub_efi_status_t (*entry_point) (grub_efi_handle_t image_handle, grub_efi_system_table_t *system_table); static grub_err_t grub_chainloader_unload (void) @@ -63,6 +70,7 @@ grub_chainloader_unload (void) grub_free (cmdline); cmdline = 0; file_path = 0; + dev_handle = 0; grub_dl_unref (my_mod); return GRUB_ERR_NONE; @@ -213,20 +221,694 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename) return file_path; } +#define SHIM_LOCK_GUID \ + { 0x605dab50, 0xe046, 0x4300, { 0xab,0xb6,0x3d,0xd8,0x10,0xdd,0x8b,0x23 } } + +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; + +struct grub_efi_shim_lock +{ + grub_efi_status_t (*verify)(void *buffer, + grub_efi_uint32_t size); + grub_efi_status_t (*hash)(void *data, + grub_efi_int32_t datasize, + pe_coff_loader_image_context_t *context, + grub_efi_uint8_t *sha256hash, + grub_efi_uint8_t *sha1hash); + grub_efi_status_t (*context)(void *data, + grub_efi_uint32_t size, + pe_coff_loader_image_context_t *context); +}; + +typedef struct grub_efi_shim_lock grub_efi_shim_lock_t; + +static grub_efi_boolean_t +read_header (void *data, grub_efi_uint32_t size, + pe_coff_loader_image_context_t *context) +{ + grub_efi_guid_t guid = SHIM_LOCK_GUID; + grub_efi_shim_lock_t *shim_lock; + grub_efi_status_t status; + + shim_lock = grub_efi_locate_protocol (&guid, NULL); + if (!shim_lock) + { + grub_dprintf ("chain", "no shim lock protocol"); + return 0; + } + + status = shim_lock->context (data, size, context); + + if (status == GRUB_EFI_SUCCESS) + { + grub_dprintf ("chain", "context success\n"); + return 1; + } + + switch (status) + { + case GRUB_EFI_UNSUPPORTED: + grub_error (GRUB_ERR_BAD_ARGUMENT, "context error unsupported"); + break; + case GRUB_EFI_INVALID_PARAMETER: + grub_error (GRUB_ERR_BAD_ARGUMENT, "context error invalid parameter"); + break; + default: + grub_error (GRUB_ERR_BAD_ARGUMENT, "context error code"); + break; + } + + return -1; +} + +static void* +image_address (void *image, grub_efi_uint64_t sz, grub_efi_uint64_t adr) +{ + if (adr > sz) + return NULL; + + return ((grub_uint8_t*)image + adr); +} + +static int +image_is_64_bit (grub_pe_header_t *pe_hdr) +{ + /* .Magic is the same offset in all cases */ + if (pe_hdr->pe32plus.optional_header.magic == GRUB_PE32_PE64_MAGIC) + return 1; + return 0; +} + +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 grub_efi_status_t +relocate_coff (pe_coff_loader_image_context_t *context, + struct grub_pe32_section_table *section, + void *orig, void *data) +{ + struct grub_pe32_data_directory *reloc_base, *reloc_base_end; + grub_efi_uint64_t adjust; + struct grub_pe32_fixup_block *reloc, *reloc_end; + char *fixup, *fixup_base, *fixup_data = NULL; + grub_efi_uint16_t *fixup_16; + grub_efi_uint32_t *fixup_32; + grub_efi_uint64_t *fixup_64; + grub_efi_uint64_t size = context->image_size; + void *image_end = (char *)orig + size; + int n = 0; + + if (image_is_64_bit (context->pe_hdr)) + context->pe_hdr->pe32plus.optional_header.image_base = + (grub_uint64_t)(unsigned long)data; + else + context->pe_hdr->pe32.optional_header.image_base = + (grub_uint32_t)(unsigned long)data; + + /* 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_base = image_address (orig, size, section->raw_data_offset); + reloc_base_end = image_address (orig, size, section->raw_data_offset + + section->virtual_size); + + grub_dprintf ("chain", "relocate_coff(): reloc_base %p reloc_base_end %p\n", + reloc_base, reloc_base_end); + + if (!reloc_base && !reloc_base_end) + return GRUB_EFI_SUCCESS; + + if (!reloc_base || !reloc_base_end) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc table overflows binary"); + return GRUB_EFI_UNSUPPORTED; + } + + adjust = (grub_uint64_t)(grub_efi_uintn_t)data - context->image_address; + if (adjust == 0) + return GRUB_EFI_SUCCESS; + + 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); + return GRUB_EFI_UNSUPPORTED; + } + + entry = &reloc->entries[0]; + reloc_end = (struct grub_pe32_fixup_block *) + ((char *)reloc_base + reloc_base->size); + + if ((void *)reloc_end < orig || (void *)reloc_end > image_end) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc entry %d overflows binary", + n); + return GRUB_EFI_UNSUPPORTED; + } + + fixup_base = image_address(data, size, reloc_base->rva); + + if (!fixup_base) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc %d Invalid fixupbase", n); + return GRUB_EFI_UNSUPPORTED; + } + + 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))); + if (fixup_data != NULL) + { + *(grub_uint16_t *) fixup_data = *fixup_16; + fixup_data = fixup_data + sizeof (grub_uint16_t); + } + break; + case GRUB_PE32_REL_BASED_LOW: + fixup_16 = (grub_uint16_t *)fixup; + *fixup_16 = (grub_uint16_t) (*fixup_16 + (grub_uint16_t)adjust); + if (fixup_data != NULL) + { + *(grub_uint16_t *) fixup_data = *fixup_16; + fixup_data = fixup_data + sizeof (grub_uint16_t); + } + break; + case GRUB_PE32_REL_BASED_HIGHLOW: + fixup_32 = (grub_uint32_t *)fixup; + *fixup_32 = *fixup_32 + (grub_uint32_t)adjust; + if (fixup_data != NULL) + { + fixup_data = (char *)ALIGN_UP ((grub_addr_t)fixup_data, sizeof (grub_uint32_t)); + *(grub_uint32_t *) fixup_data = *fixup_32; + fixup_data += sizeof (grub_uint32_t); + } + break; + case GRUB_PE32_REL_BASED_DIR64: + fixup_64 = (grub_uint64_t *)fixup; + *fixup_64 = *fixup_64 + (grub_uint64_t)adjust; + if (fixup_data != NULL) + { + fixup_data = (char *)ALIGN_UP ((grub_addr_t)fixup_data, sizeof (grub_uint64_t)); + *(grub_uint64_t *) fixup_data = *fixup_64; + fixup_data += sizeof (grub_uint64_t); + } + break; + default: + grub_error (GRUB_ERR_BAD_ARGUMENT, + "Reloc %d unknown relocation type %d", + n, (*entry) >> 12); + return GRUB_EFI_UNSUPPORTED; + } + entry += 1; + } + reloc_base = (struct grub_pe32_data_directory *)reloc_end; + n++; + } + + return GRUB_EFI_SUCCESS; +} + +static grub_efi_device_path_t * +grub_efi_get_media_file_path (grub_efi_device_path_t *dp) +{ + while (1) + { + grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp); + grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); + + if (type == GRUB_EFI_END_DEVICE_PATH_TYPE) + break; + else if (type == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE + && subtype == GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE) + return dp; + + dp = GRUB_EFI_NEXT_DEVICE_PATH (dp); + } + + return NULL; +} + +static grub_efi_boolean_t +handle_image (void *data, grub_efi_uint32_t datasize) +{ + grub_efi_boot_services_t *b; + grub_efi_loaded_image_t *li, li_bak; + grub_efi_status_t efi_status; + char *buffer = NULL; + char *buffer_aligned = NULL; + grub_efi_uint32_t i; + struct grub_pe32_section_table *section; + char *base, *end; + pe_coff_loader_image_context_t context; + grub_uint32_t section_alignment; + grub_uint32_t buffer_size; + int found_entry_point = 0; + int rc; + + b = grub_efi_system_table->boot_services; + + rc = read_header (data, datasize, &context); + if (rc < 0) + { + grub_dprintf ("chain", "Failed to read header\n"); + goto error_exit; + } + else if (rc == 0) + { + grub_dprintf ("chain", "Secure Boot is not enabled\n"); + return 0; + } + else + { + grub_dprintf ("chain", "Header read without error\n"); + } + + /* + * 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; + + buffer_size = context.image_size + section_alignment; + grub_dprintf ("chain", "image size is %08"PRIxGRUB_UINT64_T", datasize is %08x\n", + context.image_size, datasize); + + efi_status = efi_call_3 (b->allocate_pool, GRUB_EFI_LOADER_DATA, + buffer_size, &buffer); + + if (efi_status != GRUB_EFI_SUCCESS) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto error_exit; + } + + buffer_aligned = (char *)ALIGN_UP ((grub_addr_t)buffer, section_alignment); + if (!buffer_aligned) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto error_exit; + } + + grub_memcpy (buffer_aligned, data, context.size_of_headers); + + entry_point = image_address (buffer_aligned, context.image_size, + context.entry_point); + + grub_dprintf ("chain", "entry_point: %p\n", entry_point); + if (!entry_point) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid entry point"); + goto error_exit; + } + + char *reloc_base, *reloc_base_end; + grub_dprintf ("chain", "reloc_dir: %p reloc_size: 0x%08x\n", + (void *)(unsigned long)context.reloc_dir->rva, + context.reloc_dir->size); + reloc_base = image_address (buffer_aligned, context.image_size, + context.reloc_dir->rva); + /* RelocBaseEnd here is the address of the last byte of the table */ + reloc_base_end = image_address (buffer_aligned, context.image_size, + context.reloc_dir->rva + + context.reloc_dir->size - 1); + grub_dprintf ("chain", "reloc_base: %p reloc_base_end: %p\n", + reloc_base, reloc_base_end); + + struct grub_pe32_section_table *reloc_section = NULL, fake_reloc_section; + + section = context.first_section; + for (i = 0; i < context.number_of_sections; i++, section++) + { + char name[9]; + + base = image_address (buffer_aligned, context.image_size, + section->virtual_address); + end = image_address (buffer_aligned, context.image_size, + section->virtual_address + section->virtual_size -1); + + grub_strncpy(name, section->name, 9); + name[8] = '\0'; + grub_dprintf ("chain", "Section %d \"%s\" at %p..%p\n", i, + name, base, end); + + if (end < base) + { + grub_dprintf ("chain", " base is %p but end is %p... bad.\n", + base, end); + grub_error (GRUB_ERR_BAD_ARGUMENT, + "Image has invalid negative size"); + goto error_exit; + } + + if (section->virtual_address <= context.entry_point && + (section->virtual_address + section->raw_data_size - 1) + > context.entry_point) + { + found_entry_point++; + grub_dprintf ("chain", " 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"); + goto error_exit; + } + + /* 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 ("chain", " 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 ("chain", + " 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 ("chain", " section is not reloc section?\n"); + grub_dprintf ("chain", " rds: 0x%08x, vs: %08x\n", + section->raw_data_size, section->virtual_size); + grub_dprintf ("chain", " base: %p end: %p\n", base, end); + grub_dprintf ("chain", " reloc_base: %p reloc_base_end: %p\n", + reloc_base, reloc_base_end); + } + } + + grub_dprintf ("chain", " Section characteristics are %08x\n", + section->characteristics); + grub_dprintf ("chain", " Section virtual size: %08x\n", + section->virtual_size); + grub_dprintf ("chain", " Section raw_data size: %08x\n", + section->raw_data_size); + if (section->characteristics & GRUB_PE32_SCN_MEM_DISCARDABLE) + { + grub_dprintf ("chain", " Discarding section\n"); + continue; + } + + if (!base || !end) + { + grub_dprintf ("chain", " section is invalid\n"); + grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid section size"); + goto error_exit; + } + + if (section->characteristics & GRUB_PE32_SCN_CNT_UNINITIALIZED_DATA) + { + if (section->raw_data_size != 0) + grub_dprintf ("chain", " 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); + goto error_exit; + } + + if (section->raw_data_size > 0) + { + grub_dprintf ("chain", " copying 0x%08x bytes to %p\n", + section->raw_data_size, base); + grub_memcpy (base, + (grub_efi_uint8_t*)data + section->raw_data_offset, + section->raw_data_size); + } + + if (section->raw_data_size < section->virtual_size) + { + grub_dprintf ("chain", " padding with 0x%08x bytes at %p\n", + section->virtual_size - section->raw_data_size, + base + section->raw_data_size); + grub_memset (base + section->raw_data_size, 0, + section->virtual_size - section->raw_data_size); + } + + grub_dprintf ("chain", " finished section %s\n", name); + } + + /* 5 == EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC */ + if (context.number_of_rva_and_sizes <= 5) + { + grub_dprintf ("chain", "image has no relocation entry\n"); + goto error_exit; + } + + if (context.reloc_dir->size && reloc_section) + { + /* run the relocation fixups */ + efi_status = relocate_coff (&context, reloc_section, data, + buffer_aligned); + + if (efi_status != GRUB_EFI_SUCCESS) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "relocation failed"); + goto error_exit; + } + } + + if (!found_entry_point) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "entry point is not within sections"); + goto error_exit; + } + if (found_entry_point > 1) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "%d sections contain entry point", + found_entry_point); + goto error_exit; + } + + li = grub_efi_get_loaded_image (grub_efi_image_handle); + if (!li) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "no loaded image available"); + goto error_exit; + } + + grub_memcpy (&li_bak, li, sizeof (grub_efi_loaded_image_t)); + li->image_base = buffer_aligned; + li->image_size = context.image_size; + li->load_options = cmdline; + li->load_options_size = cmdline_len; + li->file_path = grub_efi_get_media_file_path (file_path); + li->device_handle = dev_handle; + if (!li->file_path) + { + grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching file path found"); + goto error_exit; + } + + grub_dprintf ("chain", "booting via entry point\n"); + efi_status = efi_call_2 (entry_point, grub_efi_image_handle, + grub_efi_system_table); + + grub_dprintf ("chain", "entry_point returned %ld\n", efi_status); + grub_memcpy (li, &li_bak, sizeof (grub_efi_loaded_image_t)); + efi_status = efi_call_1 (b->free_pool, buffer); + + return 1; + +error_exit: + grub_dprintf ("chain", "error_exit: grub_errno: %d\n", grub_errno); + if (buffer) + efi_call_1 (b->free_pool, buffer); + + return 0; +} + +static grub_err_t +grub_secureboot_chainloader_unload (void) +{ + grub_efi_boot_services_t *b; + + b = grub_efi_system_table->boot_services; + efi_call_2 (b->free_pages, address, pages); + grub_free (file_path); + grub_free (cmdline); + cmdline = 0; + file_path = 0; + dev_handle = 0; + + grub_dl_unref (my_mod); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_load_and_start_image(void *boot_image) +{ + grub_efi_boot_services_t *b; + grub_efi_status_t status; + grub_efi_loaded_image_t *loaded_image; + + b = grub_efi_system_table->boot_services; + + status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path, + boot_image, fsize, &image_handle); + if (status != GRUB_EFI_SUCCESS) + { + if (status == GRUB_EFI_OUT_OF_RESOURCES) + grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of resources"); + else + grub_error (GRUB_ERR_BAD_OS, "cannot load image"); + return -1; + } + + /* LoadImage does not set a device handler when the image is + loaded from memory, so it is necessary to set it explicitly here. + This is a mess. */ + loaded_image = grub_efi_get_loaded_image (image_handle); + if (! loaded_image) + { + grub_error (GRUB_ERR_BAD_OS, "no loaded image available"); + return -1; + } + loaded_image->device_handle = dev_handle; + + if (cmdline) + { + loaded_image->load_options = cmdline; + loaded_image->load_options_size = cmdline_len; + } + + return 0; +} + +static grub_err_t +grub_secureboot_chainloader_boot (void) +{ + int rc; + rc = handle_image ((void *)(unsigned long)address, fsize); + if (rc == 0) + { + grub_load_and_start_image((void *)(unsigned long)address); + } + + grub_loader_unset (); + return grub_errno; +} + static grub_err_t grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { grub_file_t file = 0; - grub_ssize_t size; grub_efi_status_t status; grub_efi_boot_services_t *b; grub_device_t dev = 0; grub_efi_device_path_t *dp = 0; - grub_efi_loaded_image_t *loaded_image; char *filename; void *boot_image = 0; - grub_efi_handle_t dev_handle = 0; + int rc; if (argc == 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); @@ -238,15 +920,45 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), address = 0; image_handle = 0; file_path = 0; + dev_handle = 0; b = grub_efi_system_table->boot_services; + if (argc > 1) + { + int i; + grub_efi_char16_t *p16; + + for (i = 1, cmdline_len = 0; i < argc; i++) + cmdline_len += grub_strlen (argv[i]) + 1; + + cmdline_len *= sizeof (grub_efi_char16_t); + cmdline = p16 = grub_malloc (cmdline_len); + if (! cmdline) + goto fail; + + for (i = 1; i < argc; i++) + { + char *p8; + + p8 = argv[i]; + while (*p8) + *(p16++) = *(p8++); + + *(p16++) = ' '; + } + *(--p16) = 0; + } + file = grub_file_open (filename, GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE); if (! file) goto fail; - /* Get the root device's device path. */ - dev = grub_device_open (0); + /* Get the device path from filename. */ + char *devname = grub_file_get_device_name (filename); + dev = grub_device_open (devname); + if (devname) + grub_free (devname); if (! dev) goto fail; @@ -283,17 +995,14 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), if (! file_path) goto fail; - grub_printf ("file path: "); - grub_efi_print_device_path (file_path); - - size = grub_file_size (file); - if (!size) + fsize = grub_file_size (file); + if (!fsize) { grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), filename); goto fail; } - pages = (((grub_efi_uintn_t) size + ((1 << 12) - 1)) >> 12); + pages = (((grub_efi_uintn_t) fsize + ((1 << 12) - 1)) >> 12); status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_ANY_PAGES, GRUB_EFI_LOADER_CODE, @@ -307,7 +1016,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), } boot_image = (void *) ((grub_addr_t) address); - if (grub_file_read (file, boot_image, size) != size) + if (grub_file_read (file, boot_image, fsize) != fsize) { if (grub_errno == GRUB_ERR_NONE) grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), @@ -317,7 +1026,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), } #if defined (__i386__) || defined (__x86_64__) - if (size >= (grub_ssize_t) sizeof (struct grub_macho_fat_header)) + if (fsize >= (grub_ssize_t) sizeof (struct grub_macho_fat_header)) { struct grub_macho_fat_header *head = boot_image; if (head->magic @@ -326,6 +1035,14 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_uint32_t i; struct grub_macho_fat_arch *archs = (struct grub_macho_fat_arch *) (head + 1); + + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + { + grub_error (GRUB_ERR_BAD_OS, + "MACHO binaries are forbidden with Secure Boot"); + goto fail; + } + for (i = 0; i < grub_cpu_to_le32 (head->nfat_arch); i++) { if (GRUB_MACHO_CPUTYPE_IS_HOST_CURRENT (archs[i].cputype)) @@ -340,79 +1057,39 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), > ~grub_cpu_to_le32 (archs[i].size) || grub_cpu_to_le32 (archs[i].offset) + grub_cpu_to_le32 (archs[i].size) - > (grub_size_t) size) + > (grub_size_t) fsize) { grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), filename); goto fail; } boot_image = (char *) boot_image + grub_cpu_to_le32 (archs[i].offset); - size = grub_cpu_to_le32 (archs[i].size); + fsize = grub_cpu_to_le32 (archs[i].size); } } #endif - status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path, - boot_image, size, - &image_handle); - if (status != GRUB_EFI_SUCCESS) + rc = grub_linuxefi_secure_validate((void *)(unsigned long)address, fsize); + grub_dprintf ("chain", "linuxefi_secure_validate: %d\n", rc); + if (rc > 0) { - if (status == GRUB_EFI_OUT_OF_RESOURCES) - grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of resources"); - else - grub_error (GRUB_ERR_BAD_OS, "cannot load image"); - - goto fail; - } - - /* LoadImage does not set a device handler when the image is - loaded from memory, so it is necessary to set it explicitly here. - This is a mess. */ - loaded_image = grub_efi_get_loaded_image (image_handle); - if (! loaded_image) - { - grub_error (GRUB_ERR_BAD_OS, "no loaded image available"); - goto fail; + grub_file_close (file); + grub_device_close (dev); + grub_loader_set (grub_secureboot_chainloader_boot, + grub_secureboot_chainloader_unload, 0); + return 0; } - loaded_image->device_handle = dev_handle; - - if (argc > 1) + else if (rc == 0) { - int i, len; - grub_efi_char16_t *p16; - - for (i = 1, len = 0; i < argc; i++) - len += grub_strlen (argv[i]) + 1; - - len *= sizeof (grub_efi_char16_t); - cmdline = p16 = grub_malloc (len); - if (! cmdline) - goto fail; - - for (i = 1; i < argc; i++) - { - char *p8; - - p8 = argv[i]; - while (*p8) - *(p16++) = *(p8++); - - *(p16++) = ' '; - } - *(--p16) = 0; + grub_load_and_start_image(boot_image); + grub_file_close (file); + grub_device_close (dev); + grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0); - loaded_image->load_options = cmdline; - loaded_image->load_options_size = len; + return 0; } - grub_file_close (file); - grub_device_close (dev); - - grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0); - return 0; - - fail: - +fail: if (dev) grub_device_close (dev); @@ -424,6 +1101,9 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), if (address) efi_call_2 (b->free_pages, address, pages); + if (cmdline) + grub_free (cmdline); + grub_dl_unref (my_mod); return grub_errno; diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c index c24202a5dd..c8ecce6dfd 100644 --- a/grub-core/loader/efi/linux.c +++ b/grub-core/loader/efi/linux.c @@ -33,21 +33,34 @@ struct grub_efi_shim_lock }; typedef struct grub_efi_shim_lock grub_efi_shim_lock_t; -grub_efi_boolean_t +int grub_linuxefi_secure_validate (void *data, grub_uint32_t size) { grub_efi_guid_t guid = SHIM_LOCK_GUID; grub_efi_shim_lock_t *shim_lock; + grub_efi_status_t status; shim_lock = grub_efi_locate_protocol(&guid, NULL); - + grub_dprintf ("secureboot", "shim_lock: %p\n", shim_lock); if (!shim_lock) - return 1; + { + grub_dprintf ("secureboot", "shim not available\n"); + return 0; + } + + grub_dprintf ("secureboot", "Asking shim to verify kernel signature\n"); + status = shim_lock->verify (data, size); + grub_dprintf ("secureboot", "shim_lock->verify(): %ld\n", (long int)status); + if (status == GRUB_EFI_SUCCESS) + { + grub_dprintf ("secureboot", "Kernel signature verification passed\n"); + return 1; + } - if (shim_lock->verify(data, size) == GRUB_EFI_SUCCESS) - return 1; + grub_dprintf ("secureboot", "Kernel signature verification failed (0x%lx)\n", + (unsigned long) status); - return 0; + return -1; } #pragma GCC diagnostic push diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index bb2616a809..6b24cbb948 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -117,6 +117,8 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), goto fail; } + grub_dprintf ("linux", "initrd_mem = %lx\n", (unsigned long) initrd_mem); + params->ramdisk_size = size; params->ramdisk_image = (grub_uint32_t)(grub_addr_t) initrd_mem; @@ -159,6 +161,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), struct linux_i386_kernel_header lh; grub_ssize_t len, start, filelen; void *kernel = NULL; + int rc; grub_dl_ref (my_mod); @@ -184,11 +187,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), if (grub_file_read (file, kernel, filelen) != filelen) { - grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"), argv[0]); + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"), + argv[0]); goto fail; } - if (! grub_linuxefi_secure_validate (kernel, filelen)) + rc = grub_linuxefi_secure_validate (kernel, filelen); + if (rc < 0) { grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), argv[0]); @@ -203,6 +208,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } + grub_dprintf ("linux", "params = %lx\n", (unsigned long) params); + grub_memset (params, 0, 16384); grub_memcpy (&lh, kernel, sizeof (lh)); @@ -241,6 +248,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } + grub_dprintf ("linux", "linux_cmdline = %lx\n", + (unsigned long)linux_cmdline); + grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE)); grub_create_loader_cmdline (argc, argv, linux_cmdline + sizeof (LINUX_IMAGE) - 1, @@ -275,9 +285,10 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_memcpy (params, &lh, 2 * 512); params->type_of_loader = 0x21; + grub_dprintf("linux", "kernel_mem: %p handover_offset: %08x\n", + kernel_mem, handover_offset); fail: - if (file) grub_file_close (file); diff --git a/include/grub/efi/linux.h b/include/grub/efi/linux.h index d9ede36773..0033d9305a 100644 --- a/include/grub/efi/linux.h +++ b/include/grub/efi/linux.h @@ -22,7 +22,7 @@ #include #include -grub_efi_boolean_t +int EXPORT_FUNC(grub_linuxefi_secure_validate) (void *data, grub_uint32_t size); grub_err_t EXPORT_FUNC(grub_efi_linux_boot) (void *kernel_address, grub_off_t offset, diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h index 0ed8781f03..a43adf2746 100644 --- a/include/grub/efi/pe32.h +++ b/include/grub/efi/pe32.h @@ -223,7 +223,11 @@ struct grub_pe64_optional_header struct grub_pe32_section_table { char name[8]; - grub_uint32_t virtual_size; + union + { + grub_uint32_t physical_address; + grub_uint32_t virtual_size; + }; grub_uint32_t virtual_address; grub_uint32_t raw_data_size; grub_uint32_t raw_data_offset; @@ -234,12 +238,18 @@ struct grub_pe32_section_table grub_uint32_t characteristics; }; +#define GRUB_PE32_SCN_TYPE_NO_PAD 0x00000008 #define GRUB_PE32_SCN_CNT_CODE 0x00000020 #define GRUB_PE32_SCN_CNT_INITIALIZED_DATA 0x00000040 -#define GRUB_PE32_SCN_MEM_DISCARDABLE 0x02000000 -#define GRUB_PE32_SCN_MEM_EXECUTE 0x20000000 -#define GRUB_PE32_SCN_MEM_READ 0x40000000 -#define GRUB_PE32_SCN_MEM_WRITE 0x80000000 +#define GRUB_PE32_SCN_CNT_UNINITIALIZED_DATA 0x00000080 +#define GRUB_PE32_SCN_LNK_OTHER 0x00000100 +#define GRUB_PE32_SCN_LNK_INFO 0x00000200 +#define GRUB_PE32_SCN_LNK_REMOVE 0x00000800 +#define GRUB_PE32_SCN_LNK_COMDAT 0x00001000 +#define GRUB_PE32_SCN_GPREL 0x00008000 +#define GRUB_PE32_SCN_MEM_16BIT 0x00020000 +#define GRUB_PE32_SCN_MEM_LOCKED 0x00040000 +#define GRUB_PE32_SCN_MEM_PRELOAD 0x00080000 #define GRUB_PE32_SCN_ALIGN_1BYTES 0x00100000 #define GRUB_PE32_SCN_ALIGN_2BYTES 0x00200000 @@ -248,10 +258,28 @@ struct grub_pe32_section_table #define GRUB_PE32_SCN_ALIGN_16BYTES 0x00500000 #define GRUB_PE32_SCN_ALIGN_32BYTES 0x00600000 #define GRUB_PE32_SCN_ALIGN_64BYTES 0x00700000 +#define GRUB_PE32_SCN_ALIGN_128BYTES 0x00800000 +#define GRUB_PE32_SCN_ALIGN_256BYTES 0x00900000 +#define GRUB_PE32_SCN_ALIGN_512BYTES 0x00A00000 +#define GRUB_PE32_SCN_ALIGN_1024BYTES 0x00B00000 +#define GRUB_PE32_SCN_ALIGN_2048BYTES 0x00C00000 +#define GRUB_PE32_SCN_ALIGN_4096BYTES 0x00D00000 +#define GRUB_PE32_SCN_ALIGN_8192BYTES 0x00E00000 #define GRUB_PE32_SCN_ALIGN_SHIFT 20 #define GRUB_PE32_SCN_ALIGN_MASK 7 +#define GRUB_PE32_SCN_LNK_NRELOC_OVFL 0x01000000 +#define GRUB_PE32_SCN_MEM_DISCARDABLE 0x02000000 +#define GRUB_PE32_SCN_MEM_NOT_CACHED 0x04000000 +#define GRUB_PE32_SCN_MEM_NOT_PAGED 0x08000000 +#define GRUB_PE32_SCN_MEM_SHARED 0x10000000 +#define GRUB_PE32_SCN_MEM_EXECUTE 0x20000000 +#define GRUB_PE32_SCN_MEM_READ 0x40000000 +#define GRUB_PE32_SCN_MEM_WRITE 0x80000000 + + + #define GRUB_PE32_SIGNATURE_SIZE 4 struct grub_pe32_header @@ -274,6 +302,20 @@ struct grub_pe32_header #endif }; +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; +}; + struct grub_pe32_fixup_block { grub_uint32_t page_rva; From 68728a66b415d124864e84db66944750d6e8c42f Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 6 Oct 2015 16:09:25 -0400 Subject: [PATCH 008/367] Make any of the loaders that link in efi mode honor secure boot. And in this case "honor" means "even if somebody does link this in, they won't register commands if SB is enabled." Signed-off-by: Peter Jones --- grub-core/commands/iorw.c | 7 ++++++ grub-core/commands/memrw.c | 7 ++++++ grub-core/kern/dl.c | 3 ++- grub-core/kern/efi/efi.c | 34 ------------------------------ grub-core/loader/efi/appleloader.c | 7 ++++++ grub-core/loader/efi/chainloader.c | 1 + grub-core/loader/i386/bsd.c | 7 ++++++ grub-core/loader/i386/linux.c | 7 ++++++ grub-core/loader/i386/pc/linux.c | 7 ++++++ grub-core/loader/multiboot.c | 7 ++++++ grub-core/loader/xnu.c | 7 ++++++ include/grub/efi/efi.h | 1 - include/grub/ia64/linux.h | 0 include/grub/mips/linux.h | 0 include/grub/powerpc/linux.h | 0 include/grub/sparc64/linux.h | 0 16 files changed, 59 insertions(+), 36 deletions(-) create mode 100644 include/grub/ia64/linux.h create mode 100644 include/grub/mips/linux.h create mode 100644 include/grub/powerpc/linux.h create mode 100644 include/grub/sparc64/linux.h diff --git a/grub-core/commands/iorw.c b/grub-core/commands/iorw.c index 584baec8f9..7b2999b14b 100644 --- a/grub-core/commands/iorw.c +++ b/grub-core/commands/iorw.c @@ -24,6 +24,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -119,6 +120,9 @@ grub_cmd_write (grub_command_t cmd, int argc, char **argv) GRUB_MOD_INIT(memrw) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + cmd_read_byte = grub_register_extcmd ("inb", grub_cmd_read, 0, N_("PORT"), N_("Read 8-bit value from PORT."), @@ -147,6 +151,9 @@ GRUB_MOD_INIT(memrw) GRUB_MOD_FINI(memrw) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + grub_unregister_extcmd (cmd_read_byte); grub_unregister_extcmd (cmd_read_word); grub_unregister_extcmd (cmd_read_dword); diff --git a/grub-core/commands/memrw.c b/grub-core/commands/memrw.c index d401a6db0e..39cf3a06db 100644 --- a/grub-core/commands/memrw.c +++ b/grub-core/commands/memrw.c @@ -23,6 +23,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -121,6 +122,9 @@ grub_cmd_write (grub_command_t cmd, int argc, char **argv) GRUB_MOD_INIT(memrw) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + cmd_read_byte = grub_register_extcmd ("read_byte", grub_cmd_read, 0, N_("ADDR"), N_("Read 8-bit value from ADDR."), @@ -149,6 +153,9 @@ GRUB_MOD_INIT(memrw) GRUB_MOD_FINI(memrw) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + grub_unregister_extcmd (cmd_read_byte); grub_unregister_extcmd (cmd_read_word); grub_unregister_extcmd (cmd_read_dword); diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index b714937095..7afb9e6f72 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -32,6 +32,7 @@ #include #include #include +#include /* Platforms where modules are in a readonly area of memory. */ #if defined(GRUB_MACHINE_QEMU) @@ -704,7 +705,7 @@ grub_dl_load_file (const char *filename) grub_dl_t mod = 0; #ifdef GRUB_MACHINE_EFI - if (grub_efi_secure_boot ()) + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) { #if 0 /* This is an error, but grub2-mkconfig still generates a pile of diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index 4a2259aa1c..8cff7be028 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -286,40 +286,6 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid, return grub_efi_get_variable_with_attributes (var, guid, datasize_out, data_out, NULL); } -grub_efi_boolean_t -grub_efi_secure_boot (void) -{ - grub_efi_guid_t efi_var_guid = GRUB_EFI_GLOBAL_VARIABLE_GUID; - grub_size_t datasize; - char *secure_boot = NULL; - char *setup_mode = NULL; - grub_efi_boolean_t ret = 0; - - secure_boot = grub_efi_get_variable("SecureBoot", &efi_var_guid, &datasize); - if (datasize != 1 || !secure_boot) - { - grub_dprintf ("secureboot", "No SecureBoot variable\n"); - goto out; - } - grub_dprintf ("secureboot", "SecureBoot: %d\n", *secure_boot); - - setup_mode = grub_efi_get_variable("SetupMode", &efi_var_guid, &datasize); - if (datasize != 1 || !setup_mode) - { - grub_dprintf ("secureboot", "No SetupMode variable\n"); - goto out; - } - grub_dprintf ("secureboot", "SetupMode: %d\n", *setup_mode); - - if (*secure_boot && !*setup_mode) - ret = 1; - - out: - grub_free (secure_boot); - grub_free (setup_mode); - return ret; -} - #pragma GCC diagnostic ignored "-Wcast-align" /* Search the mods section from the PE32/PE32+ image. This code uses diff --git a/grub-core/loader/efi/appleloader.c b/grub-core/loader/efi/appleloader.c index 74888c463b..585f2b5738 100644 --- a/grub-core/loader/efi/appleloader.c +++ b/grub-core/loader/efi/appleloader.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -227,6 +228,9 @@ static grub_command_t cmd; GRUB_MOD_INIT(appleloader) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + cmd = grub_register_command ("appleloader", grub_cmd_appleloader, N_("[OPTS]"), /* TRANSLATORS: This command is used on EFI to @@ -238,5 +242,8 @@ GRUB_MOD_INIT(appleloader) GRUB_MOD_FINI(appleloader) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + grub_unregister_command (cmd); } diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index e6a8d4ad0e..07c4937898 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include diff --git a/grub-core/loader/i386/bsd.c b/grub-core/loader/i386/bsd.c index 5f3290ce17..54befc2662 100644 --- a/grub-core/loader/i386/bsd.c +++ b/grub-core/loader/i386/bsd.c @@ -40,6 +40,7 @@ #ifdef GRUB_MACHINE_PCBIOS #include #endif +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -2137,6 +2138,9 @@ static grub_command_t cmd_netbsd_module_elf, cmd_openbsd_ramdisk; GRUB_MOD_INIT (bsd) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + /* Net and OpenBSD kernels are often compressed. */ grub_dl_load ("gzio"); @@ -2176,6 +2180,9 @@ GRUB_MOD_INIT (bsd) GRUB_MOD_FINI (bsd) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + grub_unregister_extcmd (cmd_freebsd); grub_unregister_extcmd (cmd_openbsd); grub_unregister_extcmd (cmd_netbsd); diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index dccf3bb300..4aeb0e4b9a 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -37,6 +37,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -1138,6 +1139,9 @@ static grub_command_t cmd_linux, cmd_initrd; GRUB_MOD_INIT(linux) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + cmd_linux = grub_register_command ("linux", grub_cmd_linux, 0, N_("Load Linux.")); cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, @@ -1147,6 +1151,9 @@ GRUB_MOD_INIT(linux) GRUB_MOD_FINI(linux) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + grub_unregister_command (cmd_linux); grub_unregister_command (cmd_initrd); } diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c index 4b1750e360..e3fa1221e8 100644 --- a/grub-core/loader/i386/pc/linux.c +++ b/grub-core/loader/i386/pc/linux.c @@ -36,6 +36,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -487,6 +488,9 @@ static grub_command_t cmd_linux, cmd_linux16, cmd_initrd, cmd_initrd16; GRUB_MOD_INIT(linux16) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + cmd_linux = grub_register_command ("linux", grub_cmd_linux, 0, N_("Load Linux.")); @@ -504,6 +508,9 @@ GRUB_MOD_INIT(linux16) GRUB_MOD_FINI(linux16) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + grub_unregister_command (cmd_linux); grub_unregister_command (cmd_linux16); grub_unregister_command (cmd_initrd); diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c index facb13f3d3..47e481f457 100644 --- a/grub-core/loader/multiboot.c +++ b/grub-core/loader/multiboot.c @@ -50,6 +50,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -444,6 +445,9 @@ static grub_command_t cmd_multiboot, cmd_module; GRUB_MOD_INIT(multiboot) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + cmd_multiboot = #ifdef GRUB_USE_MULTIBOOT2 grub_register_command ("multiboot2", grub_cmd_multiboot, @@ -464,6 +468,9 @@ GRUB_MOD_INIT(multiboot) GRUB_MOD_FINI(multiboot) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + grub_unregister_command (cmd_multiboot); grub_unregister_command (cmd_module); } diff --git a/grub-core/loader/xnu.c b/grub-core/loader/xnu.c index 1c0cf6a430..baa54e652a 100644 --- a/grub-core/loader/xnu.c +++ b/grub-core/loader/xnu.c @@ -35,6 +35,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -1497,6 +1498,9 @@ static grub_extcmd_t cmd_splash; GRUB_MOD_INIT(xnu) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + cmd_kernel = grub_register_command ("xnu_kernel", grub_cmd_xnu_kernel, 0, N_("Load XNU image.")); cmd_kernel64 = grub_register_command ("xnu_kernel64", grub_cmd_xnu_kernel64, @@ -1540,6 +1544,9 @@ GRUB_MOD_INIT(xnu) GRUB_MOD_FINI(xnu) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + #ifndef GRUB_MACHINE_EMU grub_unregister_command (cmd_resume); #endif diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index 6295df85f3..585fa6662b 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -91,7 +91,6 @@ EXPORT_FUNC (grub_efi_set_variable) (const char *var, const grub_efi_guid_t *guid, void *data, grub_size_t datasize); -grub_efi_boolean_t EXPORT_FUNC (grub_efi_secure_boot) (void); int EXPORT_FUNC (grub_efi_compare_device_paths) (const grub_efi_device_path_t *dp1, const grub_efi_device_path_t *dp2); diff --git a/include/grub/ia64/linux.h b/include/grub/ia64/linux.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/include/grub/mips/linux.h b/include/grub/mips/linux.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/include/grub/powerpc/linux.h b/include/grub/powerpc/linux.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/include/grub/sparc64/linux.h b/include/grub/sparc64/linux.h new file mode 100644 index 0000000000..e69de29bb2 From 170c7001876aed0eea297a37ef29f4789ad470db Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 8 Jul 2019 12:32:37 +0200 Subject: [PATCH 009/367] Handle multi-arch (64-on-32) boot in linuxefi loader. Allow booting 64-bit kernels on 32-bit EFI on x86. Signed-off-by: Peter Jones --- grub-core/loader/efi/linux.c | 9 ++- grub-core/loader/i386/efi/linux.c | 114 ++++++++++++++++++++---------- include/grub/i386/linux.h | 7 +- 3 files changed, 91 insertions(+), 39 deletions(-) diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c index c8ecce6dfd..0622dfa48d 100644 --- a/grub-core/loader/efi/linux.c +++ b/grub-core/loader/efi/linux.c @@ -69,12 +69,17 @@ grub_linuxefi_secure_validate (void *data, grub_uint32_t size) typedef void (*handover_func) (void *, grub_efi_system_table_t *, void *); grub_err_t -grub_efi_linux_boot (void *kernel_addr, grub_off_t offset, +grub_efi_linux_boot (void *kernel_addr, grub_off_t handover_offset, void *kernel_params) { handover_func hf; + int offset = 0; - hf = (handover_func)((char *)kernel_addr + offset); +#ifdef __x86_64__ + offset = 512; +#endif + + hf = (handover_func)((char *)kernel_addr + handover_offset + offset); hf (grub_efi_image_handle, grub_efi_system_table, kernel_params); return GRUB_ERR_BUG; diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 6b24cbb948..3017d0f3e5 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -44,14 +44,10 @@ static char *linux_cmdline; static grub_err_t grub_linuxefi_boot (void) { - int offset = 0; - -#ifdef __x86_64__ - offset = 512; -#endif asm volatile ("cli"); - return grub_efi_linux_boot ((char *)kernel_mem, handover_offset + offset, + return grub_efi_linux_boot ((char *)kernel_mem, + handover_offset, params); } @@ -153,14 +149,20 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), return grub_errno; } +#define MIN(a, b) \ + ({ typeof (a) _a = (a); \ + typeof (b) _b = (b); \ + _a < _b ? _a : _b; }) + static grub_err_t grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { grub_file_t file = 0; - struct linux_i386_kernel_header lh; - grub_ssize_t len, start, filelen; + struct linux_i386_kernel_header *lh = NULL; + grub_ssize_t start, filelen; void *kernel = NULL; + int setup_header_end_offset; int rc; grub_dl_ref (my_mod); @@ -200,48 +202,79 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } - params = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(16384)); - + params = grub_efi_allocate_pages_max (0x3fffffff, + BYTES_TO_PAGES(sizeof(*params))); if (! params) { grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters"); goto fail; } - grub_dprintf ("linux", "params = %lx\n", (unsigned long) params); - - grub_memset (params, 0, 16384); - - grub_memcpy (&lh, kernel, sizeof (lh)); - - if (lh.boot_flag != grub_cpu_to_le16 (0xaa55)) + grub_dprintf ("linux", "params = %p\n", params); + + grub_memset (params, 0, sizeof(*params)); + + setup_header_end_offset = *((grub_uint8_t *)kernel + 0x201); + grub_dprintf ("linux", "copying %lu bytes from %p to %p\n", + MIN((grub_size_t)0x202+setup_header_end_offset, + sizeof (*params)) - 0x1f1, + (grub_uint8_t *)kernel + 0x1f1, + (grub_uint8_t *)params + 0x1f1); + grub_memcpy ((grub_uint8_t *)params + 0x1f1, + (grub_uint8_t *)kernel + 0x1f1, + MIN((grub_size_t)0x202+setup_header_end_offset,sizeof (*params)) - 0x1f1); + lh = (struct linux_i386_kernel_header *)params; + grub_dprintf ("linux", "lh is at %p\n", lh); + grub_dprintf ("linux", "checking lh->boot_flag\n"); + if (lh->boot_flag != grub_cpu_to_le16 (0xaa55)) { grub_error (GRUB_ERR_BAD_OS, N_("invalid magic number")); goto fail; } - if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS) + grub_dprintf ("linux", "checking lh->setup_sects\n"); + if (lh->setup_sects > GRUB_LINUX_MAX_SETUP_SECTS) { grub_error (GRUB_ERR_BAD_OS, N_("too many setup sectors")); goto fail; } - if (lh.version < grub_cpu_to_le16 (0x020b)) + grub_dprintf ("linux", "checking lh->version\n"); + if (lh->version < grub_cpu_to_le16 (0x020b)) { grub_error (GRUB_ERR_BAD_OS, N_("kernel too old")); goto fail; } - if (!lh.handover_offset) + grub_dprintf ("linux", "checking lh->handover_offset\n"); + if (!lh->handover_offset) { grub_error (GRUB_ERR_BAD_OS, N_("kernel doesn't support EFI handover")); goto fail; } +#if defined(__x86_64__) || defined(__aarch64__) + grub_dprintf ("linux", "checking lh->xloadflags\n"); + if (!(lh->xloadflags & LINUX_XLF_KERNEL_64)) + { + grub_error (GRUB_ERR_BAD_OS, N_("kernel doesn't support 64-bit CPUs")); + goto fail; + } +#endif + +#if defined(__i386__) + if ((lh->xloadflags & LINUX_XLF_KERNEL_64) && + !(lh->xloadflags & LINUX_XLF_EFI_HANDOVER_32)) + { + grub_error (GRUB_ERR_BAD_OS, + N_("kernel doesn't support 32-bit handover")); + goto fail; + } +#endif + grub_dprintf ("linux", "setting up cmdline\n"); linux_cmdline = grub_efi_allocate_pages_max(0x3fffffff, - BYTES_TO_PAGES(lh.cmdline_size + 1)); - + BYTES_TO_PAGES(lh->cmdline_size + 1)); if (!linux_cmdline) { grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate cmdline")); @@ -254,22 +287,24 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE)); grub_create_loader_cmdline (argc, argv, linux_cmdline + sizeof (LINUX_IMAGE) - 1, - lh.cmdline_size - (sizeof (LINUX_IMAGE) - 1), + lh->cmdline_size - (sizeof (LINUX_IMAGE) - 1), GRUB_VERIFY_KERNEL_CMDLINE); - lh.cmd_line_ptr = (grub_uint32_t)(grub_addr_t)linux_cmdline; + grub_dprintf ("linux", "cmdline:%s\n", linux_cmdline); + grub_dprintf ("linux", "setting lh->cmd_line_ptr\n"); + lh->cmd_line_ptr = (grub_uint32_t)(grub_addr_t)linux_cmdline; - handover_offset = lh.handover_offset; + grub_dprintf ("linux", "computing handover offset\n"); + handover_offset = lh->handover_offset; - start = (lh.setup_sects + 1) * 512; - len = grub_file_size(file) - start; + start = (lh->setup_sects + 1) * 512; - kernel_mem = grub_efi_allocate_pages_max(lh.pref_address, - BYTES_TO_PAGES(lh.init_size)); + kernel_mem = grub_efi_allocate_pages_max(lh->pref_address, + BYTES_TO_PAGES(lh->init_size)); if (!kernel_mem) kernel_mem = grub_efi_allocate_pages_max(0x3fffffff, - BYTES_TO_PAGES(lh.init_size)); + BYTES_TO_PAGES(lh->init_size)); if (!kernel_mem) { @@ -277,14 +312,21 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } - grub_memcpy (kernel_mem, (char *)kernel + start, len); + grub_dprintf ("linux", "kernel_mem = %lx\n", (unsigned long) kernel_mem); + grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0); loaded=1; + grub_dprintf ("linux", "setting lh->code32_start to %p\n", kernel_mem); + lh->code32_start = (grub_uint32_t)(grub_addr_t) kernel_mem; + + grub_memcpy (kernel_mem, (char *)kernel + start, filelen - start); - lh.code32_start = (grub_uint32_t)(grub_uint64_t) kernel_mem; - grub_memcpy (params, &lh, 2 * 512); + grub_dprintf ("linux", "setting lh->type_of_loader\n"); + lh->type_of_loader = 0x6; - params->type_of_loader = 0x21; + grub_dprintf ("linux", "setting lh->ext_loader_{type,ver}\n"); + params->ext_loader_type = 0; + params->ext_loader_ver = 2; grub_dprintf("linux", "kernel_mem: %p handover_offset: %08x\n", kernel_mem, handover_offset); @@ -301,10 +343,10 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), loaded = 0; } - if (linux_cmdline && !loaded) + if (linux_cmdline && lh && !loaded) grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t) linux_cmdline, - BYTES_TO_PAGES(lh.cmdline_size + 1)); + BYTES_TO_PAGES(lh->cmdline_size + 1)); if (kernel_mem && !loaded) grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)kernel_mem, diff --git a/include/grub/i386/linux.h b/include/grub/i386/linux.h index eddf9251d9..25ef52c04e 100644 --- a/include/grub/i386/linux.h +++ b/include/grub/i386/linux.h @@ -138,7 +138,12 @@ struct linux_i386_kernel_header grub_uint32_t kernel_alignment; grub_uint8_t relocatable; grub_uint8_t min_alignment; - grub_uint8_t pad[2]; +#define LINUX_XLF_KERNEL_64 (1<<0) +#define LINUX_XLF_CAN_BE_LOADED_ABOVE_4G (1<<1) +#define LINUX_XLF_EFI_HANDOVER_32 (1<<2) +#define LINUX_XLF_EFI_HANDOVER_64 (1<<3) +#define LINUX_XLF_EFI_KEXEC (1<<4) + grub_uint16_t xloadflags; grub_uint32_t cmdline_size; grub_uint32_t hardware_subarch; grub_uint64_t hardware_subarch_data; From 99aac59161c41564d5f5d69a3f3e8c995e9464c1 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 8 Jul 2019 12:55:29 +0200 Subject: [PATCH 010/367] re-write .gitignore --- .gitignore | 150 ++++++++++++++++++++++++++++++ docs/.gitignore | 5 + grub-core/.gitignore | 16 ++++ grub-core/lib/.gitignore | 1 + include/grub/gcrypt/.gitignore | 2 + po/.gitignore | 5 + util/bash-completion.d/.gitignore | 2 + 7 files changed, 181 insertions(+) create mode 100644 docs/.gitignore create mode 100644 grub-core/.gitignore create mode 100644 grub-core/lib/.gitignore create mode 100644 include/grub/gcrypt/.gitignore create mode 100644 po/.gitignore create mode 100644 util/bash-completion.d/.gitignore diff --git a/.gitignore b/.gitignore index f6a1bd0517..208d1d2325 100644 --- a/.gitignore +++ b/.gitignore @@ -275,3 +275,153 @@ widthspec.bin /xfs_test /xzcompress_test /zfs_test +======= +# things ./autogen.sh will create +/Makefile.utilgcry.def +/ABOUT-NLS +/aclocal.m4 +/autom4te.cache +/build-aux +/configure +/gnulib +/grub-core/lib/gnulib/ +/Makefile + +# things very common editors create that we never want +*~ +.*.sw? +*.patch + +# stuff you're likely to make while building test trees +grub.cfg +/build*/ + +# built objects across the whole tree +Makefile.in +*.a +*.am +*.efi +*.exec +*.image +*.img +*.info +*.lst +*.marker +/m4 +*.mod +*.module +*.o +*.pf2 +*.yy.[ch] +.deps/ +.deps-core/ +.deps-util/ +.dirstamp + +# next are things you get if you do ./configure in the topdir (for e.g. +# "make dist" invocation. +/config-util.h +/config.h +/include/grub/cpu +/include/grub/machine +/INSTALL +/INSTALL.grub +/po/Makefile.in.in +/po/Makevars +/po/Makevars.template +/po/POTFILES +/po/Rules-quot +/stamp-h +/stamp-h1 +bootstrap.log +config.log +config.status + +# stuff "make dist" creates +ChangeLog +grub-*.tar +grub-*.tar.* + +# stuff "make" creates +/[[:digit:]][[:digit:]]_?* +/ascii.h +/build-grub-gen-asciih +/build-grub-gen-widthspec +/build-grub-mkfont +/config-util.h.in +/garbage-gen +/grub*-bios-setup +/grub*-bios-setup.8 +/grub*-editenv +/grub*-editenv.1 +/grub*-file +/grub*-file.1 +/grub*-fs-tester +/grub*-fstest +/grub*-fstest.1 +/grub*-get-kernel-settings +/grub*-get-kernel-settings.3 +/grub*-glue-efi +/grub*-glue-efi.1 +/grub*-install +/grub*-install.8 +/grub*-kbdcomp +/grub*-kbdcomp.1 +/grub*-macbless +/grub*-macbless.8 +/grub*-menulst2cfg +/grub*-menulst2cfg.1 +/grub*-mount +/grub*-mount.1 +/grub*-mkconfig +/grub*-mkconfig.8 +/grub*-mkconfig_lib +/grub*-mkfont +/grub*-mkfont.1 +/grub*-mkimage +/grub*-mkimage.1 +/grub*-mklayout +/grub*-mklayout.1 +/grub*-mknetdir +/grub*-mknetdir.1 +/grub*-mkpasswd-pbkdf2 +/grub*-mkpasswd-pbkdf2.1 +/grub*-mkrelpath +/grub*-mkrelpath.1 +/grub*-mkrescue +/grub*-mkrescue.1 +/grub*-mkstandalone +/grub*-mkstandalone.1 +/grub*-ofpathname +/grub*-ofpathname.8 +/grub*-probe +/grub*-probe.8 +/grub*-reboot +/grub*-reboot.8 +/grub*-render-label +/grub*-render-label.1 +/grub*-script-check +/grub*-script-check.1 +/grub*-set-bootflag +/grub*-set-bootflag.1 +/grub*-set-default +/grub*-set-default.8 +/grub*-set-password +/grub*-set-password.8 +/grub*-shell +/grub*-shell-tester +/grub*-sparc64-setup +/grub*-sparc64-setup.8 +/grub*-syslinux2cfg +/grub*-syslinux2cfg.1 +/grub*-switch-to-blscfg +/grub*-switch-to-blscfg.8 +/grub_fstest.pp +/grub_fstest_init.c +/grub_fstest_init.lst +/grub_script.tab.[ch] +/libgrub.pp +/libgrub_a_init.c +/libgrub_a_init.lst +/stamp-h.in +/widthspec.h diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000000..e1d849ef95 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,5 @@ +/*.in +/Makefile +/stamp-1 +/stamp-vti +/version*.texi diff --git a/grub-core/.gitignore b/grub-core/.gitignore new file mode 100644 index 0000000000..2acce28115 --- /dev/null +++ b/grub-core/.gitignore @@ -0,0 +1,16 @@ +/*.lst +/Makefile +/Makefile.gcry.def +/unidata.c +/build-grub-module-verifier +/gdb_grub +/genmod.sh +/gensyminfo.sh +/gentrigtables +/gmodule.pl +/grub_script.tab.[ch] +/modinfo.sh +/rs_decoder.h +/symlist.c +/symlist.h +/trigtables.c diff --git a/grub-core/lib/.gitignore b/grub-core/lib/.gitignore new file mode 100644 index 0000000000..6815459140 --- /dev/null +++ b/grub-core/lib/.gitignore @@ -0,0 +1 @@ +/libgcrypt-grub/ diff --git a/include/grub/gcrypt/.gitignore b/include/grub/gcrypt/.gitignore new file mode 100644 index 0000000000..8fbf564624 --- /dev/null +++ b/include/grub/gcrypt/.gitignore @@ -0,0 +1,2 @@ +g10lib.h +gcrypt.h diff --git a/po/.gitignore b/po/.gitignore new file mode 100644 index 0000000000..f507e7741e --- /dev/null +++ b/po/.gitignore @@ -0,0 +1,5 @@ +/Makefile +/POTFILES*.in +/grub.pot +/remove-potcdate.sed +/stamp-po diff --git a/util/bash-completion.d/.gitignore b/util/bash-completion.d/.gitignore new file mode 100644 index 0000000000..6813a527ad --- /dev/null +++ b/util/bash-completion.d/.gitignore @@ -0,0 +1,2 @@ +Makefile +grub From 11c34f1a92376671042e7d1ac2b8854bde1301a6 Mon Sep 17 00:00:00 2001 From: Paulo Flabiano Smorigo Date: Thu, 20 Sep 2012 18:07:39 -0300 Subject: [PATCH 011/367] IBM client architecture (CAS) reboot support This is an implementation of IBM client architecture (CAS) reboot for GRUB. There are cases where the POWER firmware must reboot in order to support specific features requested by a kernel. The kernel calls ibm,client-architecture-support and it may either return or reboot with the new feature set. eg: Calling ibm,client-architecture-support.../ Elapsed time since release of system processors: 70959 mins 50 secs Welcome to GRUB! Instead of return to the GRUB menu, it will check if the flag for CAS reboot is set. If so, grub will automatically boot the last booted kernel using the same parameters Signed-off-by: Paulo Flabiano Smorigo [rharwood@redhat.com: commit message rewrap] Signed-off-by: Robbie Harwood --- grub-core/kern/ieee1275/openfw.c | 63 ++++++++++++++++++++++++++++++++ grub-core/normal/main.c | 19 ++++++++++ grub-core/script/execute.c | 7 ++++ include/grub/ieee1275/ieee1275.h | 2 + 4 files changed, 91 insertions(+) diff --git a/grub-core/kern/ieee1275/openfw.c b/grub-core/kern/ieee1275/openfw.c index 4d493ab766..3a6689abb1 100644 --- a/grub-core/kern/ieee1275/openfw.c +++ b/grub-core/kern/ieee1275/openfw.c @@ -591,3 +591,66 @@ grub_ieee1275_get_boot_dev (void) return bootpath; } + +/* Check if it's a CAS reboot. If so, set the script to be executed. */ +int +grub_ieee1275_cas_reboot (char *script) +{ + grub_uint32_t ibm_ca_support_reboot; + grub_uint32_t ibm_fw_nbr_reboots; + char property_value[10]; + grub_ssize_t actual; + grub_ieee1275_ihandle_t options; + + if (grub_ieee1275_finddevice ("/options", &options) < 0) + return -1; + + /* Check two properties, one is enough to get cas reboot value */ + ibm_ca_support_reboot = 0; + if (grub_ieee1275_get_integer_property (grub_ieee1275_chosen, + "ibm,client-architecture-support-reboot", + &ibm_ca_support_reboot, + sizeof (ibm_ca_support_reboot), + &actual) >= 0) + grub_dprintf("ieee1275", "ibm,client-architecture-support-reboot: %u\n", + ibm_ca_support_reboot); + + ibm_fw_nbr_reboots = 0; + if (grub_ieee1275_get_property (options, "ibm,fw-nbr-reboots", + property_value, sizeof (property_value), + &actual) >= 0) + { + property_value[sizeof (property_value) - 1] = 0; + ibm_fw_nbr_reboots = (grub_uint8_t) grub_strtoul (property_value, 0, 10); + grub_dprintf("ieee1275", "ibm,fw-nbr-reboots: %u\n", ibm_fw_nbr_reboots); + } + + if (ibm_ca_support_reboot || ibm_fw_nbr_reboots) + { + if (! grub_ieee1275_get_property_length (options, "boot-last-label", &actual)) + { + if (actual > 1024) + script = grub_realloc (script, actual + 1); + grub_ieee1275_get_property (options, "boot-last-label", script, actual, + &actual); + return 0; + } + } + + grub_ieee1275_set_boot_last_label (""); + + return -1; +} + +int grub_ieee1275_set_boot_last_label (const char *text) +{ + grub_ieee1275_ihandle_t options; + grub_ssize_t actual; + + grub_dprintf("ieee1275", "set boot_last_label (size: %u)\n", grub_strlen(text)); + if (! grub_ieee1275_finddevice ("/options", &options) && + options != (grub_ieee1275_ihandle_t) -1) + grub_ieee1275_set_property (options, "boot-last-label", text, + grub_strlen (text), &actual); + return 0; +} diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index c4ebe9e22a..70614de156 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -34,6 +34,9 @@ #include #include #include +#ifdef GRUB_MACHINE_IEEE1275 +#include +#endif GRUB_MOD_LICENSE ("GPLv3+"); @@ -276,6 +279,22 @@ grub_normal_execute (const char *config, int nested, int batch) { menu = read_config_file (config); +#ifdef GRUB_MACHINE_IEEE1275 + int boot; + boot = 0; + char *script; + script = grub_malloc (1024); + if (! grub_ieee1275_cas_reboot (script)) + { + char *dummy[1] = { NULL }; + if (! grub_script_execute_sourcecode (script)) + boot = 1; + } + grub_free (script); + if (boot) + grub_command_execute ("boot", 0, 0); +#endif + /* Ignore any error. */ grub_errno = GRUB_ERR_NONE; } diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c index 25158407dd..ad80399246 100644 --- a/grub-core/script/execute.c +++ b/grub-core/script/execute.c @@ -28,6 +28,9 @@ #include #include #include +#ifdef GRUB_MACHINE_IEEE1275 +#include +#endif /* Max digits for a char is 3 (0xFF is 255), similarly for an int it is sizeof (int) * 3, and one extra for a possible -ve sign. */ @@ -883,6 +886,10 @@ grub_script_execute_sourcecode (const char *source) grub_err_t ret = 0; struct grub_script *parsed_script; +#ifdef GRUB_MACHINE_IEEE1275 + grub_ieee1275_set_boot_last_label (source); +#endif + while (source) { char *line; diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h index 73e2f46447..0a599607f3 100644 --- a/include/grub/ieee1275/ieee1275.h +++ b/include/grub/ieee1275/ieee1275.h @@ -254,6 +254,8 @@ int EXPORT_FUNC(grub_ieee1275_devalias_next) (struct grub_ieee1275_devalias *ali void EXPORT_FUNC(grub_ieee1275_children_peer) (struct grub_ieee1275_devalias *alias); void EXPORT_FUNC(grub_ieee1275_children_first) (const char *devpath, struct grub_ieee1275_devalias *alias); +int EXPORT_FUNC(grub_ieee1275_cas_reboot) (char *script); +int EXPORT_FUNC(grub_ieee1275_set_boot_last_label) (const char *text); char *EXPORT_FUNC(grub_ieee1275_get_boot_dev) (void); From d039dcc47114d94d0410934d708b436bd2d45d4e Mon Sep 17 00:00:00 2001 From: Paulo Flabiano Smorigo Date: Wed, 24 Apr 2013 10:51:48 -0300 Subject: [PATCH 012/367] for ppc, reset console display attr when clear screen v2: Also use \x0c instead of a literal ^L to make future patches less awkward. This should fix this bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=908519 Signed-off-by: Peter Jones Signed-off-by: Paulo Flabiano Smorigo Signed-off-by: Robbie Harwood --- grub-core/term/terminfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/term/terminfo.c b/grub-core/term/terminfo.c index 85ecf06b4d..05c88dcf49 100644 --- a/grub-core/term/terminfo.c +++ b/grub-core/term/terminfo.c @@ -151,7 +151,7 @@ grub_terminfo_set_current (struct grub_term_output *term, /* Clear the screen. Using serial console, screen(1) only recognizes the * ANSI escape sequence. Using video console, Apple Open Firmware * (version 3.1.1) only recognizes the literal ^L. So use both. */ - data->cls = grub_strdup (" \e[2J"); + data->cls = grub_strdup ("\x0c\e[2J\e[m"); data->reverse_video_on = grub_strdup ("\e[7m"); data->reverse_video_off = grub_strdup ("\e[m"); if (grub_strcmp ("ieee1275", str) == 0) From d9583b49735439d3107d195ceae8f10e8f08a022 Mon Sep 17 00:00:00 2001 From: Paulo Flabiano Smorigo Date: Tue, 11 Jun 2013 15:14:05 -0300 Subject: [PATCH 013/367] Disable GRUB video support for IBM power machines Should fix the problem in bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=973205 Signed-off-by: Paulo Flabiano Smorigo Signed-off-by: Robbie Harwood --- grub-core/kern/ieee1275/cmain.c | 5 ++++- grub-core/video/ieee1275.c | 9 ++++++--- include/grub/ieee1275/ieee1275.h | 2 ++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/grub-core/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c index 20cbbd761e..04df9d2c66 100644 --- a/grub-core/kern/ieee1275/cmain.c +++ b/grub-core/kern/ieee1275/cmain.c @@ -90,7 +90,10 @@ grub_ieee1275_find_options (void) } if (rc >= 0 && grub_strncmp (tmp, "IBM", 3) == 0) - grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_TREE_SCANNING_FOR_DISKS); + { + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_TREE_SCANNING_FOR_DISKS); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT); + } /* Old Macs have no key repeat, newer ones have fully working one. The ones inbetween when repeated key generates an escaoe sequence diff --git a/grub-core/video/ieee1275.c b/grub-core/video/ieee1275.c index 17a3dbbb57..b8e4b3feb3 100644 --- a/grub-core/video/ieee1275.c +++ b/grub-core/video/ieee1275.c @@ -352,9 +352,12 @@ static struct grub_video_adapter grub_video_ieee1275_adapter = GRUB_MOD_INIT(ieee1275_fb) { - find_display (); - if (display) - grub_video_register (&grub_video_ieee1275_adapter); + if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT)) + { + find_display (); + if (display) + grub_video_register (&grub_video_ieee1275_adapter); + } } GRUB_MOD_FINI(ieee1275_fb) diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h index 0a599607f3..b5a1d49bbc 100644 --- a/include/grub/ieee1275/ieee1275.h +++ b/include/grub/ieee1275/ieee1275.h @@ -148,6 +148,8 @@ enum grub_ieee1275_flag GRUB_IEEE1275_FLAG_CURSORONOFF_ANSI_BROKEN, GRUB_IEEE1275_FLAG_RAW_DEVNAMES, + + GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT }; extern int EXPORT_FUNC(grub_ieee1275_test_flag) (enum grub_ieee1275_flag flag); From 6d55ab539cbbe9a4fdae033730f36068dc8cd076 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 3 Apr 2013 14:35:34 -0400 Subject: [PATCH 014/367] Move bash completion script (#922997) Apparently these go in a new place now. --- configure.ac | 11 +++++++++++ util/bash-completion.d/Makefile.am | 1 - 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 7517fc49d9..8331f95b64 100644 --- a/configure.ac +++ b/configure.ac @@ -314,6 +314,14 @@ AC_SUBST(grubdirname) AC_DEFINE_UNQUOTED(GRUB_DIR_NAME, "$grubdirname", [Default grub directory name]) +PKG_PROG_PKG_CONFIG +AS_IF([$($PKG_CONFIG --exists bash-completion)], [ + bashcompletiondir=$($PKG_CONFIG --variable=completionsdir bash-completion) +] , [ + bashcompletiondir=${datadir}/bash-completion/completions +]) +AC_SUBST(bashcompletiondir) + # # Checks for build programs. # @@ -525,6 +533,9 @@ HOST_CFLAGS="$HOST_CFLAGS $grub_cv_cc_w_extra_flags" # Check for target programs. # +# This makes sure pkg.m4 is available. +m4_pattern_forbid([^_?PKG_[A-Z_]+$],[*** pkg.m4 missing, please install pkg-config]) + # Find tools for the target. if test "x$target_alias" != x && test "x$host_alias" != "x$target_alias"; then tmp_ac_tool_prefix="$ac_tool_prefix" diff --git a/util/bash-completion.d/Makefile.am b/util/bash-completion.d/Makefile.am index 136287cf1b..61108f0542 100644 --- a/util/bash-completion.d/Makefile.am +++ b/util/bash-completion.d/Makefile.am @@ -6,7 +6,6 @@ EXTRA_DIST = $(bash_completion_source) CLEANFILES = $(bash_completion_script) config.log -bashcompletiondir = $(sysconfdir)/bash_completion.d bashcompletion_DATA = $(bash_completion_script) $(bash_completion_script): $(bash_completion_source) $(top_builddir)/config.status From a53a35681627f98b31f4331f1f9141d846b2d13c Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 5 Sep 2014 10:07:04 -0400 Subject: [PATCH 015/367] Allow "fallback" to include entries by title, not just number. Resolves: rhbz#1026084 Signed-off-by: Peter Jones --- grub-core/normal/menu.c | 85 ++++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 27 deletions(-) diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c index 8397886fa0..d7a222e681 100644 --- a/grub-core/normal/menu.c +++ b/grub-core/normal/menu.c @@ -163,15 +163,40 @@ grub_menu_set_timeout (int timeout) } } +static int +menuentry_eq (const char *id, const char *spec) +{ + const char *ptr1, *ptr2; + ptr1 = id; + ptr2 = spec; + while (1) + { + if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0) + return ptr2 - spec; + if (*ptr2 == '>' && ptr2[1] != '>') + return 0; + if (*ptr2 == '>') + ptr2++; + if (*ptr1 != *ptr2) + return 0; + if (*ptr1 == 0) + return ptr1 - id; + ptr1++; + ptr2++; + } + return 0; +} + /* Get the first entry number from the value of the environment variable NAME, which is a space-separated list of non-negative integers. The entry number which is returned is stripped from the value of NAME. If no entry number can be found, -1 is returned. */ static int -get_and_remove_first_entry_number (const char *name) +get_and_remove_first_entry_number (grub_menu_t menu, const char *name) { const char *val, *tail; int entry; + int sz = 0; val = grub_env_get (name); if (! val) @@ -181,9 +206,39 @@ get_and_remove_first_entry_number (const char *name) entry = (int) grub_strtoul (val, &tail, 0); + if (grub_errno == GRUB_ERR_BAD_NUMBER) + { + /* See if the variable matches the title of a menu entry. */ + grub_menu_entry_t e = menu->entry_list; + int i; + + for (i = 0; e; i++) + { + sz = menuentry_eq (e->title, val); + if (sz < 1) + sz = menuentry_eq (e->id, val); + + if (sz >= 1) + { + entry = i; + break; + } + e = e->next; + } + + if (sz > 0) + grub_errno = GRUB_ERR_NONE; + + if (! e) + entry = -1; + } + if (grub_errno == GRUB_ERR_NONE) { - /* Skip whitespace to find the next digit. */ + if (sz > 0) + tail += sz; + + /* Skip whitespace to find the next entry. */ while (*tail && grub_isspace (*tail)) tail++; grub_env_set (name, tail); @@ -346,7 +401,7 @@ grub_menu_execute_with_fallback (grub_menu_t menu, grub_menu_execute_entry (entry, 1); /* Deal with fallback entries. */ - while ((fallback_entry = get_and_remove_first_entry_number ("fallback")) + while ((fallback_entry = get_and_remove_first_entry_number (menu, "fallback")) >= 0) { grub_print_error (); @@ -464,30 +519,6 @@ grub_menu_register_viewer (struct grub_menu_viewer *viewer) viewers = viewer; } -static int -menuentry_eq (const char *id, const char *spec) -{ - const char *ptr1, *ptr2; - ptr1 = id; - ptr2 = spec; - while (1) - { - if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0) - return 1; - if (*ptr2 == '>' && ptr2[1] != '>') - return 0; - if (*ptr2 == '>') - ptr2++; - if (*ptr1 != *ptr2) - return 0; - if (*ptr1 == 0) - return 1; - ptr1++; - ptr2++; - } -} - - /* Get the entry number from the variable NAME. */ static int get_entry_number (grub_menu_t menu, const char *name) From a3a099d4494dc381a979d5a274038cc47170adc0 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 26 Feb 2014 21:49:12 -0500 Subject: [PATCH 016/367] Make "exit" take a return code. This adds "exit" with a return code. With this patch, any "exit" command /may/ include a return code, and on platforms that support returning with an exit status, we will do so. By default we return the same exit status we did before this patch. Signed-off-by: Peter Jones --- grub-core/commands/minicmd.c | 20 ++++++++++++++++---- grub-core/kern/efi/efi.c | 9 +++++++-- grub-core/kern/emu/main.c | 2 +- grub-core/kern/emu/misc.c | 5 +++-- grub-core/kern/i386/coreboot/init.c | 2 +- grub-core/kern/i386/qemu/init.c | 2 +- grub-core/kern/ieee1275/init.c | 2 +- grub-core/kern/mips/arc/init.c | 2 +- grub-core/kern/mips/loongson/init.c | 2 +- grub-core/kern/mips/qemu_mips/init.c | 2 +- grub-core/kern/misc.c | 11 ++++++++++- grub-core/kern/uboot/init.c | 6 +++--- grub-core/kern/xen/init.c | 2 +- include/grub/misc.h | 2 +- 14 files changed, 48 insertions(+), 21 deletions(-) diff --git a/grub-core/commands/minicmd.c b/grub-core/commands/minicmd.c index fa498931ed..2bd3ac76f2 100644 --- a/grub-core/commands/minicmd.c +++ b/grub-core/commands/minicmd.c @@ -182,12 +182,24 @@ grub_mini_cmd_lsmod (struct grub_command *cmd __attribute__ ((unused)), } /* exit */ -static grub_err_t __attribute__ ((noreturn)) +static grub_err_t grub_mini_cmd_exit (struct grub_command *cmd __attribute__ ((unused)), - int argc __attribute__ ((unused)), - char *argv[] __attribute__ ((unused))) + int argc, char *argv[]) { - grub_exit (); + int retval = -1; + unsigned long n; + + if (argc < 0 || argc > 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + if (argc == 1) + { + n = grub_strtoul (argv[0], 0, 10); + if (n != ~0UL) + retval = n; + } + + grub_exit (retval); /* Not reached. */ } diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index 8cff7be028..05d8237a9b 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -165,11 +165,16 @@ grub_reboot (void) } void -grub_exit (void) +grub_exit (int retval) { + int rc = GRUB_EFI_LOAD_ERROR; + + if (retval == 0) + rc = GRUB_EFI_SUCCESS; + grub_machine_fini (GRUB_LOADER_FLAG_NORETURN); efi_call_4 (grub_efi_system_table->boot_services->exit, - grub_efi_image_handle, GRUB_EFI_SUCCESS, 0, 0); + grub_efi_image_handle, rc, 0, 0); for (;;) ; } diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c index 425bb96034..55ea5a11cc 100644 --- a/grub-core/kern/emu/main.c +++ b/grub-core/kern/emu/main.c @@ -67,7 +67,7 @@ grub_reboot (void) } void -grub_exit (void) +grub_exit (int retval __attribute__((unused))) { grub_reboot (); } diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c index dfd8a8ec48..0ff13bcaf8 100644 --- a/grub-core/kern/emu/misc.c +++ b/grub-core/kern/emu/misc.c @@ -151,9 +151,10 @@ xasprintf (const char *fmt, ...) #if !defined (GRUB_MACHINE_EMU) || defined (GRUB_UTIL) void -grub_exit (void) +__attribute__ ((noreturn)) +grub_exit (int rc) { - exit (1); + exit (rc < 0 ? 1 : rc); } #endif diff --git a/grub-core/kern/i386/coreboot/init.c b/grub-core/kern/i386/coreboot/init.c index 3314f027fe..36f9134b7b 100644 --- a/grub-core/kern/i386/coreboot/init.c +++ b/grub-core/kern/i386/coreboot/init.c @@ -41,7 +41,7 @@ extern grub_uint8_t _end[]; extern grub_uint8_t _edata[]; void __attribute__ ((noreturn)) -grub_exit (void) +grub_exit (int rc __attribute__((unused))) { /* We can't use grub_fatal() in this function. This would create an infinite loop, since grub_fatal() calls grub_abort() which in turn calls grub_exit(). */ diff --git a/grub-core/kern/i386/qemu/init.c b/grub-core/kern/i386/qemu/init.c index 271b6fbfab..9fafe98f01 100644 --- a/grub-core/kern/i386/qemu/init.c +++ b/grub-core/kern/i386/qemu/init.c @@ -42,7 +42,7 @@ extern grub_uint8_t _end[]; extern grub_uint8_t _edata[]; void __attribute__ ((noreturn)) -grub_exit (void) +grub_exit (int rc __attribute__((unused))) { /* We can't use grub_fatal() in this function. This would create an infinite loop, since grub_fatal() calls grub_abort() which in turn calls grub_exit(). */ diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index d483e35eed..e71d158416 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -71,7 +71,7 @@ grub_addr_t grub_ieee1275_original_stack; #endif void -grub_exit (void) +grub_exit (int rc __attribute__((unused))) { grub_ieee1275_exit (); } diff --git a/grub-core/kern/mips/arc/init.c b/grub-core/kern/mips/arc/init.c index 2ed3ff3191..5c40c34078 100644 --- a/grub-core/kern/mips/arc/init.c +++ b/grub-core/kern/mips/arc/init.c @@ -276,7 +276,7 @@ grub_halt (void) } void -grub_exit (void) +grub_exit (int rc __attribute__((unused))) { GRUB_ARC_FIRMWARE_VECTOR->exit (); diff --git a/grub-core/kern/mips/loongson/init.c b/grub-core/kern/mips/loongson/init.c index 7b96531b98..dff598ca7b 100644 --- a/grub-core/kern/mips/loongson/init.c +++ b/grub-core/kern/mips/loongson/init.c @@ -304,7 +304,7 @@ grub_halt (void) } void -grub_exit (void) +grub_exit (int rc __attribute__((unused))) { grub_halt (); } diff --git a/grub-core/kern/mips/qemu_mips/init.c b/grub-core/kern/mips/qemu_mips/init.c index be88b77d22..8b6c55ffc0 100644 --- a/grub-core/kern/mips/qemu_mips/init.c +++ b/grub-core/kern/mips/qemu_mips/init.c @@ -75,7 +75,7 @@ grub_machine_fini (int flags __attribute__ ((unused))) } void -grub_exit (void) +grub_exit (int rc __attribute__((unused))) { grub_halt (); } diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index 3af336ee22..63b586d09c 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -1209,9 +1209,18 @@ grub_abort (void) grub_getkey (); } - grub_exit (); + grub_exit (1); } +#if defined (__clang__) && !defined (GRUB_UTIL) +/* clang emits references to abort(). */ +void __attribute__ ((noreturn)) +abort (void) +{ + grub_abort (); +} +#endif + void grub_fatal (const char *fmt, ...) { diff --git a/grub-core/kern/uboot/init.c b/grub-core/kern/uboot/init.c index 3e338645c5..be2a5be1d0 100644 --- a/grub-core/kern/uboot/init.c +++ b/grub-core/kern/uboot/init.c @@ -39,9 +39,9 @@ extern grub_size_t grub_total_module_size; static unsigned long timer_start; void -grub_exit (void) +grub_exit (int rc) { - grub_uboot_return (0); + grub_uboot_return (rc < 0 ? 1 : rc); } static grub_uint64_t @@ -78,7 +78,7 @@ grub_machine_init (void) if (!ver) { /* Don't even have a console to log errors to... */ - grub_exit (); + grub_exit (-1); } else if (ver > API_SIG_VERSION) { diff --git a/grub-core/kern/xen/init.c b/grub-core/kern/xen/init.c index 782ca72952..708b060f32 100644 --- a/grub-core/kern/xen/init.c +++ b/grub-core/kern/xen/init.c @@ -584,7 +584,7 @@ grub_machine_init (void) } void -grub_exit (void) +grub_exit (int rc __attribute__((unused))) { struct sched_shutdown arg; diff --git a/include/grub/misc.h b/include/grub/misc.h index 7d2b551969..fd18e6320b 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -353,7 +353,7 @@ int EXPORT_FUNC(grub_vsnprintf) (char *str, grub_size_t n, const char *fmt, char *EXPORT_FUNC(grub_xasprintf) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2))) WARN_UNUSED_RESULT; char *EXPORT_FUNC(grub_xvasprintf) (const char *fmt, va_list args) WARN_UNUSED_RESULT; -void EXPORT_FUNC(grub_exit) (void) __attribute__ ((noreturn)); +void EXPORT_FUNC(grub_exit) (int rc) __attribute__ ((noreturn)); grub_uint64_t EXPORT_FUNC(grub_divmod64) (grub_uint64_t n, grub_uint64_t d, grub_uint64_t *r); From 2a31f4c5faafc7c4acd12cd322b35248b6356276 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 7 Dec 2015 14:20:49 -0500 Subject: [PATCH 017/367] Make efi machines load an env block from a variable Signed-off-by: Peter Jones --- grub-core/Makefile.core.def | 1 + grub-core/kern/efi/init.c | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 45d3edaa4d..c865a08b02 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -207,6 +207,7 @@ kernel = { efi = kern/efi/acpi.c; efi = kern/efi/sb.c; efi = kern/lockdown.c; + efi = lib/envblk.c; i386_coreboot = kern/i386/pc/acpi.c; i386_multiboot = kern/i386/pc/acpi.c; i386_coreboot = kern/acpi.c; diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c index 7facacf09c..6d39bd3ad2 100644 --- a/grub-core/kern/efi/init.c +++ b/grub-core/kern/efi/init.c @@ -27,8 +27,11 @@ #include #include #include + #include +#include + #ifdef GRUB_STACK_PROTECTOR static grub_efi_guid_t rng_protocol_guid = GRUB_EFI_RNG_PROTOCOL_GUID; @@ -82,6 +85,36 @@ stack_protector_init (void) grub_addr_t grub_modbase; +#define GRUB_EFI_GRUB_VARIABLE_GUID \ + { 0x91376aff, 0xcba6, 0x42be, \ + { 0x94, 0x9d, 0x06, 0xfd, 0xe8, 0x11, 0x28, 0xe8 } \ + } + +/* Helper for grub_efi_env_init */ +static int +set_var (const char *name, const char *value, + void *whitelist __attribute__((__unused__))) +{ + grub_env_set (name, value); + return 0; +} + +static void +grub_efi_env_init (void) +{ + grub_efi_guid_t efi_grub_guid = GRUB_EFI_GRUB_VARIABLE_GUID; + struct grub_envblk envblk_s = { NULL, 0 }; + grub_envblk_t envblk = &envblk_s; + + grub_efi_get_variable ("GRUB_ENV", &efi_grub_guid, &envblk_s.size, + (void **) &envblk_s.buf); + if (!envblk_s.buf || envblk_s.size < 1) + return; + + grub_envblk_iterate (envblk, NULL, set_var); + grub_free (envblk_s.buf); +} + void grub_efi_init (void) { @@ -108,10 +141,11 @@ grub_efi_init (void) efi_call_4 (grub_efi_system_table->boot_services->set_watchdog_timer, 0, 0, 0, NULL); + grub_efi_env_init (); grub_efidisk_init (); } -void (*grub_efi_net_config) (grub_efi_handle_t hnd, +void (*grub_efi_net_config) (grub_efi_handle_t hnd, char **device, char **path); From 654eb6c8ab1e58cde31d1a2dfd41999806f4f228 Mon Sep 17 00:00:00 2001 From: Mark Hamzy Date: Wed, 28 Mar 2012 14:46:41 -0500 Subject: [PATCH 018/367] Migrate PPC from Yaboot to Grub2 Add configuration support for serial terminal consoles. This will set the maximum screen size so that text is not overwritten. Signed-off-by: Mark Hamzy Signed-off-by: Robbie Harwood --- Makefile.util.def | 7 ++ util/grub.d/20_ppc_terminfo.in | 114 +++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 util/grub.d/20_ppc_terminfo.in diff --git a/Makefile.util.def b/Makefile.util.def index f8b356cc1f..2c9b283a23 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -508,6 +508,13 @@ script = { condition = COND_HOST_LINUX; }; +script = { + name = '20_ppc_terminfo'; + common = util/grub.d/20_ppc_terminfo.in; + installdir = grubconf; + condition = COND_HOST_LINUX; +}; + script = { name = '30_os-prober'; common = util/grub.d/30_os-prober.in; diff --git a/util/grub.d/20_ppc_terminfo.in b/util/grub.d/20_ppc_terminfo.in new file mode 100644 index 0000000000..10d6658682 --- /dev/null +++ b/util/grub.d/20_ppc_terminfo.in @@ -0,0 +1,114 @@ +#! /bin/sh +set -e + +# grub-mkconfig helper script. +# Copyright (C) 2006,2007,2008,2009,2010 Free Software Foundation, Inc. +# +# GRUB is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# GRUB is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GRUB. If not, see . + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +bindir=@bindir@ +libdir=@libdir@ +. "@datadir@/@PACKAGE@/grub-mkconfig_lib" + +export TEXTDOMAIN=@PACKAGE@ +export TEXTDOMAINDIR=@localedir@ + +X=80 +Y=24 +TERMINAL=ofconsole + +argument () { + opt=$1 + shift + + if test $# -eq 0; then + echo "$0: option requires an argument -- '$opt'" 1>&2 + exit 1 + fi + echo $1 +} + +check_terminfo () { + + while test $# -gt 0 + do + option=$1 + shift + + case "$option" in + terminfo | TERMINFO) + ;; + + -g) + NEWXY=`argument $option "$@"` + NEWX=`echo $NEWXY | cut -d x -f 1` + NEWY=`echo $NEWXY | cut -d x -f 2` + + if [ ${NEWX} -ge 80 ] ; then + X=${NEWX} + else + echo "Warning: ${NEWX} is less than the minimum size of 80" + fi + + if [ ${NEWY} -ge 24 ] ; then + Y=${NEWY} + else + echo "Warning: ${NEWY} is less than the minimum size of 24" + fi + + shift + ;; + + *) +# # accept console or ofconsole +# if [ "$option" != "console" -a "$option" != "ofconsole" ] ; then +# echo "Error: GRUB_TERMINFO unknown console: $option" +# exit 1 +# fi +# # perfer console +# TERMINAL=console + # accept ofconsole + if [ "$option" != "ofconsole" ] ; then + echo "Error: GRUB_TERMINFO unknown console: $option" + exit 1 + fi + # perfer console + TERMINAL=ofconsole + ;; + esac + + done + +} + +if ! uname -m | grep -q ppc ; then + exit 0 +fi + +if [ "x${GRUB_TERMINFO}" != "x" ] ; then + F1=`echo ${GRUB_TERMINFO} | cut -d " " -f 1` + + if [ "${F1}" != "terminfo" ] ; then + echo "Error: GRUB_TERMINFO is set to \"${GRUB_TERMINFO}\" The first word should be terminfo." + exit 1 + fi + + check_terminfo ${GRUB_TERMINFO} +fi + +cat << EOF + terminfo -g ${X}x${Y} ${TERMINAL} +EOF From 07b431a6be7be45c02166661195b33c615c17d90 Mon Sep 17 00:00:00 2001 From: Paulo Flabiano Smorigo Date: Wed, 19 Sep 2012 21:22:55 -0300 Subject: [PATCH 019/367] Add fw_path variable (revised) This patch makes grub look for its config file on efi where the app was found. It was originally written by Matthew Garrett, and adapted to fix the "No modules are loaded on grub2 network boot" issue: https://bugzilla.redhat.com/show_bug.cgi?id=857936 Signed-off-by: Paulo Flabiano Smorigo Signed-off-by: Robbie Harwood --- grub-core/kern/main.c | 13 ++++++------- grub-core/normal/main.c | 25 ++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c index 73967e2f5b..d1de9fa687 100644 --- a/grub-core/kern/main.c +++ b/grub-core/kern/main.c @@ -128,16 +128,15 @@ grub_set_prefix_and_root (void) grub_machine_get_bootlocation (&fwdevice, &fwpath); - if (fwdevice) + if (fwdevice && fwpath) { - char *cmdpath; + char *fw_path; - cmdpath = grub_xasprintf ("(%s)%s", fwdevice, fwpath ? : ""); - if (cmdpath) + fw_path = grub_xasprintf ("(%s)/%s", fwdevice, fwpath); + if (fw_path) { - grub_env_set ("cmdpath", cmdpath); - grub_env_export ("cmdpath"); - grub_free (cmdpath); + grub_env_set ("fw_path", fw_path); + grub_free (fw_path); } } diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 70614de156..62571e6dfc 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -339,7 +339,30 @@ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)), /* Guess the config filename. It is necessary to make CONFIG static, so that it won't get broken by longjmp. */ char *config; - const char *prefix; + const char *prefix, *fw_path; + + fw_path = grub_env_get ("fw_path"); + if (fw_path) + { + config = grub_xasprintf ("%s/grub.cfg", fw_path); + if (config) + { + grub_file_t file; + + file = grub_file_open (config, GRUB_FILE_TYPE_CONFIG); + if (file) + { + grub_file_close (file); + grub_enter_normal_mode (config); + } + else + { + /* Ignore all errors. */ + grub_errno = 0; + } + grub_free (config); + } + } prefix = grub_env_get ("prefix"); if (prefix) From 97f2de6b5a4b7e4b99ccb7ebade5df29d2a95a6a Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 1 Oct 2012 13:24:37 -0400 Subject: [PATCH 020/367] Pass "\x[[:hex:]][[:hex:]]" straight through unmolested. Don't munge raw spaces when we're doing our cmdline escaping (#923374) Signed-off-by: Peter Jones --- grub-core/commands/wildcard.c | 16 ++++++++++++- grub-core/lib/cmdline.c | 25 ++++++++++++++++++-- grub-core/script/execute.c | 43 ++++++++++++++++++++++++++++++----- 3 files changed, 75 insertions(+), 9 deletions(-) diff --git a/grub-core/commands/wildcard.c b/grub-core/commands/wildcard.c index cc3290311f..8f67a4be7f 100644 --- a/grub-core/commands/wildcard.c +++ b/grub-core/commands/wildcard.c @@ -488,6 +488,12 @@ check_file (const char *dir, const char *basename) return ctx.found; } +static int +is_hex(char c) +{ + return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + static void unescape (char *out, const char *in, const char *end) { @@ -496,7 +502,15 @@ unescape (char *out, const char *in, const char *end) for (optr = out, iptr = in; iptr < end;) { - if (*iptr == '\\' && iptr + 1 < end) + if (*iptr == '\\' && iptr + 3 < end && iptr[1] == 'x' && is_hex(iptr[2]) && is_hex(iptr[3])) + { + *optr++ = *iptr++; + *optr++ = *iptr++; + *optr++ = *iptr++; + *optr++ = *iptr++; + continue; + } + else if (*iptr == '\\' && iptr + 1 < end) { *optr++ = iptr[1]; iptr += 2; diff --git a/grub-core/lib/cmdline.c b/grub-core/lib/cmdline.c index ed0b149dca..8e2294d8ff 100644 --- a/grub-core/lib/cmdline.c +++ b/grub-core/lib/cmdline.c @@ -20,6 +20,12 @@ #include #include +static int +is_hex(char c) +{ + return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + static unsigned int check_arg (char *c, int *has_space) { int space = 0; @@ -27,7 +33,13 @@ static unsigned int check_arg (char *c, int *has_space) while (*c) { - if (*c == '\\' || *c == '\'' || *c == '"') + if (*c == '\\' && *(c+1) == 'x' && is_hex(*(c+2)) && is_hex(*(c+3))) + { + size += 4; + c += 4; + continue; + } + else if (*c == '\\' || *c == '\'' || *c == '"') size++; else if (*c == ' ') space = 1; @@ -86,7 +98,16 @@ grub_create_loader_cmdline (int argc, char *argv[], char *buf, while (*c) { - if (*c == '\\' || *c == '\'' || *c == '"') + if (*c == '\\' && *(c+1) == 'x' && + is_hex(*(c+2)) && is_hex(*(c+3))) + { + *buf++ = *c++; + *buf++ = *c++; + *buf++ = *c++; + *buf++ = *c++; + continue; + } + else if (*c == '\\' || *c == '\'' || *c == '"') *buf++ = '\\'; *buf++ = *c; diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c index ad80399246..0c6dd9c520 100644 --- a/grub-core/script/execute.c +++ b/grub-core/script/execute.c @@ -56,6 +56,12 @@ static struct grub_script_scope *scope = 0; /* Wildcard translator for GRUB script. */ struct grub_script_wildcard_translator *grub_wildcard_translator; +static int +is_hex(char c) +{ + return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + static char* wildcard_escape (const char *s) { @@ -72,7 +78,15 @@ wildcard_escape (const char *s) i = 0; while ((ch = *s++)) { - if (ch == '*' || ch == '\\' || ch == '?') + if (ch == '\\' && s[0] == 'x' && is_hex(s[1]) && is_hex(s[2])) + { + p[i++] = ch; + p[i++] = *s++; + p[i++] = *s++; + p[i++] = *s++; + continue; + } + else if (ch == '*' || ch == '\\' || ch == '?') p[i++] = '\\'; p[i++] = ch; } @@ -96,7 +110,14 @@ wildcard_unescape (const char *s) i = 0; while ((ch = *s++)) { - if (ch == '\\') + if (ch == '\\' && s[0] == 'x' && is_hex(s[1]) && is_hex(s[2])) + { + p[i++] = '\\'; + p[i++] = *s++; + p[i++] = *s++; + p[i++] = *s++; + } + else if (ch == '\\') p[i++] = *s++; else p[i++] = ch; @@ -398,10 +419,20 @@ parse_string (const char *str, switch (*ptr) { case '\\': - escaped = !escaped; - if (!escaped && put) - *(put++) = '\\'; - ptr++; + if (!escaped && put && *(ptr+1) == 'x' && is_hex(*(ptr+2)) && is_hex(*(ptr+3))) + { + *(put++) = *ptr++; + *(put++) = *ptr++; + *(put++) = *ptr++; + *(put++) = *ptr++; + } + else + { + escaped = !escaped; + if (!escaped && put) + *(put++) = '\\'; + ptr++; + } break; case '$': if (escaped) From cf06bbc373e3851ee1df5ee4cc3cba20ce9996bf Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 22 Jan 2013 06:31:38 +0100 Subject: [PATCH 021/367] blscfg: add blscfg module to parse Boot Loader Specification snippets The BootLoaderSpec (BLS) defines a scheme where different bootloaders can share a format for boot items and a configuration directory that accepts these common configurations as drop-in files. Signed-off-by: Peter Jones Signed-off-by: Javier Martinez Canillas [wjt: some cleanups and fixes] Signed-off-by: Will Thompson --- grub-core/Makefile.core.def | 11 + grub-core/commands/blscfg.c | 1177 ++++++++++++++++++++++++++++++++ grub-core/commands/legacycfg.c | 5 +- grub-core/commands/loadenv.c | 77 +-- grub-core/commands/loadenv.h | 93 +++ grub-core/commands/menuentry.c | 20 +- grub-core/normal/main.c | 6 + include/grub/compiler.h | 2 + include/grub/menu.h | 13 + include/grub/normal.h | 2 +- 10 files changed, 1324 insertions(+), 82 deletions(-) create mode 100644 grub-core/commands/blscfg.c create mode 100644 grub-core/commands/loadenv.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index c865a08b02..c15e91943b 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -814,6 +814,16 @@ module = { common = commands/blocklist.c; }; +module = { + name = blscfg; + common = commands/blscfg.c; + common = commands/loadenv.h; + enable = powerpc_ieee1275; + enable = efi; + enable = i386_pc; + enable = emu; +}; + module = { name = boot; common = commands/boot.c; @@ -980,6 +990,7 @@ module = { module = { name = loadenv; common = commands/loadenv.c; + common = commands/loadenv.h; common = lib/envblk.c; }; diff --git a/grub-core/commands/blscfg.c b/grub-core/commands/blscfg.c new file mode 100644 index 0000000000..e907a6a5d2 --- /dev/null +++ b/grub-core/commands/blscfg.c @@ -0,0 +1,1177 @@ +/*-*- Mode: C; c-basic-offset: 2; indent-tabs-mode: t -*-*/ + +/* bls.c - implementation of the boot loader spec */ + +/* + * GRUB -- GRand Unified Bootloader + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#include "loadenv.h" + +#define GRUB_BLS_CONFIG_PATH "/loader/entries/" +#ifdef GRUB_MACHINE_EMU +#define GRUB_BOOT_DEVICE "/boot" +#else +#define GRUB_BOOT_DEVICE "($root)" +#endif + +struct keyval +{ + const char *key; + char *val; +}; + +static struct bls_entry *entries = NULL; + +#define FOR_BLS_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries) + +static int bls_add_keyval(struct bls_entry *entry, char *key, char *val) +{ + char *k, *v; + struct keyval **kvs, *kv; + int new_n = entry->nkeyvals + 1; + + kvs = grub_realloc (entry->keyvals, new_n * sizeof (struct keyval *)); + if (!kvs) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "couldn't find space for BLS entry"); + entry->keyvals = kvs; + + kv = grub_malloc (sizeof (struct keyval)); + if (!kv) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "couldn't find space for BLS entry"); + + k = grub_strdup (key); + if (!k) + { + grub_free (kv); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "couldn't find space for BLS entry"); + } + + v = grub_strdup (val); + if (!v) + { + grub_free (k); + grub_free (kv); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "couldn't find space for BLS entry"); + } + + kv->key = k; + kv->val = v; + + entry->keyvals[entry->nkeyvals] = kv; + grub_dprintf("blscfg", "new keyval at %p:%s:%s\n", entry->keyvals[entry->nkeyvals], k, v); + entry->nkeyvals = new_n; + + return 0; +} + +/* Find they value of the key named by keyname. If there are allowed to be + * more than one, pass a pointer to an int set to -1 the first time, and pass + * the same pointer through each time after, and it'll return them in sorted + * order as defined in the BLS fragment file */ +static char *bls_get_val(struct bls_entry *entry, const char *keyname, int *last) +{ + int idx, start = 0; + struct keyval *kv = NULL; + + if (last) + start = *last + 1; + + for (idx = start; idx < entry->nkeyvals; idx++) { + kv = entry->keyvals[idx]; + + if (!grub_strcmp (keyname, kv->key)) + break; + } + + if (idx == entry->nkeyvals) { + if (last) + *last = -1; + return NULL; + } + + if (last) + *last = idx; + + return kv->val; +} + +#define goto_return(x) ({ ret = (x); goto finish; }) + +/* compare alpha and numeric segments of two versions */ +/* return 1: a is newer than b */ +/* 0: a and b are the same version */ +/* -1: b is newer than a */ +static int vercmp(const char * a, const char * b) +{ + char oldch1, oldch2; + char *abuf, *bbuf; + char *str1, *str2; + char * one, * two; + int rc; + int isnum; + int ret = 0; + + grub_dprintf("blscfg", "%s comparing %s and %s\n", __func__, a, b); + if (!grub_strcmp(a, b)) + return 0; + + abuf = grub_malloc(grub_strlen(a) + 1); + bbuf = grub_malloc(grub_strlen(b) + 1); + str1 = abuf; + str2 = bbuf; + grub_strcpy(str1, a); + grub_strcpy(str2, b); + + one = str1; + two = str2; + + /* loop through each version segment of str1 and str2 and compare them */ + while (*one || *two) { + while (*one && !grub_isalnum(*one) && *one != '~' && *one != '+') one++; + while (*two && !grub_isalnum(*two) && *two != '~' && *two != '+') two++; + + /* handle the tilde separator, it sorts before everything else */ + if (*one == '~' || *two == '~') { + if (*one != '~') goto_return (1); + if (*two != '~') goto_return (-1); + one++; + two++; + continue; + } + + /* + * Handle plus separator. Concept is the same as tilde, + * except that if one of the strings ends (base version), + * the other is considered as higher version. + */ + if (*one == '+' || *two == '+') { + if (!*one) return -1; + if (!*two) return 1; + if (*one != '+') goto_return (1); + if (*two != '+') goto_return (-1); + one++; + two++; + continue; + } + + /* If we ran to the end of either, we are finished with the loop */ + if (!(*one && *two)) break; + + str1 = one; + str2 = two; + + /* grab first completely alpha or completely numeric segment */ + /* leave one and two pointing to the start of the alpha or numeric */ + /* segment and walk str1 and str2 to end of segment */ + if (grub_isdigit(*str1)) { + while (*str1 && grub_isdigit(*str1)) str1++; + while (*str2 && grub_isdigit(*str2)) str2++; + isnum = 1; + } else { + while (*str1 && grub_isalpha(*str1)) str1++; + while (*str2 && grub_isalpha(*str2)) str2++; + isnum = 0; + } + + /* save character at the end of the alpha or numeric segment */ + /* so that they can be restored after the comparison */ + oldch1 = *str1; + *str1 = '\0'; + oldch2 = *str2; + *str2 = '\0'; + + /* this cannot happen, as we previously tested to make sure that */ + /* the first string has a non-null segment */ + if (one == str1) goto_return(-1); /* arbitrary */ + + /* take care of the case where the two version segments are */ + /* different types: one numeric, the other alpha (i.e. empty) */ + /* numeric segments are always newer than alpha segments */ + /* XXX See patch #60884 (and details) from bugzilla #50977. */ + if (two == str2) goto_return (isnum ? 1 : -1); + + if (isnum) { + grub_size_t onelen, twolen; + /* this used to be done by converting the digit segments */ + /* to ints using atoi() - it's changed because long */ + /* digit segments can overflow an int - this should fix that. */ + + /* throw away any leading zeros - it's a number, right? */ + while (*one == '0') one++; + while (*two == '0') two++; + + /* whichever number has more digits wins */ + onelen = grub_strlen(one); + twolen = grub_strlen(two); + if (onelen > twolen) goto_return (1); + if (twolen > onelen) goto_return (-1); + } + + /* grub_strcmp will return which one is greater - even if the two */ + /* segments are alpha or if they are numeric. don't return */ + /* if they are equal because there might be more segments to */ + /* compare */ + rc = grub_strcmp(one, two); + if (rc) goto_return (rc < 1 ? -1 : 1); + + /* restore character that was replaced by null above */ + *str1 = oldch1; + one = str1; + *str2 = oldch2; + two = str2; + } + + /* this catches the case where all numeric and alpha segments have */ + /* compared identically but the segment sepparating characters were */ + /* different */ + if ((!*one) && (!*two)) goto_return (0); + + /* whichever version still has characters left over wins */ + if (!*one) goto_return (-1); else goto_return (1); + +finish: + grub_free (abuf); + grub_free (bbuf); + return ret; +} + +/* returns name/version/release */ +/* NULL string pointer returned if nothing found */ +static void +split_package_string (char *package_string, char **name, + char **version, char **release) +{ + char *package_version, *package_release; + + /* Release */ + package_release = grub_strrchr (package_string, '-'); + + if (package_release != NULL) + *package_release++ = '\0'; + + *release = package_release; + + if (name == NULL) + { + *version = package_string; + } + else + { + /* Version */ + package_version = grub_strrchr(package_string, '-'); + + if (package_version != NULL) + *package_version++ = '\0'; + + *version = package_version; + /* Name */ + *name = package_string; + } + + /* Bubble up non-null values from release to name */ + if (name != NULL && *name == NULL) + { + *name = (*version == NULL ? *release : *version); + *version = *release; + *release = NULL; + } + if (*version == NULL) + { + *version = *release; + *release = NULL; + } +} + +static int +split_cmp(char *nvr0, char *nvr1, int has_name) +{ + int ret = 0; + char *name0, *version0, *release0; + char *name1, *version1, *release1; + + split_package_string(nvr0, has_name ? &name0 : NULL, &version0, &release0); + split_package_string(nvr1, has_name ? &name1 : NULL, &version1, &release1); + + if (has_name) + { + ret = vercmp(name0 == NULL ? "" : name0, + name1 == NULL ? "" : name1); + if (ret != 0) + return ret; + } + + ret = vercmp(version0 == NULL ? "" : version0, + version1 == NULL ? "" : version1); + if (ret != 0) + return ret; + + ret = vercmp(release0 == NULL ? "" : release0, + release1 == NULL ? "" : release1); + return ret; +} + +/* return 1: e0 is newer than e1 */ +/* 0: e0 and e1 are the same version */ +/* -1: e1 is newer than e0 */ +static int bls_cmp(const struct bls_entry *e0, const struct bls_entry *e1) +{ + char *id0, *id1; + int r; + + id0 = grub_strdup(e0->filename); + id1 = grub_strdup(e1->filename); + + r = split_cmp(id0, id1, 1); + + grub_free(id0); + grub_free(id1); + + return r; +} + +static void list_add_tail(struct bls_entry *head, struct bls_entry *item) +{ + item->next = head; + if (head->prev) + head->prev->next = item; + item->prev = head->prev; + head->prev = item; +} + +static int bls_add_entry(struct bls_entry *entry) +{ + struct bls_entry *e, *last = NULL; + int rc; + + if (!entries) { + grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename); + entries = entry; + return 0; + } + + FOR_BLS_ENTRIES(e) { + rc = bls_cmp(entry, e); + + if (!rc) + return GRUB_ERR_BAD_ARGUMENT; + + if (rc == 1) { + grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename); + list_add_tail (e, entry); + if (e == entries) { + entries = entry; + entry->prev = NULL; + } + return 0; + } + last = e; + } + + if (last) { + grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename); + last->next = entry; + entry->prev = last; + } + + return 0; +} + +struct read_entry_info { + const char *devid; + const char *dirname; + grub_file_t file; +}; + +static int read_entry ( + const char *filename, + const struct grub_dirhook_info *dirhook_info UNUSED, + void *data) +{ + grub_size_t m = 0, n, clip = 0; + int rc = 0; + char *p = NULL; + grub_file_t f = NULL; + struct bls_entry *entry; + struct read_entry_info *info = (struct read_entry_info *)data; + + grub_dprintf ("blscfg", "filename: \"%s\"\n", filename); + + n = grub_strlen (filename); + + if (info->file) + { + f = info->file; + } + else + { + if (filename[0] == '.') + return 0; + + if (n <= 5) + return 0; + + if (grub_strcmp (filename + n - 5, ".conf") != 0) + return 0; + + p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename); + + f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG); + if (!f) + goto finish; + } + + entry = grub_zalloc (sizeof (*entry)); + if (!entry) + goto finish; + + if (info->file) + { + char *slash; + + if (n > 5 && !grub_strcmp (filename + n - 5, ".conf") == 0) + clip = 5; + + slash = grub_strrchr (filename, '/'); + if (!slash) + slash = grub_strrchr (filename, '\\'); + + while (*slash == '/' || *slash == '\\') + slash++; + + m = slash ? slash - filename : 0; + } + else + { + m = 0; + clip = 5; + } + n -= m; + + entry->filename = grub_strndup(filename + m, n - clip); + if (!entry->filename) + goto finish; + + entry->filename[n - 5] = '\0'; + + for (;;) + { + char *buf; + char *separator; + + buf = grub_file_getline (f); + if (!buf) + break; + + while (buf && buf[0] && (buf[0] == ' ' || buf[0] == '\t')) + buf++; + if (buf[0] == '#') + continue; + + separator = grub_strchr (buf, ' '); + + if (!separator) + separator = grub_strchr (buf, '\t'); + + if (!separator || separator[1] == '\0') + { + grub_free (buf); + break; + } + + separator[0] = '\0'; + + do { + separator++; + } while (*separator == ' ' || *separator == '\t'); + + rc = bls_add_keyval (entry, buf, separator); + grub_free (buf); + if (rc < 0) + break; + } + + if (!rc) + bls_add_entry(entry); + +finish: + if (p) + grub_free (p); + + if (f) + grub_file_close (f); + + return 0; +} + +static grub_envblk_t saved_env = NULL; + +static int UNUSED +save_var (const char *name, const char *value, void *whitelist UNUSED) +{ + const char *val = grub_env_get (name); + grub_dprintf("blscfg", "saving \"%s\"\n", name); + + if (val) + grub_envblk_set (saved_env, name, value); + + return 0; +} + +static int UNUSED +unset_var (const char *name, const char *value UNUSED, void *whitelist) +{ + grub_dprintf("blscfg", "restoring \"%s\"\n", name); + if (! whitelist) + { + grub_env_unset (name); + return 0; + } + + if (test_whitelist_membership (name, + (const grub_env_whitelist_t *) whitelist)) + grub_env_unset (name); + + return 0; +} + +static char **bls_make_list (struct bls_entry *entry, const char *key, int *num) +{ + int last = -1; + char *val; + + int nlist = 0; + char **list = NULL; + + list = grub_malloc (sizeof (char *)); + if (!list) + return NULL; + list[0] = NULL; + + while (1) + { + char **new; + + val = bls_get_val (entry, key, &last); + if (!val) + break; + + new = grub_realloc (list, (nlist + 2) * sizeof (char *)); + if (!new) + break; + + list = new; + list[nlist++] = val; + list[nlist] = NULL; + } + + if (!nlist) + { + grub_free (list); + return NULL; + } + + if (num) + *num = nlist; + + return list; +} + +static char *field_append(bool is_var, char *buffer, const char *start, const char *end) +{ + char *tmp = grub_strndup(start, end - start + 1); + const char *field = tmp; + int term = is_var ? 2 : 1; + + if (is_var) { + field = grub_env_get (tmp); + if (!field) + return buffer; + } + + if (!buffer) + buffer = grub_zalloc (grub_strlen(field) + term); + else + buffer = grub_realloc (buffer, grub_strlen(buffer) + grub_strlen(field) + term); + + if (!buffer) + return NULL; + + tmp = buffer + grub_strlen(buffer); + tmp = grub_stpcpy (tmp, field); + + if (is_var) + tmp = grub_stpcpy (tmp, " "); + + return buffer; +} + +static char *expand_val(const char *value) +{ + char *buffer = NULL; + const char *start = value; + const char *end = value; + bool is_var = false; + + if (!value) + return NULL; + + while (*value) { + if (*value == '$') { + if (start != end) { + buffer = field_append(is_var, buffer, start, end); + if (!buffer) + return NULL; + } + + is_var = true; + start = value + 1; + } else if (is_var) { + if (!grub_isalnum(*value) && *value != '_') { + buffer = field_append(is_var, buffer, start, end); + is_var = false; + start = value; + if (*start == ' ') + start++; + } + } + + end = value; + value++; + } + + if (start != end) { + buffer = field_append(is_var, buffer, start, end); + if (!buffer) + return NULL; + } + + return buffer; +} + +static char **early_initrd_list (const char *initrd) +{ + int nlist = 0; + char **list = NULL; + char *separator; + + while ((separator = grub_strchr (initrd, ' '))) + { + list = grub_realloc (list, (nlist + 2) * sizeof (char *)); + if (!list) + return NULL; + + list[nlist++] = grub_strndup(initrd, separator - initrd); + list[nlist] = NULL; + initrd = separator + 1; + } + + list = grub_realloc (list, (nlist + 2) * sizeof (char *)); + if (!list) + return NULL; + + list[nlist++] = grub_strndup(initrd, grub_strlen(initrd)); + list[nlist] = NULL; + + return list; +} + +static void create_entry (struct bls_entry *entry) +{ + int argc = 0; + const char **argv = NULL; + + char *title = NULL; + char *clinux = NULL; + char *options = NULL; + char **initrds = NULL; + char *initrd = NULL; + const char *early_initrd = NULL; + char **early_initrds = NULL; + char *initrd_prefix = NULL; + char *devicetree = NULL; + char *dt = NULL; + char *id = entry->filename; + char *dotconf = id; + char *hotkey = NULL; + + char *users = NULL; + char **classes = NULL; + + char **args = NULL; + + char *src = NULL; + int i, index; + bool add_dt_prefix = false; + + grub_dprintf("blscfg", "%s got here\n", __func__); + clinux = bls_get_val (entry, "linux", NULL); + if (!clinux) + { + grub_dprintf ("blscfg", "Skipping file %s with no 'linux' key.\n", entry->filename); + goto finish; + } + + /* + * strip the ".conf" off the end before we make it our "id" field. + */ + do + { + dotconf = grub_strstr(dotconf, ".conf"); + } while (dotconf != NULL && dotconf[5] != '\0'); + if (dotconf) + dotconf[0] = '\0'; + + title = bls_get_val (entry, "title", NULL); + options = expand_val (bls_get_val (entry, "options", NULL)); + + if (!options) + options = expand_val (grub_env_get("default_kernelopts")); + + initrds = bls_make_list (entry, "initrd", NULL); + + devicetree = expand_val (bls_get_val (entry, "devicetree", NULL)); + + if (!devicetree) + { + devicetree = expand_val (grub_env_get("devicetree")); + add_dt_prefix = true; + } + + hotkey = bls_get_val (entry, "grub_hotkey", NULL); + users = expand_val (bls_get_val (entry, "grub_users", NULL)); + classes = bls_make_list (entry, "grub_class", NULL); + args = bls_make_list (entry, "grub_arg", &argc); + + argc += 1; + argv = grub_malloc ((argc + 1) * sizeof (char *)); + argv[0] = title ? title : clinux; + for (i = 1; i < argc; i++) + argv[i] = args[i-1]; + argv[argc] = NULL; + + early_initrd = grub_env_get("early_initrd"); + + grub_dprintf ("blscfg", "adding menu entry for \"%s\" with id \"%s\"\n", + title, id); + if (early_initrd) + { + early_initrds = early_initrd_list(early_initrd); + if (!early_initrds) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto finish; + } + + if (initrds != NULL && initrds[0] != NULL) + { + initrd_prefix = grub_strrchr (initrds[0], '/'); + initrd_prefix = grub_strndup(initrds[0], initrd_prefix - initrds[0] + 1); + } + else + { + initrd_prefix = grub_strrchr (clinux, '/'); + initrd_prefix = grub_strndup(clinux, initrd_prefix - clinux + 1); + } + + if (!initrd_prefix) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto finish; + } + } + + if (early_initrds || initrds) + { + int initrd_size = sizeof ("initrd"); + char *tmp; + + for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++) + initrd_size += sizeof (" " GRUB_BOOT_DEVICE) \ + + grub_strlen(initrd_prefix) \ + + grub_strlen (early_initrds[i]) + 1; + + for (i = 0; initrds != NULL && initrds[i] != NULL; i++) + initrd_size += sizeof (" " GRUB_BOOT_DEVICE) \ + + grub_strlen (initrds[i]) + 1; + initrd_size += 1; + + initrd = grub_malloc (initrd_size); + if (!initrd) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto finish; + } + + tmp = grub_stpcpy(initrd, "initrd"); + for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++) + { + grub_dprintf ("blscfg", "adding early initrd %s\n", early_initrds[i]); + tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE); + tmp = grub_stpcpy (tmp, initrd_prefix); + tmp = grub_stpcpy (tmp, early_initrds[i]); + grub_free(early_initrds[i]); + } + + for (i = 0; initrds != NULL && initrds[i] != NULL; i++) + { + grub_dprintf ("blscfg", "adding initrd %s\n", initrds[i]); + tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE); + tmp = grub_stpcpy (tmp, initrds[i]); + } + tmp = grub_stpcpy (tmp, "\n"); + } + + if (devicetree) + { + char *prefix = NULL; + int dt_size; + + if (add_dt_prefix) + { + prefix = grub_strrchr (clinux, '/'); + prefix = grub_strndup(clinux, prefix - clinux + 1); + if (!prefix) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto finish; + } + } + + dt_size = sizeof("devicetree " GRUB_BOOT_DEVICE) + grub_strlen(devicetree) + 1; + + if (add_dt_prefix) + { + dt_size += grub_strlen(prefix); + } + + dt = grub_malloc (dt_size); + if (!dt) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto finish; + } + char *tmp = dt; + tmp = grub_stpcpy (dt, "devicetree"); + tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE); + if (add_dt_prefix) + tmp = grub_stpcpy (tmp, prefix); + tmp = grub_stpcpy (tmp, devicetree); + tmp = grub_stpcpy (tmp, "\n"); + + grub_free(prefix); + } + + grub_dprintf ("blscfg2", "devicetree %s for id:\"%s\"\n", dt, id); + + const char *sdval = grub_env_get("save_default"); + bool savedefault = ((NULL != sdval) && (grub_strcmp(sdval, "true") == 0)); + src = grub_xasprintf ("%sload_video\n" + "set gfxpayload=keep\n" + "insmod gzio\n" + "linux %s%s%s%s\n" + "%s%s", + savedefault ? "savedefault\n" : "", + GRUB_BOOT_DEVICE, clinux, options ? " " : "", options ? options : "", + initrd ? initrd : "", dt ? dt : ""); + + grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, &index, entry); + grub_dprintf ("blscfg", "Added entry %d id:\"%s\"\n", index, id); + +finish: + grub_free (dt); + grub_free (initrd); + grub_free (initrd_prefix); + grub_free (early_initrds); + grub_free (devicetree); + grub_free (initrds); + grub_free (options); + grub_free (classes); + grub_free (args); + grub_free (argv); + grub_free (src); +} + +struct find_entry_info { + const char *dirname; + const char *devid; + grub_device_t dev; + grub_fs_t fs; +}; + +/* + * info: the filesystem object the file is on. + */ +static int find_entry (struct find_entry_info *info) +{ + struct read_entry_info read_entry_info; + grub_fs_t blsdir_fs = NULL; + grub_device_t blsdir_dev = NULL; + const char *blsdir = info->dirname; + int fallback = 0; + int r = 0; + + if (!blsdir) { + blsdir = grub_env_get ("blsdir"); + if (!blsdir) + blsdir = GRUB_BLS_CONFIG_PATH; + } + + read_entry_info.file = NULL; + read_entry_info.dirname = blsdir; + + grub_dprintf ("blscfg", "scanning blsdir: %s\n", blsdir); + + blsdir_dev = info->dev; + blsdir_fs = info->fs; + read_entry_info.devid = info->devid; + +read_fallback: + r = blsdir_fs->fs_dir (blsdir_dev, read_entry_info.dirname, read_entry, + &read_entry_info); + if (r != 0) { + grub_dprintf ("blscfg", "read_entry returned error\n"); + grub_err_t e; + do + { + e = grub_error_pop(); + } while (e); + } + + if (r && !info->dirname && !fallback) { + read_entry_info.dirname = "/boot" GRUB_BLS_CONFIG_PATH; + grub_dprintf ("blscfg", "Entries weren't found in %s, fallback to %s\n", + blsdir, read_entry_info.dirname); + fallback = 1; + goto read_fallback; + } + + return 0; +} + +static grub_err_t +bls_load_entries (const char *path) +{ + grub_size_t len; + grub_fs_t fs; + grub_device_t dev; + static grub_err_t r; + const char *devid = NULL; + char *blsdir = NULL; + struct find_entry_info info = { + .dev = NULL, + .fs = NULL, + .dirname = NULL, + }; + struct read_entry_info rei = { + .devid = NULL, + .dirname = NULL, + }; + + if (path) { + len = grub_strlen (path); + if (grub_strcmp (path + len - 5, ".conf") == 0) { + rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG); + if (!rei.file) + return grub_errno; + /* + * read_entry() closes the file + */ + return read_entry(path, NULL, &rei); + } else if (path[0] == '(') { + devid = path + 1; + + blsdir = grub_strchr (path, ')'); + if (!blsdir) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Filepath isn't correct")); + + *blsdir = '\0'; + blsdir = blsdir + 1; + } + } + + if (!devid) { +#ifdef GRUB_MACHINE_EMU + devid = "host"; +#else + devid = grub_env_get ("root"); +#endif + if (!devid) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, + N_("variable `%s' isn't set"), "root"); + } + + grub_dprintf ("blscfg", "opening %s\n", devid); + dev = grub_device_open (devid); + if (!dev) + return grub_errno; + + grub_dprintf ("blscfg", "probing fs\n"); + fs = grub_fs_probe (dev); + if (!fs) + { + r = grub_errno; + goto finish; + } + + info.dirname = blsdir; + info.devid = devid; + info.dev = dev; + info.fs = fs; + find_entry(&info); + +finish: + if (dev) + grub_device_close (dev); + + return r; +} + +static bool +is_default_entry(const char *def_entry, struct bls_entry *entry, int idx) +{ + const char *title; + int def_idx; + + if (!def_entry) + return false; + + if (grub_strcmp(def_entry, entry->filename) == 0) + return true; + + title = bls_get_val(entry, "title", NULL); + + if (title && grub_strcmp(def_entry, title) == 0) + return true; + + def_idx = (int)grub_strtol(def_entry, NULL, 0); + if (grub_errno == GRUB_ERR_BAD_NUMBER) { + grub_errno = GRUB_ERR_NONE; + return false; + } + + if (def_idx == idx) + return true; + + return false; +} + +static grub_err_t +bls_create_entries (bool show_default, bool show_non_default, char *entry_id) +{ + const char *def_entry = NULL; + struct bls_entry *entry = NULL; + int idx = 0; + + def_entry = grub_env_get("default"); + + grub_dprintf ("blscfg", "%s Creating entries from bls\n", __func__); + FOR_BLS_ENTRIES(entry) { + if (entry->visible) { + idx++; + continue; + } + + if ((show_default && is_default_entry(def_entry, entry, idx)) || + (show_non_default && !is_default_entry(def_entry, entry, idx)) || + (entry_id && grub_strcmp(entry_id, entry->filename) == 0)) { + create_entry(entry); + entry->visible = 1; + } + idx++; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_blscfg (grub_extcmd_context_t ctxt UNUSED, + int argc, char **args) +{ + grub_err_t r; + char *path = NULL; + char *entry_id = NULL; + bool show_default = true; + bool show_non_default = true; + + if (argc == 1) { + if (grub_strcmp (args[0], "default") == 0) { + show_non_default = false; + } else if (grub_strcmp (args[0], "non-default") == 0) { + show_default = false; + } else if (args[0][0] == '(') { + path = args[0]; + } else { + entry_id = args[0]; + show_default = false; + show_non_default = false; + } + } + + r = bls_load_entries(path); + if (r) + return r; + + return bls_create_entries(show_default, show_non_default, entry_id); +} + +static grub_extcmd_t cmd; +static grub_extcmd_t oldcmd; + +GRUB_MOD_INIT(blscfg) +{ + grub_dprintf("blscfg", "%s got here\n", __func__); + cmd = grub_register_extcmd ("blscfg", + grub_cmd_blscfg, + 0, + NULL, + N_("Import Boot Loader Specification snippets."), + NULL); + oldcmd = grub_register_extcmd ("bls_import", + grub_cmd_blscfg, + 0, + NULL, + N_("Import Boot Loader Specification snippets."), + NULL); +} + +GRUB_MOD_FINI(blscfg) +{ + grub_unregister_extcmd (cmd); + grub_unregister_extcmd (oldcmd); +} diff --git a/grub-core/commands/legacycfg.c b/grub-core/commands/legacycfg.c index cc5971f4db..782761c31a 100644 --- a/grub-core/commands/legacycfg.c +++ b/grub-core/commands/legacycfg.c @@ -143,7 +143,7 @@ legacy_file (const char *filename) args[0] = oldname; grub_normal_add_menu_entry (1, args, NULL, NULL, "legacy", NULL, NULL, - entrysrc, 0); + entrysrc, 0, NULL, NULL); grub_free (args); entrysrc[0] = 0; grub_free (oldname); @@ -205,7 +205,8 @@ legacy_file (const char *filename) } args[0] = entryname; grub_normal_add_menu_entry (1, args, NULL, NULL, NULL, - NULL, NULL, entrysrc, 0); + NULL, NULL, entrysrc, 0, NULL, + NULL); grub_free (args); } diff --git a/grub-core/commands/loadenv.c b/grub-core/commands/loadenv.c index 3fd664aac3..163b9a0904 100644 --- a/grub-core/commands/loadenv.c +++ b/grub-core/commands/loadenv.c @@ -28,6 +28,8 @@ #include #include +#include "loadenv.h" + GRUB_MOD_LICENSE ("GPLv3+"); static const struct grub_arg_option options[] = @@ -79,81 +81,6 @@ open_envblk_file (char *filename, return file; } -static grub_envblk_t -read_envblk_file (grub_file_t file) -{ - grub_off_t offset = 0; - char *buf; - grub_size_t size = grub_file_size (file); - grub_envblk_t envblk; - - buf = grub_malloc (size); - if (! buf) - return 0; - - while (size > 0) - { - grub_ssize_t ret; - - ret = grub_file_read (file, buf + offset, size); - if (ret <= 0) - { - grub_free (buf); - return 0; - } - - size -= ret; - offset += ret; - } - - envblk = grub_envblk_open (buf, offset); - if (! envblk) - { - grub_free (buf); - grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block"); - return 0; - } - - return envblk; -} - -struct grub_env_whitelist -{ - grub_size_t len; - char **list; -}; -typedef struct grub_env_whitelist grub_env_whitelist_t; - -static int -test_whitelist_membership (const char* name, - const grub_env_whitelist_t* whitelist) -{ - grub_size_t i; - - for (i = 0; i < whitelist->len; i++) - if (grub_strcmp (name, whitelist->list[i]) == 0) - return 1; /* found it */ - - return 0; /* not found */ -} - -/* Helper for grub_cmd_load_env. */ -static int -set_var (const char *name, const char *value, void *whitelist) -{ - if (! whitelist) - { - grub_env_set (name, value); - return 0; - } - - if (test_whitelist_membership (name, - (const grub_env_whitelist_t *) whitelist)) - grub_env_set (name, value); - - return 0; -} - static grub_err_t grub_cmd_load_env (grub_extcmd_context_t ctxt, int argc, char **args) { diff --git a/grub-core/commands/loadenv.h b/grub-core/commands/loadenv.h new file mode 100644 index 0000000000..952f46121b --- /dev/null +++ b/grub-core/commands/loadenv.h @@ -0,0 +1,93 @@ +/* loadenv.c - command to load/save environment variable. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +static grub_envblk_t UNUSED +read_envblk_file (grub_file_t file) +{ + grub_off_t offset = 0; + char *buf; + grub_size_t size = grub_file_size (file); + grub_envblk_t envblk; + + buf = grub_malloc (size); + if (! buf) + return 0; + + while (size > 0) + { + grub_ssize_t ret; + + ret = grub_file_read (file, buf + offset, size); + if (ret <= 0) + { + grub_free (buf); + return 0; + } + + size -= ret; + offset += ret; + } + + envblk = grub_envblk_open (buf, offset); + if (! envblk) + { + grub_free (buf); + grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block"); + return 0; + } + + return envblk; +} + +struct grub_env_whitelist +{ + grub_size_t len; + char **list; +}; +typedef struct grub_env_whitelist grub_env_whitelist_t; + +static int UNUSED +test_whitelist_membership (const char* name, + const grub_env_whitelist_t* whitelist) +{ + grub_size_t i; + + for (i = 0; i < whitelist->len; i++) + if (grub_strcmp (name, whitelist->list[i]) == 0) + return 1; /* found it */ + + return 0; /* not found */ +} + +/* Helper for grub_cmd_load_env. */ +static int UNUSED +set_var (const char *name, const char *value, void *whitelist) +{ + if (! whitelist) + { + grub_env_set (name, value); + return 0; + } + + if (test_whitelist_membership (name, + (const grub_env_whitelist_t *) whitelist)) + grub_env_set (name, value); + + return 0; +} diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c index 720e6d8ea3..b194123eb6 100644 --- a/grub-core/commands/menuentry.c +++ b/grub-core/commands/menuentry.c @@ -78,7 +78,7 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes, const char *id, const char *users, const char *hotkey, const char *prefix, const char *sourcecode, - int submenu) + int submenu, int *index, struct bls_entry *bls) { int menu_hotkey = 0; char **menu_args = NULL; @@ -149,9 +149,12 @@ grub_normal_add_menu_entry (int argc, const char **args, if (! menu_title) goto fail; + grub_dprintf ("menu", "id:\"%s\"\n", id); + grub_dprintf ("menu", "title:\"%s\"\n", menu_title); menu_id = grub_strdup (id ? : menu_title); if (! menu_id) goto fail; + grub_dprintf ("menu", "menu_id:\"%s\"\n", menu_id); /* Save argc, args to pass as parameters to block arg later. */ menu_args = grub_calloc (argc + 1, sizeof (char *)); @@ -170,8 +173,12 @@ grub_normal_add_menu_entry (int argc, const char **args, } /* Add the menu entry at the end of the list. */ + int ind=0; while (*last) - last = &(*last)->next; + { + ind++; + last = &(*last)->next; + } *last = grub_zalloc (sizeof (**last)); if (! *last) @@ -188,8 +195,11 @@ grub_normal_add_menu_entry (int argc, const char **args, (*last)->args = menu_args; (*last)->sourcecode = menu_sourcecode; (*last)->submenu = submenu; + (*last)->bls = bls; menu->size++; + if (index) + *index = ind; return GRUB_ERR_NONE; fail: @@ -286,7 +296,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) users, ctxt->state[2].arg, 0, ctxt->state[3].arg, - ctxt->extcmd->cmd->name[0] == 's'); + ctxt->extcmd->cmd->name[0] == 's', + NULL, NULL); src = args[argc - 1]; args[argc - 1] = NULL; @@ -303,7 +314,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) ctxt->state[0].args, ctxt->state[4].arg, users, ctxt->state[2].arg, prefix, src + 1, - ctxt->extcmd->cmd->name[0] == 's'); + ctxt->extcmd->cmd->name[0] == 's', NULL, + NULL); src[len - 1] = ch; args[argc - 1] = src; diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 62571e6dfc..7ca2e5400b 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +71,11 @@ grub_normal_free_menu (grub_menu_t menu) grub_free (entry->args); } + if (entry->bls) + { + entry->bls->visible = 0; + } + grub_free ((void *) entry->id); grub_free ((void *) entry->users); grub_free ((void *) entry->title); diff --git a/include/grub/compiler.h b/include/grub/compiler.h index 8f3be3ae70..ebafec6895 100644 --- a/include/grub/compiler.h +++ b/include/grub/compiler.h @@ -56,4 +56,6 @@ # define CLANG_PREREQ(maj,min) 0 #endif +#define UNUSED __attribute__((__unused__)) + #endif /* ! GRUB_COMPILER_HEADER */ diff --git a/include/grub/menu.h b/include/grub/menu.h index ee2b5e9104..0acdc2aa6b 100644 --- a/include/grub/menu.h +++ b/include/grub/menu.h @@ -20,6 +20,16 @@ #ifndef GRUB_MENU_HEADER #define GRUB_MENU_HEADER 1 +struct bls_entry +{ + struct bls_entry *next; + struct bls_entry *prev; + struct keyval **keyvals; + int nkeyvals; + char *filename; + int visible; +}; + struct grub_menu_entry_class { char *name; @@ -60,6 +70,9 @@ struct grub_menu_entry /* The next element. */ struct grub_menu_entry *next; + + /* BLS used to populate the entry */ + struct bls_entry *bls; }; typedef struct grub_menu_entry *grub_menu_entry_t; diff --git a/include/grub/normal.h b/include/grub/normal.h index 218cbabcca..8839ad85a1 100644 --- a/include/grub/normal.h +++ b/include/grub/normal.h @@ -145,7 +145,7 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes, const char *id, const char *users, const char *hotkey, const char *prefix, const char *sourcecode, - int submenu); + int submenu, int *index, struct bls_entry *bls); grub_err_t grub_normal_set_password (const char *user, const char *password); From a6e44102ddf9a1f01be8fa0d3cc94ea8b762a116 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 14 Jan 2014 13:12:23 -0500 Subject: [PATCH 022/367] Add devicetree loading Signed-off-by: Peter Jones Switch to use APM Mustang device tree, for hardware testing. Signed-off-by: David A. Marlin Use the default device tree from the grub default file instead of hardcoding a value. Signed-off-by: David A. Marlin --- util/grub-mkconfig.in | 3 ++- util/grub.d/10_linux.in | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index d3e879b8e5..8ea2315ebc 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -248,7 +248,8 @@ export GRUB_DEFAULT \ GRUB_ENABLE_CRYPTODISK \ GRUB_BADRAM \ GRUB_OS_PROBER_SKIP_LIST \ - GRUB_DISABLE_SUBMENU + GRUB_DISABLE_SUBMENU \ + GRUB_DEFAULT_DTB if test "x${grub_cfg}" != "x"; then rm -f "${grub_cfg}.new" diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index e8b01c0d0c..dc75a1c30b 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -153,6 +153,13 @@ EOF sed "s/^/$submenu_indentation/" << EOF echo '$(echo "$message" | grub_quote)' initrd $(echo $initrd_path) +EOF + fi + if test -n "${fdt}" ; then + message="$(gettext_printf "Loading fdt ...")" + sed "s/^/$submenu_indentation/" << EOF + echo '$(echo "$message" | grub_quote)' + devicetree ${rel_dirname}/${fdt} EOF fi sed "s/^/$submenu_indentation/" << EOF @@ -236,6 +243,14 @@ while [ "x$list" != "x" ] ; do gettext_printf "Found initrd image: %s\n" "$(echo $initrd_display)" >&2 fi + fdt= + for i in "dtb-${version}" "dtb-${alt_version}"; do + if test -f "${dirname}/${i}/${GRUB_DEFAULT_DTB}" ; then + fdt="${i}/${GRUB_DEFAULT_DTB}" + break + fi + done + config= for i in "${dirname}/config-${version}" "${dirname}/config-${alt_version}" "/etc/kernels/kernel-config-${version}" ; do if test -e "${i}" ; then From b9d05a4423bce71c73ba233ff8c17bb1648fe2e0 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 28 Oct 2013 10:09:27 -0400 Subject: [PATCH 023/367] Enable pager by default. (#985860) Signed-off-by: Peter Jones --- util/grub.d/00_header.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in index 93a90233ea..858b526c92 100644 --- a/util/grub.d/00_header.in +++ b/util/grub.d/00_header.in @@ -43,6 +43,8 @@ if [ "x${GRUB_DEFAULT_BUTTON}" = "xsaved" ] ; then GRUB_DEFAULT_BUTTON='${saved_ if [ "x${GRUB_TIMEOUT_BUTTON}" = "x" ] ; then GRUB_TIMEOUT_BUTTON="$GRUB_TIMEOUT" ; fi cat << EOF +set pager=1 + if [ -s \$prefix/grubenv ]; then load_env fi From 47f33ee46ceb39e0f3513e2bd95762b5755f3c39 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 14 Mar 2011 14:27:42 -0400 Subject: [PATCH 024/367] Don't say "GNU/Linux" in generated menus. [rharwood: say it even less] --- grub-core/normal/main.c | 2 +- tests/util/grub-shell-tester.in | 2 +- tests/util/grub-shell.in | 2 +- util/grub.d/10_linux.in | 4 ++-- util/grub.d/20_linux_xen.in | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 7ca2e5400b..98372217ad 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -218,7 +218,7 @@ grub_normal_init_page (struct grub_term_output *term, grub_term_cls (term); - msg_formatted = grub_xasprintf (_("GNU GRUB version %s"), PACKAGE_VERSION); + msg_formatted = grub_xasprintf (_("GRUB version %s"), PACKAGE_VERSION); if (!msg_formatted) return; diff --git a/tests/util/grub-shell-tester.in b/tests/util/grub-shell-tester.in index 8a87109b15..9a4319d4f4 100644 --- a/tests/util/grub-shell-tester.in +++ b/tests/util/grub-shell-tester.in @@ -56,7 +56,7 @@ for option in "$@"; do usage exit 0 ;; -v | --version) - echo "$0 (GNU GRUB ${PACKAGE_VERSION})" + echo "$0 (GRUB ${PACKAGE_VERSION})" exit 0 ;; --modules=*) ms=`echo "$option" | sed -e 's/--modules=//'` diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in index 93e9f51484..ec1182bf93 100644 --- a/tests/util/grub-shell.in +++ b/tests/util/grub-shell.in @@ -209,7 +209,7 @@ for option in "$@"; do usage exit 0 ;; -v | --version) - echo "$0 (GNU GRUB ${PACKAGE_VERSION})" + echo "$0 (GRUB ${PACKAGE_VERSION})" exit 0 ;; --trim) trim=1 diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index dc75a1c30b..4a499c53a6 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -29,9 +29,9 @@ export TEXTDOMAINDIR="@localedir@" CLASS="--class gnu-linux --class gnu --class os" if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then - OS=GNU/Linux + OS="$(sed 's, release .*$,,g' /etc/system-release)" else - OS="${GRUB_DISTRIBUTOR} GNU/Linux" + OS="${GRUB_DISTRIBUTOR}" CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr 'A-Z' 'a-z' | cut -d' ' -f1|LC_ALL=C sed 's,[^[:alnum:]_],_,g') ${CLASS}" fi diff --git a/util/grub.d/20_linux_xen.in b/util/grub.d/20_linux_xen.in index 3b1f470492..ada20775a1 100644 --- a/util/grub.d/20_linux_xen.in +++ b/util/grub.d/20_linux_xen.in @@ -29,9 +29,9 @@ export TEXTDOMAINDIR="@localedir@" CLASS="--class gnu-linux --class gnu --class os --class xen" if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then - OS=GNU/Linux + OS="$(sed 's, release .*$,,g' /etc/system-release)" else - OS="${GRUB_DISTRIBUTOR} GNU/Linux" + OS="${GRUB_DISTRIBUTOR}" CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr 'A-Z' 'a-z' | cut -d' ' -f1|LC_ALL=C sed 's,[^[:alnum:]_],_,g') ${CLASS}" fi From e55b66bec58e13d3c70e3f0717d30cf0eff279f0 Mon Sep 17 00:00:00 2001 From: Fedora Ninjas Date: Mon, 13 Jan 2014 21:50:59 -0500 Subject: [PATCH 025/367] Add .eh_frame to list of relocations stripped --- conf/Makefile.common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/Makefile.common b/conf/Makefile.common index 2a1a886f6d..191b1a70c6 100644 --- a/conf/Makefile.common +++ b/conf/Makefile.common @@ -38,7 +38,7 @@ CFLAGS_KERNEL = $(CFLAGS_PLATFORM) -ffreestanding LDFLAGS_KERNEL = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) CPPFLAGS_KERNEL = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) -DGRUB_KERNEL=1 CCASFLAGS_KERNEL = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM) -STRIPFLAGS_KERNEL = -R .rel.dyn -R .reginfo -R .note -R .comment -R .drectve -R .note.gnu.gold-version -R .MIPS.abiflags -R .ARM.exidx +STRIPFLAGS_KERNEL = -R .eh_frame -R .rel.dyn -R .reginfo -R .note -R .comment -R .drectve -R .note.gnu.gold-version -R .MIPS.abiflags -R .ARM.exidx CFLAGS_MODULE = $(CFLAGS_PLATFORM) -ffreestanding LDFLAGS_MODULE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-r,-d From 3da5c054e9b6d7570d6690ed382aec685aeabad4 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 11 Feb 2014 11:14:50 -0500 Subject: [PATCH 026/367] Don't require a password to boot entries generated by grub-mkconfig. When we set a password, we just want that to mean you can't /edit/ an entry. Resolves: rhbz#1030176 Signed-off-by: Peter Jones --- util/grub.d/10_linux.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 4a499c53a6..cf8d118698 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -26,7 +26,7 @@ datarootdir="@datarootdir@" export TEXTDOMAIN=@PACKAGE@ export TEXTDOMAINDIR="@localedir@" -CLASS="--class gnu-linux --class gnu --class os" +CLASS="--class gnu-linux --class gnu --class os --unrestricted" if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then OS="$(sed 's, release .*$,,g' /etc/system-release)" From a54fe331c775aa79e264a97a856d7ea308a072c4 Mon Sep 17 00:00:00 2001 From: Fedora Ninjas Date: Wed, 19 Feb 2014 15:58:43 -0500 Subject: [PATCH 027/367] use fw_path prefix when fallback searching for grub config When PXE booting via UEFI firmware, grub was searching for grub.cfg in the fw_path directory where the grub application was found. If that didn't exist, a fallback search would look for config file names based on MAC and IP address. However, the search would look in the prefix directory which may not be the same fw_path. This patch changes that behavior to use the fw_path directory for the fallback search. Only if fw_path is NULL will the prefix directory be searched. Signed-off-by: Mark Salter --- grub-core/normal/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 98372217ad..bf24e65713 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -347,7 +347,7 @@ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)), char *config; const char *prefix, *fw_path; - fw_path = grub_env_get ("fw_path"); + prefix = fw_path = grub_env_get ("fw_path"); if (fw_path) { config = grub_xasprintf ("%s/grub.cfg", fw_path); @@ -370,7 +370,8 @@ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)), } } - prefix = grub_env_get ("prefix"); + if (! prefix) + prefix = grub_env_get ("prefix"); if (prefix) { grub_size_t config_len; From 39270546c463613505efc02fa476c66ee5f79fd6 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 8 Jul 2019 17:33:22 +0200 Subject: [PATCH 028/367] Try mac/guid/etc before grub.cfg on tftp config files. Signed-off-by: Peter Jones --- grub-core/normal/main.c | 93 ++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index bf24e65713..0a99768f75 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -345,61 +345,66 @@ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)), /* Guess the config filename. It is necessary to make CONFIG static, so that it won't get broken by longjmp. */ char *config; - const char *prefix, *fw_path; - - prefix = fw_path = grub_env_get ("fw_path"); - if (fw_path) - { - config = grub_xasprintf ("%s/grub.cfg", fw_path); - if (config) - { - grub_file_t file; - - file = grub_file_open (config, GRUB_FILE_TYPE_CONFIG); - if (file) - { - grub_file_close (file); - grub_enter_normal_mode (config); - } - else - { - /* Ignore all errors. */ - grub_errno = 0; - } - grub_free (config); - } - } + const char *prefix; + const char *net_search_cfg; + int disable_net_search = 0; + prefix = grub_env_get ("fw_path"); if (! prefix) prefix = grub_env_get ("prefix"); + + net_search_cfg = grub_env_get ("feature_net_search_cfg"); + if (net_search_cfg && net_search_cfg[0] == 'n') + disable_net_search = 1; + if (prefix) { - grub_size_t config_len; - int disable_net_search = 0; - const char *net_search_cfg; - - config_len = grub_strlen (prefix) + - sizeof ("/grub.cfg-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); - config = grub_malloc (config_len); + if (grub_strncmp (prefix + 1, "tftp", sizeof ("tftp") - 1) == 0 && + !disable_net_search) + { + grub_size_t config_len; + config_len = grub_strlen (prefix) + + sizeof ("/grub.cfg-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); + config = grub_malloc (config_len); - if (!config) - goto quit; + if (! config) + goto quit; - grub_snprintf (config, config_len, "%s/grub.cfg", prefix); + grub_snprintf (config, config_len, "%s/grub.cfg", prefix); - net_search_cfg = grub_env_get ("feature_net_search_cfg"); - if (net_search_cfg && net_search_cfg[0] == 'n') - disable_net_search = 1; + grub_net_search_configfile (config); - if (grub_strncmp (prefix + 1, "tftp", sizeof ("tftp") - 1) == 0 && - !disable_net_search) - grub_net_search_config_file (config); + grub_enter_normal_mode (config); + grub_free (config); + config = NULL; + } - grub_enter_normal_mode (config); - grub_free (config); - } + if (!config) + { + config = grub_xasprintf ("%s/grub.cfg", prefix); + if (config) + { + grub_file_t file; + + file = grub_file_open (config, GRUB_FILE_TYPE_CONFIG); + if (file) + { + grub_file_close (file); + grub_enter_normal_mode (config); + } + else + { + /* Ignore all errors. */ + grub_errno = 0; + } + grub_free (config); + } + } + } else - grub_enter_normal_mode (0); + { + grub_enter_normal_mode (0); + } } else grub_enter_normal_mode (argv[0]); From a432ff40d854c5c13627245892cd963e65661b36 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 4 Sep 2014 14:23:23 -0400 Subject: [PATCH 029/367] Generate OS and CLASS in 10_linux from /etc/os-release This makes us use pretty names in the titles we generate in grub2-mkconfig when GRUB_DISTRIBUTOR isn't set. Resolves: rhbz#996794 Signed-off-by: Peter Jones --- util/grub.d/10_linux.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index cf8d118698..5f6d3c8d52 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -29,7 +29,8 @@ export TEXTDOMAINDIR="@localedir@" CLASS="--class gnu-linux --class gnu --class os --unrestricted" if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then - OS="$(sed 's, release .*$,,g' /etc/system-release)" + OS="$(eval $(grep PRETTY_NAME /etc/os-release) ; echo ${PRETTY_NAME})" + CLASS="--class $(eval $(grep '^ID_LIKE=\|^ID=' /etc/os-release) ; [ -n "${ID_LIKE}" ] && echo ${ID_LIKE} || echo ${ID}) ${CLASS}" else OS="${GRUB_DISTRIBUTOR}" CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr 'A-Z' 'a-z' | cut -d' ' -f1|LC_ALL=C sed 's,[^[:alnum:]_],_,g') ${CLASS}" From 7487cdf5e7bd19e5074376deb9412859c83d42a2 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 4 Sep 2014 15:52:08 -0400 Subject: [PATCH 030/367] Minimize the sort ordering for .debug and -rescue- kernels. Resolves: rhbz#1065360 Signed-off-by: Peter Jones --- util/grub-mkconfig_lib.in | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in index 301d1ac229..0f6505bf3b 100644 --- a/util/grub-mkconfig_lib.in +++ b/util/grub-mkconfig_lib.in @@ -253,6 +253,14 @@ version_test_gt () *.old:*.old) ;; *.old:*) version_test_gt_a="`echo "$version_test_gt_a" | sed -e 's/\.old$//'`" ; version_test_gt_cmp=gt ;; *:*.old) version_test_gt_b="`echo "$version_test_gt_b" | sed -e 's/\.old$//'`" ; version_test_gt_cmp=ge ;; + *-rescue*:*-rescue*) ;; + *?debug:*?debug) ;; + *-rescue*:*?debug) return 1 ;; + *?debug:*-rescue*) return 0 ;; + *-rescue*:*) return 1 ;; + *:*-rescue*) return 0 ;; + *?debug:*) return 1 ;; + *:*?debug) return 0 ;; esac version_test_numeric "$version_test_gt_a" "$version_test_gt_cmp" "$version_test_gt_b" return "$?" From 4311222def5282817822845c08f6683e910f8eb5 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 9 Jul 2019 10:35:16 +0200 Subject: [PATCH 031/367] Try $prefix if $fw_path doesn't work. Related: rhbz#1148652 Signed-off-by: Peter Jones --- grub-core/kern/ieee1275/init.c | 30 ++++---- grub-core/net/net.c | 2 +- grub-core/normal/main.c | 132 ++++++++++++++++----------------- 3 files changed, 82 insertions(+), 82 deletions(-) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index e71d158416..0cd2a62723 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -127,23 +127,25 @@ grub_machine_get_bootlocation (char **device, char **path) grub_free (canon); } else - *device = grub_ieee1275_encode_devname (bootpath); - grub_free (type); - - filename = grub_ieee1275_get_filename (bootpath); - if (filename) { - char *lastslash = grub_strrchr (filename, '\\'); - - /* Truncate at last directory. */ - if (lastslash) + filename = grub_ieee1275_get_filename (bootpath); + if (filename) { - *lastslash = '\0'; - grub_translate_ieee1275_path (filename); - - *path = filename; - } + char *lastslash = grub_strrchr (filename, '\\'); + + /* Truncate at last directory. */ + if (lastslash) + { + *lastslash = '\0'; + grub_translate_ieee1275_path (filename); + + *path = filename; + } + } + *device = grub_ieee1275_encode_devname (bootpath); } + + grub_free (type); grub_free (bootpath); } diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 4d3eb5c1a5..0ef148f4ad 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -1869,7 +1869,7 @@ grub_net_search_config_file (char *config) /* Remove the remaining minus sign at the end. */ config[config_len] = '\0'; - return GRUB_ERR_NONE; + return GRUB_ERR_FILE_NOT_FOUND; } static struct grub_preboot *fini_hnd; diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 0a99768f75..55558cc0b9 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -335,81 +335,79 @@ grub_enter_normal_mode (const char *config) grub_boot_time ("Exiting normal mode"); } +static grub_err_t +grub_try_normal (const char *variable) +{ + char *config; + const char *prefix; + grub_err_t err = GRUB_ERR_FILE_NOT_FOUND; + const char *net_search_cfg; + int disable_net_search = 0; + + prefix = grub_env_get (variable); + if (!prefix) + return GRUB_ERR_FILE_NOT_FOUND; + + net_search_cfg = grub_env_get ("feature_net_search_cfg"); + if (net_search_cfg && net_search_cfg[0] == 'n') + disable_net_search = 1; + + if (grub_strncmp (prefix + 1, "tftp", sizeof ("tftp") - 1) == 0 && + !disable_net_search) + { + grub_size_t config_len; + config_len = grub_strlen (prefix) + + sizeof ("/grub.cfg-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); + config = grub_malloc (config_len); + + if (! config) + return GRUB_ERR_FILE_NOT_FOUND; + + grub_snprintf (config, config_len, "%s/grub.cfg", prefix); + err = grub_net_search_config_file (config); + } + + if (err != GRUB_ERR_NONE) + { + config = grub_xasprintf ("%s/grub.cfg", prefix); + if (config) + { + grub_file_t file; + file = grub_file_open (config, GRUB_FILE_TYPE_CONFIG); + if (file) + { + grub_file_close (file); + err = GRUB_ERR_NONE; + } + } + } + + if (err == GRUB_ERR_NONE) + grub_enter_normal_mode (config); + + grub_errno = 0; + grub_free (config); + return err; +} + /* Enter normal mode from rescue mode. */ static grub_err_t grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)), int argc, char *argv[]) { - if (argc == 0) + if (argc) + grub_enter_normal_mode (argv[0]); + else { - /* Guess the config filename. It is necessary to make CONFIG static, - so that it won't get broken by longjmp. */ - char *config; - const char *prefix; - const char *net_search_cfg; - int disable_net_search = 0; - - prefix = grub_env_get ("fw_path"); - if (! prefix) - prefix = grub_env_get ("prefix"); - - net_search_cfg = grub_env_get ("feature_net_search_cfg"); - if (net_search_cfg && net_search_cfg[0] == 'n') - disable_net_search = 1; - - if (prefix) - { - if (grub_strncmp (prefix + 1, "tftp", sizeof ("tftp") - 1) == 0 && - !disable_net_search) - { - grub_size_t config_len; - config_len = grub_strlen (prefix) + - sizeof ("/grub.cfg-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); - config = grub_malloc (config_len); - - if (! config) - goto quit; - - grub_snprintf (config, config_len, "%s/grub.cfg", prefix); - - grub_net_search_configfile (config); - - grub_enter_normal_mode (config); - grub_free (config); - config = NULL; - } - - if (!config) - { - config = grub_xasprintf ("%s/grub.cfg", prefix); - if (config) - { - grub_file_t file; - - file = grub_file_open (config, GRUB_FILE_TYPE_CONFIG); - if (file) - { - grub_file_close (file); - grub_enter_normal_mode (config); - } - else - { - /* Ignore all errors. */ - grub_errno = 0; - } - grub_free (config); - } - } - } - else - { - grub_enter_normal_mode (0); - } + /* Guess the config filename. */ + grub_err_t err; + err = grub_try_normal ("fw_path"); + if (err == GRUB_ERR_FILE_NOT_FOUND) + err = grub_try_normal ("prefix"); + if (err == GRUB_ERR_FILE_NOT_FOUND) + grub_enter_normal_mode (0); } - else - grub_enter_normal_mode (argv[0]); -quit: return 0; } From 9e20a5b54fe266c6409dc8a70ae4f456c4a3b228 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 28 Apr 2015 11:15:03 -0400 Subject: [PATCH 032/367] Make grub2-mkconfig construct titles that look like the ones we want elsewhere. Resolves: rhbz#1215839 Signed-off-by: Peter Jones --- util/grub.d/10_linux.in | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 5f6d3c8d52..786dbabb4a 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -78,6 +78,32 @@ case x"$GRUB_FS" in ;; esac +mktitle () +{ + local title_type + local version + local OS_NAME + local OS_VERS + + title_type=$1 && shift + version=$1 && shift + + OS_NAME="$(eval $(grep ^NAME= /etc/os-release) ; echo ${NAME})" + OS_VERS="$(eval $(grep ^VERSION= /etc/os-release) ; echo ${VERSION})" + + case $title_type in + recovery) + title=$(printf '%s (%s) %s (recovery mode)' \ + "${OS_NAME}" "${version}" "${OS_VERS}") + ;; + *) + title=$(printf '%s (%s) %s' \ + "${OS_NAME}" "${version}" "${OS_VERS}") + ;; + esac + echo -n ${title} +} + title_correction_code= linux_entry () @@ -91,17 +117,11 @@ linux_entry () boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")" fi if [ x$type != xsimple ] ; then - case $type in - recovery) - title="$(gettext_printf "%s, with Linux %s (recovery mode)" "${os}" "${version}")" ;; - *) - title="$(gettext_printf "%s, with Linux %s" "${os}" "${version}")" ;; - esac + title=$(mktitle "$type" "$version") if [ x"$title" = x"$GRUB_ACTUAL_DEFAULT" ] || [ x"Previous Linux versions>$title" = x"$GRUB_ACTUAL_DEFAULT" ]; then replacement_title="$(echo "Advanced options for ${OS}" | sed 's,>,>>,g')>$(echo "$title" | sed 's,>,>>,g')" quoted="$(echo "$GRUB_ACTUAL_DEFAULT" | grub_quote)" title_correction_code="${title_correction_code}if [ \"x\$default\" = '$quoted' ]; then default='$(echo "$replacement_title" | grub_quote)'; fi;" - grub_warn "$(gettext_printf "Please don't use old title \`%s' for GRUB_DEFAULT, use \`%s' (for versions before 2.00) or \`%s' (for 2.00 or later)" "$GRUB_ACTUAL_DEFAULT" "$replacement_title" "gnulinux-advanced-$boot_device_id>gnulinux-$version-$type-$boot_device_id")" fi echo "menuentry '$(echo "$title" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {" | sed "s/^/$submenu_indentation/" else From 759a67ef844c50f78f47ce283e9ca6e4b6b2ea0e Mon Sep 17 00:00:00 2001 From: Robert Marshall Date: Thu, 25 Jun 2015 11:13:11 -0400 Subject: [PATCH 033/367] Add friendly grub2 password config tool (#985962) Provided a tool for users to reset the grub2 root user password without having to alter the grub.cfg. The hashed password now lives in a root-only-readable configuration file. Resolves: rhbz#985962 Signed-off-by: Robert Marshall [pjones: fix the efidir in grub-setpassword and rename tool] Signed-off-by: Peter Jones [luto: fix grub-setpassword -o's output path] Signed-off-by: Andy Lutomirski [rharwood: migrate man page to h2m, context] Signed-off-by: Robbie Harwood --- Makefile.util.def | 13 ++++ configure.ac | 1 + docs/man/grub-set-password.h2m | 2 + util/grub-mkconfig.in | 2 + util/grub-set-password.in | 128 +++++++++++++++++++++++++++++++++ util/grub.d/01_users.in | 11 +++ 6 files changed, 157 insertions(+) create mode 100644 docs/man/grub-set-password.h2m create mode 100644 util/grub-set-password.in create mode 100644 util/grub.d/01_users.in diff --git a/Makefile.util.def b/Makefile.util.def index 2c9b283a23..4ee22c5daa 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -452,6 +452,12 @@ script = { installdir = grubconf; }; +script = { + name = '01_users'; + common = util/grub.d/01_users.in; + installdir = grubconf; +}; + script = { name = '10_windows'; common = util/grub.d/10_windows.in; @@ -724,6 +730,13 @@ script = { installdir = sbin; }; +script = { + name = grub-set-password; + common = util/grub-set-password.in; + mansection = 8; + installdir = sbin; +}; + script = { name = grub-mkconfig_lib; common = util/grub-mkconfig_lib.in; diff --git a/configure.ac b/configure.ac index 8331f95b64..7f59ad788f 100644 --- a/configure.ac +++ b/configure.ac @@ -72,6 +72,7 @@ grub_TRANSFORM([grub-mkrelpath]) grub_TRANSFORM([grub-mkrescue]) grub_TRANSFORM([grub-probe]) grub_TRANSFORM([grub-reboot]) +grub_TRANSFORM([grub-set-password]) grub_TRANSFORM([grub-script-check]) grub_TRANSFORM([grub-set-default]) grub_TRANSFORM([grub-sparc64-setup]) diff --git a/docs/man/grub-set-password.h2m b/docs/man/grub-set-password.h2m new file mode 100644 index 0000000000..10ee82f4d5 --- /dev/null +++ b/docs/man/grub-set-password.h2m @@ -0,0 +1,2 @@ +[NAME] +grub-set-password \- generate the user.cfg file containing the hashed grub bootloader password diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index 8ea2315ebc..ba14cf6261 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -276,6 +276,8 @@ for i in "${grub_mkconfig_dir}"/* ; do *~) ;; # emacsen autosave files. FIXME: support other editors */\#*\#) ;; + # rpm config files of yore. + *.rpmsave|*.rpmnew|*.rpmorig) ;; *) if grub_file_is_not_garbage "$i" && test -x "$i" ; then echo diff --git a/util/grub-set-password.in b/util/grub-set-password.in new file mode 100644 index 0000000000..5ebf50576d --- /dev/null +++ b/util/grub-set-password.in @@ -0,0 +1,128 @@ +#!/bin/sh -e + +EFIDIR=$(grep ^ID= /etc/os-release | sed -e 's/^ID=//' -e 's/rhel/redhat/') +if [ -d /sys/firmware/efi/efivars/ ]; then + grubdir=`echo "/@bootdirname@/efi/EFI/${EFIDIR}/" | sed 's,//*,/,g'` +else + grubdir=`echo "/@bootdirname@/@grubdirname@" | sed 's,//*,/,g'` +fi + +PACKAGE_VERSION="@PACKAGE_VERSION@" +PACKAGE_NAME="@PACKAGE_NAME@" +self=`basename $0` +bindir="@bindir@" +grub_mkpasswd="${bindir}/@grub_mkpasswd_pbkdf2@" + +# Usage: usage +# Print the usage. +usage () { + cat < put user.cfg in a user-selected directory + +Report bugs at https://bugzilla.redhat.com. +EOF +} + +argument () { + opt=$1 + shift + + if test $# -eq 0; then + gettext_printf "%s: option requires an argument -- \`%s'\n" "$self" "$opt" 1>&2 + exit 1 + fi + echo $1 +} + +# Ensure that it's the root user running this script +if [ "${EUID}" -ne 0 ]; then + echo "The grub bootloader password may only be set by root." + usage + exit 2 +fi + +# Check the arguments. +while test $# -gt 0 +do + option=$1 + shift + + case "$option" in + -h | --help) + usage + exit 0 ;; + -v | --version) + echo "$self (${PACKAGE_NAME}) ${PACKAGE_VERSION}" + exit 0 ;; + -o | --output) + OUTPUT_PATH=`argument $option "$@"`; shift ;; + --output=*) + OUTPUT_PATH=`echo "$option" | sed 's/--output=//'` ;; + -o=*) + OUTPUT_PATH=`echo "$option" | sed 's/-o=//'` ;; + esac +done + +# set user input or default path for user.cfg file +if [ -z "${OUTPUT_PATH}" ]; then + OUTPUT_PATH="${grubdir}" +fi + +if [ ! -d "${OUTPUT_PATH}" ]; then + echo "${OUTPUT_PATH} does not exist." + usage + exit 2; +fi + +ttyopt=$(stty -g) +fixtty() { + stty ${ttyopt} +} + +trap fixtty EXIT +stty -echo + +# prompt & confirm new grub2 root user password +echo -n "Enter password: " +read PASSWORD +echo +echo -n "Confirm password: " +read PASSWORD_CONFIRM +echo +stty ${ttyopt} + +getpass() { + local P0 + local P1 + P0="$1" && shift + P1="$1" && shift + + ( echo ${P0} ; echo ${P1} ) | \ + LC_ALL=C ${grub_mkpasswd} | \ + grep -v '[eE]nter password:' | \ + sed -e "s/PBKDF2 hash of your password is //" +} + +MYPASS="$(getpass "${PASSWORD}" "${PASSWORD_CONFIRM}")" +if [ -z "${MYPASS}" ]; then + echo "${self}: error: empty password" 1>&2 + exit 1 +fi + +# on the ESP, these will fail to set the permissions, but it's okay because +# the directory is protected. +install -m 0600 /dev/null "${OUTPUT_PATH}/user.cfg" 2>/dev/null || : +chmod 0600 "${OUTPUT_PATH}/user.cfg" 2>/dev/null || : +echo "GRUB2_PASSWORD=${MYPASS}" > "${OUTPUT_PATH}/user.cfg" + +if ! grep -q "^### BEGIN /etc/grub.d/01_users ###$" "${OUTPUT_PATH}/grub.cfg"; then + echo "WARNING: The current configuration lacks password support!" + echo "Update your configuration with @grub_mkconfig@ to support this feature." +fi diff --git a/util/grub.d/01_users.in b/util/grub.d/01_users.in new file mode 100644 index 0000000000..db2f44bfb7 --- /dev/null +++ b/util/grub.d/01_users.in @@ -0,0 +1,11 @@ +#!/bin/sh -e +cat << EOF +if [ -f \${prefix}/user.cfg ]; then + source \${prefix}/user.cfg + if [ -n "\${GRUB2_PASSWORD}" ]; then + set superusers="root" + export superusers + password_pbkdf2 root \${GRUB2_PASSWORD} + fi +fi +EOF From 402b0ff839ca7f0febd6a285f97af6f03dbe6664 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 12 Aug 2015 08:57:55 -0700 Subject: [PATCH 034/367] tcp: add window scaling support Sometimes we have to provision boxes across regions, such as California to Sweden. The http server has a 10 minute timeout, so if we can't get our 250mb image transferred fast enough our provisioning fails, which is not ideal. So add tcp window scaling on open connections and set the window size to 1mb. With this change we're able to get higher sustained transfers between regions and can transfer our image in well below 10 minutes. Without this patch we'd time out every time halfway through the transfer. Thanks, Signed-off-by: Josef Bacik --- grub-core/net/tcp.c | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index e8ad34b84d..7d4b822626 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -106,6 +106,18 @@ struct tcphdr grub_uint16_t urgent; } GRUB_PACKED; +struct tcp_scale_opt { + grub_uint8_t kind; + grub_uint8_t length; + grub_uint8_t scale; +} GRUB_PACKED; + +struct tcp_synhdr { + struct tcphdr tcphdr; + struct tcp_scale_opt scale_opt; + grub_uint8_t padding; +}; + struct tcp_pseudohdr { grub_uint32_t src; @@ -566,7 +578,7 @@ grub_net_tcp_open (char *server, grub_net_tcp_socket_t socket; static grub_uint16_t in_port = 21550; struct grub_net_buff *nb; - struct tcphdr *tcph; + struct tcp_synhdr *tcph; int i; grub_uint8_t *nbd; grub_net_link_level_address_t ll_target_addr; @@ -635,20 +647,24 @@ grub_net_tcp_open (char *server, } tcph = (void *) nb->data; + grub_memset(tcph, 0, sizeof (*tcph)); socket->my_start_seq = grub_get_time_ms (); socket->my_cur_seq = socket->my_start_seq + 1; - socket->my_window = 8192; - tcph->seqnr = grub_cpu_to_be32 (socket->my_start_seq); - tcph->ack = grub_cpu_to_be32_compile_time (0); - tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_SYN); - tcph->window = grub_cpu_to_be16 (socket->my_window); - tcph->urgent = 0; - tcph->src = grub_cpu_to_be16 (socket->in_port); - tcph->dst = grub_cpu_to_be16 (socket->out_port); - tcph->checksum = 0; - tcph->checksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_TCP, - &socket->inf->address, - &socket->out_nla); + socket->my_window = 32768; + tcph->tcphdr.seqnr = grub_cpu_to_be32 (socket->my_start_seq); + tcph->tcphdr.ack = grub_cpu_to_be32_compile_time (0); + tcph->tcphdr.flags = grub_cpu_to_be16_compile_time ((6 << 12) | TCP_SYN); + tcph->tcphdr.window = grub_cpu_to_be16 (socket->my_window); + tcph->tcphdr.urgent = 0; + tcph->tcphdr.src = grub_cpu_to_be16 (socket->in_port); + tcph->tcphdr.dst = grub_cpu_to_be16 (socket->out_port); + tcph->tcphdr.checksum = 0; + tcph->scale_opt.kind = 3; + tcph->scale_opt.length = 3; + tcph->scale_opt.scale = 5; + tcph->tcphdr.checksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_TCP, + &socket->inf->address, + &socket->out_nla); tcp_socket_register (socket); From a54670aa25a0a9e5c13a7546b7f1f9b6511c4bb6 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 9 Jul 2019 11:47:37 +0200 Subject: [PATCH 035/367] efinet and bootp: add support for dhcpv6 Signed-off-by: Peter Jones --- grub-core/net/bootp.c | 173 +++++++++++++++++++++++++++++ grub-core/net/drivers/efi/efinet.c | 53 +++++++-- grub-core/net/net.c | 72 ++++++++++++ grub-core/net/tftp.c | 4 + include/grub/efi/api.h | 129 ++++++++++++++++++++- include/grub/net.h | 60 ++++++++++ 6 files changed, 477 insertions(+), 14 deletions(-) diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index 6fb5627025..e28fb6a09f 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -902,6 +902,179 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), static grub_command_t cmd_getdhcp, cmd_bootp, cmd_dhcp; +struct grub_net_network_level_interface * +grub_net_configure_by_dhcpv6_ack (const char *name, + struct grub_net_card *card, + grub_net_interface_flags_t flags + __attribute__((__unused__)), + const grub_net_link_level_address_t *hwaddr, + const struct grub_net_dhcpv6_packet *packet, + int is_def, char **device, char **path) +{ + struct grub_net_network_level_interface *inter = NULL; + struct grub_net_network_level_address addr; + int mask = -1; + + if (!device || !path) + return NULL; + + *device = 0; + *path = 0; + + grub_dprintf ("net", "mac address is %02x:%02x:%02x:%02x:%02x:%02x\n", + hwaddr->mac[0], hwaddr->mac[1], hwaddr->mac[2], + hwaddr->mac[3], hwaddr->mac[4], hwaddr->mac[5]); + + if (is_def) + grub_net_default_server = 0; + + if (is_def && !grub_net_default_server && packet) + { + const grub_uint8_t *options = packet->dhcp_options; + unsigned int option_max = 1024 - OFFSET_OF (dhcp_options, packet); + unsigned int i; + + for (i = 0; i < option_max - sizeof (grub_net_dhcpv6_option_t); ) + { + grub_uint16_t num, len; + grub_net_dhcpv6_option_t *opt = + (grub_net_dhcpv6_option_t *)(options + i); + + num = grub_be_to_cpu16(opt->option_num); + len = grub_be_to_cpu16(opt->option_len); + + grub_dprintf ("net", "got dhcpv6 option %d len %d\n", num, len); + + if (len == 0) + break; + + if (len + i > 1024) + break; + + if (num == GRUB_NET_DHCP6_BOOTFILE_URL) + { + char *scheme, *userinfo, *host, *file; + char *tmp; + int hostlen; + int port; + int rc = extract_url_info ((const char *)opt->option_data, + (grub_size_t)len, + &scheme, &userinfo, &host, &port, + &file); + if (rc < 0) + continue; + + /* right now this only handles tftp. */ + if (grub_strcmp("tftp", scheme)) + { + grub_free (scheme); + grub_free (userinfo); + grub_free (host); + grub_free (file); + continue; + } + grub_free (userinfo); + + hostlen = grub_strlen (host); + if (hostlen > 2 && host[0] == '[' && host[hostlen-1] == ']') + { + tmp = host+1; + host[hostlen-1] = '\0'; + } + else + tmp = host; + + *device = grub_xasprintf ("%s,%s", scheme, tmp); + grub_free (scheme); + grub_free (host); + + if (file && *file) + { + tmp = grub_strrchr (file, '/'); + if (tmp) + *(tmp+1) = '\0'; + else + file[0] = '\0'; + } + else if (!file) + file = grub_strdup (""); + + if (file[0] == '/') + { + *path = grub_strdup (file+1); + grub_free (file); + } + else + *path = file; + } + else if (num == GRUB_NET_DHCP6_IA_NA) + { + const grub_net_dhcpv6_option_t *ia_na_opt; + const grub_net_dhcpv6_opt_ia_na_t *ia_na = + (const grub_net_dhcpv6_opt_ia_na_t *)opt; + unsigned int left = len - OFFSET_OF (options, ia_na); + unsigned int j; + + if ((grub_uint8_t *)ia_na + left > + (grub_uint8_t *)options + option_max) + left -= ((grub_uint8_t *)ia_na + left) + - ((grub_uint8_t *)options + option_max); + + if (len < OFFSET_OF (option_data, opt) + + sizeof (grub_net_dhcpv6_option_t)) + { + grub_dprintf ("net", + "found dhcpv6 ia_na option with no address\n"); + continue; + } + + for (j = 0; left > sizeof (grub_net_dhcpv6_option_t); ) + { + ia_na_opt = (const grub_net_dhcpv6_option_t *) + (ia_na->options + j); + grub_uint16_t ia_na_opt_num, ia_na_opt_len; + + ia_na_opt_num = grub_be_to_cpu16 (ia_na_opt->option_num); + ia_na_opt_len = grub_be_to_cpu16 (ia_na_opt->option_len); + if (ia_na_opt_len == 0) + break; + if (j + ia_na_opt_len > left) + break; + if (ia_na_opt_num == GRUB_NET_DHCP6_IA_ADDRESS) + { + const grub_net_dhcpv6_opt_ia_address_t *ia_addr; + + ia_addr = (const grub_net_dhcpv6_opt_ia_address_t *) + ia_na_opt; + addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + grub_memcpy(addr.ipv6, ia_addr->ipv6_address, + sizeof (ia_addr->ipv6_address)); + inter = grub_net_add_addr (name, card, &addr, hwaddr, 0); + } + + j += ia_na_opt_len; + left -= ia_na_opt_len; + } + } + + i += len + 4; + } + + grub_print_error (); + } + + if (is_def) + { + grub_env_set ("net_default_interface", name); + grub_env_export ("net_default_interface"); + } + + if (inter) + grub_net_add_ipv6_local (inter, mask); + return inter; +} + + void grub_bootp_init (void) { diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c index 5388f952ba..173fb63153 100644 --- a/grub-core/net/drivers/efi/efinet.c +++ b/grub-core/net/drivers/efi/efinet.c @@ -18,11 +18,14 @@ #include #include +#include #include #include #include #include #include +#include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -329,7 +332,7 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, char **path) { struct grub_net_card *card; - grub_efi_device_path_t *dp; + grub_efi_device_path_t *dp, *ldp = NULL; dp = grub_efi_get_device_path (hnd); if (! dp) @@ -340,14 +343,19 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, grub_efi_device_path_t *cdp; struct grub_efi_pxe *pxe; struct grub_efi_pxe_mode *pxe_mode; + if (card->driver != &efidriver) continue; + cdp = grub_efi_get_device_path (card->efi_handle); if (! cdp) continue; + + ldp = grub_efi_find_last_device_path (dp); + if (grub_efi_compare_device_paths (dp, cdp) != 0) { - grub_efi_device_path_t *ldp, *dup_dp, *dup_ldp; + grub_efi_device_path_t *dup_dp, *dup_ldp; int match; /* EDK2 UEFI PXE driver creates pseudo devices with type IPv4/IPv6 @@ -356,7 +364,6 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, devices. We skip them when enumerating cards, so here we need to find matching MAC device. */ - ldp = grub_efi_find_last_device_path (dp); if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)) @@ -373,16 +380,46 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, if (!match) continue; } + pxe = grub_efi_open_protocol (hnd, &pxe_io_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (! pxe) continue; + pxe_mode = pxe->mode; - grub_net_configure_by_dhcp_ack (card->name, card, 0, - (struct grub_net_bootp_packet *) - &pxe_mode->dhcp_ack, - sizeof (pxe_mode->dhcp_ack), - 1, device, path); + if (pxe_mode->using_ipv6) + { + grub_net_link_level_address_t hwaddr; + struct grub_net_network_level_interface *intf; + + grub_dprintf ("efinet", "using ipv6 and dhcpv6\n"); + grub_dprintf ("efinet", "dhcp_ack_received: %s%s\n", + pxe_mode->dhcp_ack_received ? "yes" : "no", + pxe_mode->dhcp_ack_received ? "" : " cannot continue"); + if (!pxe_mode->dhcp_ack_received) + continue; + + hwaddr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + grub_memcpy (hwaddr.mac, + card->efi_net->mode->current_address, + sizeof (hwaddr.mac)); + + intf = grub_net_configure_by_dhcpv6_ack (card->name, card, 0, &hwaddr, + (const struct grub_net_dhcpv6_packet *)&pxe_mode->dhcp_ack.dhcpv6, + 1, device, path); + if (intf && device && path) + grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path); + } + else + { + grub_dprintf ("efinet", "using ipv4 and dhcp\n"); + grub_net_configure_by_dhcp_ack (card->name, card, 0, + (struct grub_net_bootp_packet *) + &pxe_mode->dhcp_ack, + sizeof (pxe_mode->dhcp_ack), + 1, device, path); + grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path); + } return; } } diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 0ef148f4ad..22f2689aae 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -960,6 +960,78 @@ grub_net_network_level_interface_register (struct grub_net_network_level_interfa grub_net_network_level_interfaces = inter; } +int +grub_ipv6_get_masksize (grub_uint16_t be_mask[8]) +{ + grub_uint8_t *mask; + grub_uint16_t mask16[8]; + int x, y; + int ret = 128; + + grub_memcpy (mask16, be_mask, sizeof (mask16)); + for (x = 0; x < 8; x++) + mask16[x] = grub_be_to_cpu16 (mask16[x]); + + mask = (grub_uint8_t *)mask16; + + for (x = 15; x >= 0; x--) + { + grub_uint8_t octet = mask[x]; + if (!octet) + { + ret -= 8; + continue; + } + for (y = 0; y < 8; y++) + { + if (octet & (1 << y)) + break; + else + ret--; + } + break; + } + + return ret; +} + +grub_err_t +grub_net_add_ipv6_local (struct grub_net_network_level_interface *inter, + int mask) +{ + struct grub_net_route *route; + + if (inter->address.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6) + return 0; + + if (mask == -1) + mask = grub_ipv6_get_masksize ((grub_uint16_t *)inter->address.ipv6); + + if (mask == -1) + return 0; + + route = grub_zalloc (sizeof (*route)); + if (!route) + return grub_errno; + + route->name = grub_xasprintf ("%s:local", inter->name); + if (!route->name) + { + grub_free (route); + return grub_errno; + } + + route->target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + grub_memcpy (route->target.ipv6.base, inter->address.ipv6, + sizeof (inter->address.ipv6)); + route->target.ipv6.masksize = mask; + route->is_gateway = 0; + route->interface = inter; + + grub_net_route_register (route); + + return 0; +} grub_err_t grub_net_add_ipv4_local (struct grub_net_network_level_interface *inter, diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index 7f44b30f52..4ab2f5c735 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -358,18 +358,22 @@ tftp_open (struct grub_file *file, const char *filename) file->not_easily_seekable = 1; file->data = data; + grub_dprintf("tftp", "resolving address for %s\n", file->device->net->server); err = grub_net_resolve_address (file->device->net->server, &addr); if (err) { + grub_dprintf("tftp", "Address resolution failed: %d\n", err); grub_free (data); return err; } + grub_dprintf("tftp", "opening connection\n"); data->sock = grub_net_udp_open (addr, TFTP_SERVER_PORT, tftp_receive, file); if (!data->sock) { + grub_dprintf("tftp", "connection failed\n"); grub_free (data); return grub_errno; } diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index f1a52210c0..117469450d 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -592,10 +592,16 @@ typedef void *grub_efi_handle_t; typedef void *grub_efi_event_t; typedef grub_efi_uint64_t grub_efi_lba_t; typedef grub_efi_uintn_t grub_efi_tpl_t; -typedef grub_uint8_t grub_efi_mac_address_t[32]; -typedef grub_uint8_t grub_efi_ipv4_address_t[4]; -typedef grub_uint16_t grub_efi_ipv6_address_t[8]; -typedef grub_uint8_t grub_efi_ip_address_t[8] __attribute__ ((aligned(4))); +typedef grub_efi_uint8_t grub_efi_mac_address_t[32]; +typedef grub_efi_uint8_t grub_efi_ipv4_address_t[4]; +typedef grub_efi_uint8_t grub_efi_ipv6_address_t[16]; +typedef union +{ + grub_efi_uint32_t addr[4]; + grub_efi_ipv4_address_t v4; + grub_efi_ipv6_address_t v6; +} grub_efi_ip_address_t __attribute__ ((aligned(4))); + typedef grub_efi_uint64_t grub_efi_physical_address_t; typedef grub_efi_uint64_t grub_efi_virtual_address_t; @@ -1474,16 +1480,127 @@ struct grub_efi_simple_text_output_interface }; typedef struct grub_efi_simple_text_output_interface grub_efi_simple_text_output_interface_t; -typedef grub_uint8_t grub_efi_pxe_packet_t[1472]; +typedef struct grub_efi_pxe_dhcpv4_packet +{ + grub_efi_uint8_t bootp_opcode; + grub_efi_uint8_t bootp_hwtype; + grub_efi_uint8_t bootp_hwaddr_len; + grub_efi_uint8_t bootp_gate_hops; + grub_efi_uint32_t bootp_ident; + grub_efi_uint16_t bootp_seconds; + grub_efi_uint16_t bootp_flags; + grub_efi_uint8_t bootp_ci_addr[4]; + grub_efi_uint8_t bootp_yi_addr[4]; + grub_efi_uint8_t bootp_si_addr[4]; + grub_efi_uint8_t bootp_gi_addr[4]; + grub_efi_uint8_t bootp_hw_addr[16]; + grub_efi_uint8_t bootp_srv_name[64]; + grub_efi_uint8_t bootp_boot_file[128]; + grub_efi_uint32_t dhcp_magik; + grub_efi_uint8_t dhcp_options[56]; +} grub_efi_pxe_dhcpv4_packet_t; + +struct grub_efi_pxe_dhcpv6_packet +{ + grub_efi_uint32_t message_type:8; + grub_efi_uint32_t transaction_id:24; + grub_efi_uint8_t dhcp_options[1024]; +} GRUB_PACKED; +typedef struct grub_efi_pxe_dhcpv6_packet grub_efi_pxe_dhcpv6_packet_t; + +typedef union +{ + grub_efi_uint8_t raw[1472]; + grub_efi_pxe_dhcpv4_packet_t dhcpv4; + grub_efi_pxe_dhcpv6_packet_t dhcpv6; +} grub_efi_pxe_packet_t; + +#define GRUB_EFI_PXE_MAX_IPCNT 8 +#define GRUB_EFI_PXE_MAX_ARP_ENTRIES 8 +#define GRUB_EFI_PXE_MAX_ROUTE_ENTRIES 8 + +typedef struct grub_efi_pxe_ip_filter +{ + grub_efi_uint8_t filters; + grub_efi_uint8_t ip_count; + grub_efi_uint8_t reserved; + grub_efi_ip_address_t ip_list[GRUB_EFI_PXE_MAX_IPCNT]; +} grub_efi_pxe_ip_filter_t; + +typedef struct grub_efi_pxe_arp_entry +{ + grub_efi_ip_address_t ip_addr; + grub_efi_mac_address_t mac_addr; +} grub_efi_pxe_arp_entry_t; + +typedef struct grub_efi_pxe_route_entry +{ + grub_efi_ip_address_t ip_addr; + grub_efi_ip_address_t subnet_mask; + grub_efi_ip_address_t gateway_addr; +} grub_efi_pxe_route_entry_t; + +typedef struct grub_efi_pxe_icmp_error +{ + grub_efi_uint8_t type; + grub_efi_uint8_t code; + grub_efi_uint16_t checksum; + union + { + grub_efi_uint32_t reserved; + grub_efi_uint32_t mtu; + grub_efi_uint32_t pointer; + struct + { + grub_efi_uint16_t identifier; + grub_efi_uint16_t sequence; + } echo; + } u; + grub_efi_uint8_t data[494]; +} grub_efi_pxe_icmp_error_t; + +typedef struct grub_efi_pxe_tftp_error +{ + grub_efi_uint8_t error_code; + grub_efi_char8_t error_string[127]; +} grub_efi_pxe_tftp_error_t; typedef struct grub_efi_pxe_mode { - grub_uint8_t unused[52]; + grub_efi_boolean_t started; + grub_efi_boolean_t ipv6_available; + grub_efi_boolean_t ipv6_supported; + grub_efi_boolean_t using_ipv6; + grub_efi_boolean_t bis_supported; + grub_efi_boolean_t bis_detected; + grub_efi_boolean_t auto_arp; + grub_efi_boolean_t send_guid; + grub_efi_boolean_t dhcp_discover_valid; + grub_efi_boolean_t dhcp_ack_received; + grub_efi_boolean_t proxy_offer_received; + grub_efi_boolean_t pxe_discover_valid; + grub_efi_boolean_t pxe_reply_received; + grub_efi_boolean_t pxe_bis_reply_received; + grub_efi_boolean_t icmp_error_received; + grub_efi_boolean_t tftp_error_received; + grub_efi_boolean_t make_callbacks; + grub_efi_uint8_t ttl; + grub_efi_uint8_t tos; + grub_efi_ip_address_t station_ip; + grub_efi_ip_address_t subnet_mask; grub_efi_pxe_packet_t dhcp_discover; grub_efi_pxe_packet_t dhcp_ack; grub_efi_pxe_packet_t proxy_offer; grub_efi_pxe_packet_t pxe_discover; grub_efi_pxe_packet_t pxe_reply; + grub_efi_pxe_packet_t pxe_bis_reply; + grub_efi_pxe_ip_filter_t ip_filter; + grub_efi_uint32_t arp_cache_entries; + grub_efi_pxe_arp_entry_t arp_cache[GRUB_EFI_PXE_MAX_ARP_ENTRIES]; + grub_efi_uint32_t route_table_entries; + grub_efi_pxe_route_entry_t route_table[GRUB_EFI_PXE_MAX_ROUTE_ENTRIES]; + grub_efi_pxe_icmp_error_t icmp_error; + grub_efi_pxe_tftp_error_t tftp_error; } grub_efi_pxe_mode_t; typedef struct grub_efi_pxe diff --git a/include/grub/net.h b/include/grub/net.h index 7ae4b6bd80..8a05ec4fe7 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -447,6 +447,51 @@ struct grub_net_bootp_packet grub_uint8_t vendor[0]; } GRUB_PACKED; +enum + { + GRUB_NET_DHCP6_IA_NA = 3, + GRUB_NET_DHCP6_IA_ADDRESS = 5, + GRUB_NET_DHCP6_BOOTFILE_URL = 59, + }; + +struct grub_net_dhcpv6_option +{ + grub_uint16_t option_num; + grub_uint16_t option_len; + grub_uint8_t option_data[]; +} GRUB_PACKED; +typedef struct grub_net_dhcpv6_option grub_net_dhcpv6_option_t; + +struct grub_net_dhcpv6_opt_ia_na +{ + grub_uint16_t option_num; + grub_uint16_t option_len; + grub_uint32_t iaid; + grub_uint32_t t1; + grub_uint32_t t2; + grub_uint8_t options[]; +} GRUB_PACKED; +typedef struct grub_net_dhcpv6_opt_ia_na grub_net_dhcpv6_opt_ia_na_t; + +struct grub_net_dhcpv6_opt_ia_address +{ + grub_uint16_t option_num; + grub_uint16_t option_len; + grub_uint64_t ipv6_address[2]; + grub_uint32_t preferred_lifetime; + grub_uint32_t valid_lifetime; + grub_uint8_t options[]; +} GRUB_PACKED; +typedef struct grub_net_dhcpv6_opt_ia_address grub_net_dhcpv6_opt_ia_address_t; + +struct grub_net_dhcpv6_packet +{ + grub_uint32_t message_type:8; + grub_uint32_t transaction_id:24; + grub_uint8_t dhcp_options[1024]; +} GRUB_PACKED; +typedef struct grub_net_dhcpv6_packet grub_net_dhcpv6_packet_t; + #define GRUB_NET_BOOTP_RFC1048_MAGIC_0 0x63 #define GRUB_NET_BOOTP_RFC1048_MAGIC_1 0x82 #define GRUB_NET_BOOTP_RFC1048_MAGIC_2 0x53 @@ -482,6 +527,21 @@ grub_net_configure_by_dhcp_ack (const char *name, grub_size_t size, int is_def, char **device, char **path); +struct grub_net_network_level_interface * +grub_net_configure_by_dhcpv6_ack (const char *name, + struct grub_net_card *card, + grub_net_interface_flags_t flags, + const grub_net_link_level_address_t *hwaddr, + const struct grub_net_dhcpv6_packet *packet, + int is_def, char **device, char **path); + +int +grub_ipv6_get_masksize(grub_uint16_t *mask); + +grub_err_t +grub_net_add_ipv6_local (struct grub_net_network_level_interface *inf, + int mask); + grub_err_t grub_net_add_ipv4_local (struct grub_net_network_level_interface *inf, int mask); From db2707e55d0889ef5da132bebffd38ed8d48e24c Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 23 Jun 2016 11:01:39 -0400 Subject: [PATCH 036/367] Add grub-get-kernel-settings and use it in 10_linux This patch adds grub-get-kernel-settings, which reads the system kernel installation configuration from /etc/sysconfig/kernel, and outputs ${GRUB_...} variables suitable for evaluation by grub-mkconfig. Those variables are then used by 10_linux to choose whether or not to create debug stanzas. Resolves: rhbz#1226325 [rharwood: migrate man page to h2m] --- Makefile.util.def | 7 ++ configure.ac | 1 + docs/man/grub-get-kernel-settings.h2m | 2 + .../bash-completion.d/grub-completion.bash.in | 22 +++++ util/grub-get-kernel-settings.in | 88 +++++++++++++++++++ util/grub-mkconfig.in | 3 + util/grub.d/10_linux.in | 23 +++-- 7 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 docs/man/grub-get-kernel-settings.h2m create mode 100644 util/grub-get-kernel-settings.in diff --git a/Makefile.util.def b/Makefile.util.def index 4ee22c5daa..18a9242776 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -716,6 +716,13 @@ script = { installdir = sbin; }; +script = { + name = grub-get-kernel-settings; + common = util/grub-get-kernel-settings.in; + mansection = 3; + installdir = sbin; +}; + script = { name = grub-set-default; common = util/grub-set-default.in; diff --git a/configure.ac b/configure.ac index 7f59ad788f..0d0e6782a1 100644 --- a/configure.ac +++ b/configure.ac @@ -65,6 +65,7 @@ grub_TRANSFORM([grub-install]) grub_TRANSFORM([grub-mkconfig]) grub_TRANSFORM([grub-mkfont]) grub_TRANSFORM([grub-mkimage]) +grub_TRANSFORM([grub-get-kernel-settings]) grub_TRANSFORM([grub-glue-efi]) grub_TRANSFORM([grub-mklayout]) grub_TRANSFORM([grub-mkpasswd-pbkdf2]) diff --git a/docs/man/grub-get-kernel-settings.h2m b/docs/man/grub-get-kernel-settings.h2m new file mode 100644 index 0000000000..b8051f01f3 --- /dev/null +++ b/docs/man/grub-get-kernel-settings.h2m @@ -0,0 +1,2 @@ +[NAME] +grub-get-kernel-settings \- Evaluate the system's kernel installation settings for use while making a grub configuration file diff --git a/util/bash-completion.d/grub-completion.bash.in b/util/bash-completion.d/grub-completion.bash.in index 44bf135b9f..5c4acd496d 100644 --- a/util/bash-completion.d/grub-completion.bash.in +++ b/util/bash-completion.d/grub-completion.bash.in @@ -264,6 +264,28 @@ have ${__grub_sparc64_setup_program} && \ unset __grub_sparc64_setup_program +# +# grub-get-kernel-settings +# +_grub_get_kernel_settings () { + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [[ "$cur" == -* ]]; then + __grubcomp "$(__grub_get_options_from_help)" + else + # Default complete with a filename + _filedir + fi +} +__grub_get_kernel_settings_program="@grub_get_kernel_settings@" +have ${__grub_get_kernel_settings_program} && \ + complete -F _grub_get_kernel_settings -o filenames ${__grub_get_kernel_settings_program} +unset __grub_get_kernel_settings_program + + # # grub-install # diff --git a/util/grub-get-kernel-settings.in b/util/grub-get-kernel-settings.in new file mode 100644 index 0000000000..7e87dfccc0 --- /dev/null +++ b/util/grub-get-kernel-settings.in @@ -0,0 +1,88 @@ +#!/bin/sh +set -e + +# Evaluate new-kernel-pkg's configuration file. +# Copyright (C) 2016 Free Software Foundation, Inc. +# +# GRUB is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# GRUB is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GRUB. If not, see . + +PACKAGE_NAME=@PACKAGE_NAME@ +PACKAGE_VERSION=@PACKAGE_VERSION@ +datadir="@datadir@" +if [ "x$pkgdatadir" = x ]; then + pkgdatadir="${datadir}/@PACKAGE@" +fi + +self=`basename $0` + +export TEXTDOMAIN=@PACKAGE@ +export TEXTDOMAINDIR="@localedir@" + +. "${pkgdatadir}/grub-mkconfig_lib" + +# Usage: usage +# Print the usage. +usage () { + gettext_printf "Usage: %s [OPTION]\n" "$self" + gettext "Evaluate new-kernel-pkg configuration"; echo + echo + print_option_help "-h, --help" "$(gettext "print this message and exit")" + print_option_help "-v, --version" "$(gettext "print the version information and exit")" + echo +} + +# Check the arguments. +while test $# -gt 0 +do + option=$1 + shift + + case "$option" in + -h | --help) + usage + exit 0 ;; + -v | --version) + echo "$self (${PACKAGE_NAME}) ${PACKAGE_VERSION}" + exit 0 ;; + -*) + gettext_printf "Unrecognized option \`%s'\n" "$option" 1>&2 + usage + exit 1 + ;; + # Explicitly ignore non-option arguments, for compatibility. + esac +done + +if test -f /etc/sysconfig/kernel ; then + . /etc/sysconfig/kernel +fi + +if [ "$MAKEDEBUG" = "yes" ]; then + echo GRUB_LINUX_MAKE_DEBUG=true + echo export GRUB_LINUX_MAKE_DEBUG + echo GRUB_CMDLINE_LINUX_DEBUG=\"systemd.log_level=debug systemd.log_target=kmsg\" + echo export GRUB_CMDLINE_LINUX_DEBUG + echo GRUB_LINUX_DEBUG_TITLE_POSTFIX=\" with debugging\" + echo export GRUB_LINUX_DEBUG_TITLE_POSTFIX +fi +if [ "$DEFAULTDEBUG" = "yes" ]; then + echo GRUB_DEFAULT_TO_DEBUG=true +else + echo GRUB_DEFAULT_TO_DEBUG=false +fi +echo export GRUB_DEFAULT_TO_DEBUG +if [ "$UPDATEDEFAULT" = "yes" ]; then + echo GRUB_UPDATE_DEFAULT_KERNEL=true + echo export GRUB_UPDATE_DEFAULT_KERNEL +fi diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index ba14cf6261..005f093809 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -45,6 +45,7 @@ grub_probe="${sbindir}/@grub_probe@" grub_file="${bindir}/@grub_file@" grub_editenv="${bindir}/@grub_editenv@" grub_script_check="${bindir}/@grub_script_check@" +grub_get_kernel_settings="${sbindir}/@grub_get_kernel_settings@" export TEXTDOMAIN=@PACKAGE@ export TEXTDOMAINDIR="@localedir@" @@ -158,6 +159,8 @@ if test -f ${sysconfdir}/default/grub ; then . ${sysconfdir}/default/grub fi +eval "$("${grub_get_kernel_settings}")" || true + if [ "x${GRUB_DISABLE_UUID}" = "xtrue" ]; then if [ -z "${GRUB_DISABLE_LINUX_UUID}" ]; then GRUB_DISABLE_LINUX_UUID="true" diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 786dbabb4a..292e333324 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -111,7 +111,8 @@ linux_entry () os="$1" version="$2" type="$3" - args="$4" + isdebug="$4" + args="$5" if [ -z "$boot_device_id" ]; then boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")" @@ -123,6 +124,9 @@ linux_entry () quoted="$(echo "$GRUB_ACTUAL_DEFAULT" | grub_quote)" title_correction_code="${title_correction_code}if [ \"x\$default\" = '$quoted' ]; then default='$(echo "$replacement_title" | grub_quote)'; fi;" fi + if [ x$isdebug = xdebug ]; then + title="$title${GRUB_LINUX_DEBUG_TITLE_POSTFIX}" + fi echo "menuentry '$(echo "$title" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {" | sed "s/^/$submenu_indentation/" else echo "menuentry '$(echo "$os" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-simple-$boot_device_id' {" | sed "s/^/$submenu_indentation/" @@ -306,11 +310,15 @@ while [ "x$list" != "x" ] ; do fi if [ "x$is_top_level" = xtrue ] && [ "x${GRUB_DISABLE_SUBMENU}" != xtrue ]; then - linux_entry "${OS}" "${version}" simple \ + linux_entry "${OS}" "${version}" simple standard \ "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" + if [ "x$GRUB_LINUX_MAKE_DEBUG" = "xtrue" ]; then + linux_entry "${OS}" "${version}" simple debug \ + "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT} ${GRUB_CMDLINE_LINUX_DEBUG}" + fi submenu_indentation="$grub_tab" - + if [ -z "$boot_device_id" ]; then boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")" fi @@ -319,10 +327,15 @@ while [ "x$list" != "x" ] ; do is_top_level=false fi - linux_entry "${OS}" "${version}" advanced \ + linux_entry "${OS}" "${version}" advanced standard \ "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" + if [ "x$GRUB_LINUX_MAKE_DEBUG" = "xtrue" ]; then + linux_entry "${OS}" "${version}" advanced debug \ + "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT} ${GRUB_CMDLINE_LINUX_DEBUG}" + fi + if [ "x${GRUB_DISABLE_RECOVERY}" != "xtrue" ]; then - linux_entry "${OS}" "${version}" recovery \ + linux_entry "${OS}" "${version}" recovery standard \ "single ${GRUB_CMDLINE_LINUX}" fi From cf424b8bd553e102d54cfea92226c53ab3e89d2c Mon Sep 17 00:00:00 2001 From: Masahiro Matsuya Date: Sat, 29 Oct 2016 08:35:26 +0900 Subject: [PATCH 037/367] bz1374141 fix incorrect mask for ppc64 The netmask configured in firmware is not respected on ppc64 (big endian). When 255.255.252.0 is set as netmask in firmware, the following is the value of bootpath string in grub_ieee1275_parse_bootpath(). /vdevice/l-lan@30000002:speed=auto,duplex=auto,192.168.88.10,,192.168.89.113,192.168.88.1,5,5,255.255.252.0,512 The netmask in this bootpath is no problem, since it's a value specified in firmware. But, The value of 'subnet_mask.ipv4' was set with 0xfffffc00, and __builtin_ctz (~grub_le_to_cpu32 (subnet_mask.ipv4)) returned 16 (not 22). As a result, 16 was used for netmask wrongly. 1111 1111 1111 1111 1111 1100 0000 0000 # subnet_mask.ipv4 (=0xfffffc00) 0000 0000 1111 1100 1111 1111 1111 1111 # grub_le_to_cpu32 (subnet_mask.ipv4) 1111 1111 0000 0011 0000 0000 0000 0000 # ~grub_le_to_cpu32 (subnet_mask.ipv4) And, the count of zero with __builtin_ctz can be 16. This patch changes it as below. 1111 1111 1111 1111 1111 1100 0000 0000 # subnet_mask.ipv4 (=0xfffffc00) 0000 0000 1111 1100 1111 1111 1111 1111 # grub_le_to_cpu32 (subnet_mask.ipv4) 1111 1111 1111 1111 1111 1100 0000 0000 # grub_swap_bytes32(grub_le_to_cpu32 (subnet_mask.ipv4)) 0000 0000 0000 0000 0000 0011 1111 1111 # ~grub_swap_bytes32(grub_le_to_cpu32 (subnet_mask.ipv4)) The count of zero with __builtin_clz can be 22. (clz counts the number of one bits preceding the most significant zero bit) Signed-off-by: Masahiro Matsuya Signed-off-by: Robbie Harwood --- grub-core/net/drivers/ieee1275/ofnet.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/grub-core/net/drivers/ieee1275/ofnet.c b/grub-core/net/drivers/ieee1275/ofnet.c index ac4e62a95c..3860b6f78d 100644 --- a/grub-core/net/drivers/ieee1275/ofnet.c +++ b/grub-core/net/drivers/ieee1275/ofnet.c @@ -220,8 +220,7 @@ grub_ieee1275_parse_bootpath (const char *devpath, char *bootpath, flags); inter->vlantag = vlantag; grub_net_add_ipv4_local (inter, - __builtin_ctz (~grub_le_to_cpu32 (subnet_mask.ipv4))); - + __builtin_clz (~grub_swap_bytes32(grub_le_to_cpu32 (subnet_mask.ipv4)))); } if (gateway_addr.ipv4 != 0) From f01d42d7251397222918bb68cc06b7c76d901123 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 27 Jan 2016 09:22:42 -0500 Subject: [PATCH 038/367] Make grub_fatal() also backtrace. --- grub-core/Makefile.core.def | 3 ++ grub-core/kern/misc.c | 8 ++++- grub-core/lib/arm64/backtrace.c | 62 +++++++++++++++++++++++++++++++++ grub-core/lib/backtrace.c | 2 ++ grub-core/lib/i386/backtrace.c | 14 +++++++- 5 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 grub-core/lib/arm64/backtrace.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index c15e91943b..058c88ac3a 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -188,6 +188,9 @@ kernel = { softdiv = lib/division.c; + x86 = lib/i386/backtrace.c; + x86 = lib/backtrace.c; + i386 = kern/i386/dl.c; i386_xen = kern/i386/dl.c; i386_xen_pvh = kern/i386/dl.c; diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index 63b586d09c..a3e215155b 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -24,6 +24,7 @@ #include #include #include +#include union printf_arg { @@ -1199,8 +1200,13 @@ grub_printf_fmt_check (const char *fmt, const char *fmt_expected) static void __attribute__ ((noreturn)) grub_abort (void) { +#ifndef GRUB_UTIL +#if defined(__i386__) || defined(__x86_64__) + grub_backtrace(); +#endif +#endif grub_printf ("\nAborted."); - + #ifndef GRUB_UTIL if (grub_term_inputs) #endif diff --git a/grub-core/lib/arm64/backtrace.c b/grub-core/lib/arm64/backtrace.c new file mode 100644 index 0000000000..1079b5380e --- /dev/null +++ b/grub-core/lib/arm64/backtrace.c @@ -0,0 +1,62 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_STACK_FRAME 102400 + +void +grub_backtrace_pointer (int frame) +{ + while (1) + { + void *lp = __builtin_return_address (frame); + if (!lp) + break; + + lp = __builtin_extract_return_addr (lp); + + grub_printf ("%p: ", lp); + grub_backtrace_print_address (lp); + grub_printf (" ("); + for (i = 0; i < 2; i++) + grub_printf ("%p,", ((void **)ptr) [i + 2]); + grub_printf ("%p)\n", ((void **)ptr) [i + 2]); + nptr = *(void **)ptr; + if (nptr < ptr || (void **) nptr - (void **) ptr > MAX_STACK_FRAME + || nptr == ptr) + { + grub_printf ("Invalid stack frame at %p (%p)\n", ptr, nptr); + break; + } + ptr = nptr; + } +} + +void +grub_backtrace (void) +{ + grub_backtrace_pointer (1); +} + diff --git a/grub-core/lib/backtrace.c b/grub-core/lib/backtrace.c index 825a8800e2..c0ad6ab8be 100644 --- a/grub-core/lib/backtrace.c +++ b/grub-core/lib/backtrace.c @@ -29,6 +29,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); void grub_backtrace_print_address (void *addr) { +#ifndef GRUB_UTIL grub_dl_t mod; FOR_DL_MODULES (mod) @@ -44,6 +45,7 @@ grub_backtrace_print_address (void *addr) } } +#endif grub_printf ("%p", addr); } diff --git a/grub-core/lib/i386/backtrace.c b/grub-core/lib/i386/backtrace.c index c3e03c7275..c67273db3a 100644 --- a/grub-core/lib/i386/backtrace.c +++ b/grub-core/lib/i386/backtrace.c @@ -15,11 +15,23 @@ * You should have received a copy of the GNU General Public License * along with GRUB. If not, see . */ +#include +#ifdef GRUB_UTIL +#define REALLY_GRUB_UTIL GRUB_UTIL +#undef GRUB_UTIL +#endif + +#include +#include + +#ifdef REALLY_GRUB_UTIL +#define GRUB_UTIL REALLY_GRUB_UTIL +#undef REALLY_GRUB_UTIL +#endif #include #include #include -#include #include #include #include From 3e14f6367e7d689a42cdf5899bcd0bf8483a4e90 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 9 Jul 2019 12:59:58 +0200 Subject: [PATCH 039/367] Make our info pages say "grub2" where appropriate. This needs to be hooked up to --program-transform=, but I haven't had time. Signed-off-by: Peter Jones --- docs/grub-dev.texi | 4 +- docs/grub.texi | 321 ++++++++++++++++++++++++--------------------- 2 files changed, 171 insertions(+), 154 deletions(-) diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi index 6c629a23e2..19f708ee66 100644 --- a/docs/grub-dev.texi +++ b/docs/grub-dev.texi @@ -1,7 +1,7 @@ \input texinfo @c -*-texinfo-*- @c %**start of header -@setfilename grub-dev.info +@setfilename grub2-dev.info @include version-dev.texi @settitle GNU GRUB Developers Manual @value{VERSION} @c Unify all our little indices for now. @@ -32,7 +32,7 @@ Invariant Sections. @dircategory Kernel @direntry -* grub-dev: (grub-dev). The GRand Unified Bootloader Dev +* grub2-dev: (grub2-dev). The GRand Unified Bootloader Dev @end direntry @setchapternewpage odd diff --git a/docs/grub.texi b/docs/grub.texi index 69f08d289f..0615d0ed97 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -1,7 +1,7 @@ \input texinfo @c -*-texinfo-*- @c %**start of header -@setfilename grub.info +@setfilename grub2.info @include version.texi @settitle GNU GRUB Manual @value{VERSION} @c Unify all our little indices for now. @@ -32,15 +32,15 @@ Invariant Sections. @dircategory Kernel @direntry -* GRUB: (grub). The GRand Unified Bootloader -* grub-install: (grub)Invoking grub-install. Install GRUB on your drive -* grub-mkconfig: (grub)Invoking grub-mkconfig. Generate GRUB configuration -* grub-mkpasswd-pbkdf2: (grub)Invoking grub-mkpasswd-pbkdf2. -* grub-mkrelpath: (grub)Invoking grub-mkrelpath. -* grub-mkrescue: (grub)Invoking grub-mkrescue. Make a GRUB rescue image -* grub-mount: (grub)Invoking grub-mount. Mount a file system using GRUB -* grub-probe: (grub)Invoking grub-probe. Probe device information -* grub-script-check: (grub)Invoking grub-script-check. +* GRUB2: (grub2). The GRand Unified Bootloader +* grub2-install: (grub2)Invoking grub2-install. Install GRUB on your drive +* grub2-mkconfig: (grub2)Invoking grub2-mkconfig. Generate GRUB configuration +* grub2-mkpasswd-pbkdf2: (grub2)Invoking grub2-mkpasswd-pbkdf2. +* grub2-mkrelpath: (grub2)Invoking grub2-mkrelpath. +* grub2-mkrescue: (grub2)Invoking grub2-mkrescue. Make a GRUB rescue image +* grub2-mount: (grub2)Invoking grub2-mount. Mount a file system using GRUB +* grub2-probe: (grub2)Invoking grub2-probe. Probe device information +* grub2-script-check: (grub2)Invoking grub2-script-check. @end direntry @setchapternewpage odd @@ -103,15 +103,15 @@ This edition documents version @value{VERSION}. * Platform-specific operations:: Platform-specific operations * Supported kernels:: The list of supported kernels * Troubleshooting:: Error messages produced by GRUB -* Invoking grub-install:: How to use the GRUB installer -* Invoking grub-mkconfig:: Generate a GRUB configuration file -* Invoking grub-mkpasswd-pbkdf2:: +* Invoking grub2-install:: How to use the GRUB installer +* Invoking grub2-mkconfig:: Generate a GRUB configuration file +* Invoking grub2-mkpasswd-pbkdf2:: Generate GRUB password hashes -* Invoking grub-mkrelpath:: Make system path relative to its root -* Invoking grub-mkrescue:: Make a GRUB rescue image -* Invoking grub-mount:: Mount a file system using GRUB -* Invoking grub-probe:: Probe device information for GRUB -* Invoking grub-script-check:: Check GRUB script file for syntax errors +* Invoking grub2-mkrelpath:: Make system path relative to its root +* Invoking grub2-mkrescue:: Make a GRUB rescue image +* Invoking grub2-mount:: Mount a file system using GRUB +* Invoking grub2-probe:: Probe device information for GRUB +* Invoking grub2-script-check:: Check GRUB script file for syntax errors * Obtaining and Building GRUB:: How to obtain and build GRUB * Reporting bugs:: Where you should send a bug report * Future:: Some future plans on GRUB @@ -230,7 +230,7 @@ surprising. @item @file{grub.cfg} is typically automatically generated by -@command{grub-mkconfig} (@pxref{Simple configuration}). This makes it +@command{grub2-mkconfig} (@pxref{Simple configuration}). This makes it easier to handle versioned kernel upgrades. @item @@ -244,7 +244,7 @@ scripting language: variables, conditionals, and loops are available. @item A small amount of persistent storage is available across reboots, using the @command{save_env} and @command{load_env} commands in GRUB and the -@command{grub-editenv} utility. This is not available in all configurations +@command{grub2-editenv} utility. This is not available in all configurations (@pxref{Environment block}). @item @@ -549,7 +549,7 @@ On OS which have device nodes similar to Unix-like OS GRUB tools use the OS name. E.g. for GNU/Linux: @example -# @kbd{grub-install /dev/sda} +# @kbd{grub2-install /dev/sda} @end example On AROS we use another syntax. For volumes: @@ -572,7 +572,7 @@ For disks we use syntax: E.g. @example -# @kbd{grub-install //:ata.device/0/0} +# @kbd{grub2-install //:ata.device/0/0} @end example On Windows we use UNC path. For volumes it's typically @@ -599,7 +599,7 @@ For disks it's E.g. @example -# @kbd{grub-install \\?\PhysicalDrive0} +# @kbd{grub2-install \\?\PhysicalDrive0} @end example Beware that you may need to further escape the backslashes depending on your @@ -609,7 +609,7 @@ When compiled with cygwin support then cygwin drive names are automatically when needed. E.g. @example -# @kbd{grub-install /dev/sda} +# @kbd{grub2-install /dev/sda} @end example @node Installation @@ -622,7 +622,7 @@ from the source tarball, or as a package for your OS. After you have done that, you need to install the boot loader on a drive (floppy or hard disk) by using the utility -@command{grub-install} (@pxref{Invoking grub-install}) on a UNIX-like OS. +@command{grub2-install} (@pxref{Invoking grub2-install}) on a UNIX-like OS. GRUB comes with boot images, which are normally put in the directory @file{/usr/lib/grub/-} (for BIOS-based machines @@ -633,22 +633,22 @@ loader needs to find them (usually @file{/boot}) will be called the @dfn{boot directory}. @menu -* Installing GRUB using grub-install:: +* Installing GRUB using grub2-install:: * Making a GRUB bootable CD-ROM:: * Device map:: * BIOS installation:: @end menu -@node Installing GRUB using grub-install -@section Installing GRUB using grub-install +@node Installing GRUB using grub2-install +@section Installing GRUB using grub2-install For information on where GRUB should be installed on PC BIOS platforms, @pxref{BIOS installation}. In order to install GRUB under a UNIX-like OS (such -as @sc{gnu}), invoke the program @command{grub-install} (@pxref{Invoking -grub-install}) as the superuser (@dfn{root}). +as @sc{gnu}), invoke the program @command{grub2-install} (@pxref{Invoking +grub2-install}) as the superuser (@dfn{root}). The usage is basically very simple. You only need to specify one argument to the program, namely, where to install the boot loader. The @@ -657,13 +657,13 @@ For example, under Linux the following will install GRUB into the MBR of the first IDE disk: @example -# @kbd{grub-install /dev/sda} +# @kbd{grub2-install /dev/sda} @end example Likewise, under GNU/Hurd, this has the same effect: @example -# @kbd{grub-install /dev/hd0} +# @kbd{grub2-install /dev/hd0} @end example But all the above examples assume that GRUB should put images under @@ -677,7 +677,7 @@ boot floppy with a filesystem. Here is an example: # @kbd{mke2fs /dev/fd0} # @kbd{mount -t ext2 /dev/fd0 /mnt} # @kbd{mkdir /mnt/boot} -# @kbd{grub-install --boot-directory=/mnt/boot /dev/fd0} +# @kbd{grub2-install --boot-directory=/mnt/boot /dev/fd0} # @kbd{umount /mnt} @end group @end example @@ -689,30 +689,37 @@ floppy instead of exposing the USB drive as a hard disk (they call it @example # @kbd{losetup /dev/loop0 /dev/sdb1} # @kbd{mount /dev/loop0 /mnt/usb} -# @kbd{grub-install --boot-directory=/mnt/usb/bugbios --force --allow-floppy /dev/loop0} +# @kbd{grub2-install --boot-directory=/mnt/usb/bugbios --force --allow-floppy /dev/loop0} @end example This install doesn't conflict with standard install as long as they are in separate directories. +Note that @command{grub2-install} is actually just a shell script and the +real task is done by other tools such as @command{grub2-mkimage}. Therefore, +you may run those commands directly to install GRUB, without using +@command{grub2-install}. Don't do that, however, unless you are very familiar +with the internals of GRUB. Installing a boot loader on a running OS may be +extremely dangerous. + On EFI systems for fixed disk install you have to mount EFI System Partition. If you mount it at @file{/boot/efi} then you don't need any special arguments: @example -# @kbd{grub-install} +# @kbd{grub2-install} @end example Otherwise you need to specify where your EFI System partition is mounted: @example -# @kbd{grub-install --efi-directory=/mnt/efi} +# @kbd{grub2-install --efi-directory=/mnt/efi} @end example For removable installs you have to use @option{--removable} and specify both @option{--boot-directory} and @option{--efi-directory}: @example -# @kbd{grub-install --efi-directory=/mnt/usb --boot-directory=/mnt/usb/boot --removable} +# @kbd{grub2-install --efi-directory=/mnt/usb --boot-directory=/mnt/usb/boot --removable} @end example @node Making a GRUB bootable CD-ROM @@ -732,10 +739,10 @@ usually also need to include a configuration file @file{grub.cfg} and some other GRUB modules. To make a simple generic GRUB rescue CD, you can use the -@command{grub-mkrescue} program (@pxref{Invoking grub-mkrescue}): +@command{grub2-mkrescue} program (@pxref{Invoking grub2-mkrescue}): @example -$ @kbd{grub-mkrescue -o grub.iso} +$ @kbd{grub2-mkrescue -o grub.iso} @end example You will often need to include other files in your image. To do this, first @@ -758,7 +765,7 @@ directory @file{iso/}. Finally, make the image: @example -$ @kbd{grub-mkrescue -o grub.iso iso} +$ @kbd{grub2-mkrescue -o grub.iso iso} @end example This produces a file named @file{grub.iso}, which then can be burned @@ -774,7 +781,7 @@ storage devices. @node Device map @section The map between BIOS drives and OS devices -If the device map file exists, the GRUB utilities (@command{grub-probe}, +If the device map file exists, the GRUB utilities (@command{grub2-probe}, etc.) read it to map BIOS drives to OS devices. This file consists of lines like this: @@ -1254,23 +1261,23 @@ need to write the whole thing by hand. @node Simple configuration @section Simple configuration handling -The program @command{grub-mkconfig} (@pxref{Invoking grub-mkconfig}) +The program @command{grub2-mkconfig} (@pxref{Invoking grub2-mkconfig}) generates @file{grub.cfg} files suitable for most cases. It is suitable for use when upgrading a distribution, and will discover available kernels and attempt to generate menu entries for them. -@command{grub-mkconfig} does have some limitations. While adding extra +@command{grub2-mkconfig} does have some limitations. While adding extra custom menu entries to the end of the list can be done by editing -@file{/etc/grub.d/40_custom} or creating @file{/boot/grub/custom.cfg}, +@file{/etc/grub.d/40_custom} or creating @file{/boot/grub2/custom.cfg}, changing the order of menu entries or changing their titles may require making complex changes to shell scripts stored in @file{/etc/grub.d/}. This may be improved in the future. In the meantime, those who feel that it would be easier to write @file{grub.cfg} directly are encouraged to do so (@pxref{Booting}, and @ref{Shell-like scripting}), and to disable any system -provided by their distribution to automatically run @command{grub-mkconfig}. +provided by their distribution to automatically run @command{grub2-mkconfig}. The file @file{/etc/default/grub} controls the operation of -@command{grub-mkconfig}. It is sourced by a shell script, and so must be +@command{grub2-mkconfig}. It is sourced by a shell script, and so must be valid POSIX shell input; normally, it will just be a sequence of @samp{KEY=value} lines, but if the value contains spaces or other special characters then it must be quoted. For example: @@ -1308,7 +1315,7 @@ works it's not recommended since titles often contain unstable device names and may be translated If you set this to @samp{saved}, then the default menu entry will be that -saved by @samp{GRUB_SAVEDEFAULT} or @command{grub-set-default}. This relies on +saved by @samp{GRUB_SAVEDEFAULT} or @command{grub2-set-default}. This relies on the environment block, which may not be available in all situations (@pxref{Environment block}). @@ -1319,7 +1326,7 @@ If this option is set to @samp{true}, then, when an entry is selected, save it as a new default entry for use by future runs of GRUB. This is only useful if @samp{GRUB_DEFAULT=saved}; it is a separate option because @samp{GRUB_DEFAULT=saved} is useful without this option, in conjunction with -@command{grub-set-default}. Unset by default. +@command{grub2-set-default}. Unset by default. This option relies on the environment block, which may not be available in all situations (@pxref{Environment block}). @@ -1449,7 +1456,7 @@ intel-uc.img intel-ucode.img amd-uc.img amd-ucode.img early_ucode.cpio microcode @end example @item GRUB_DISABLE_LINUX_UUID -Normally, @command{grub-mkconfig} will generate menu entries that use +Normally, @command{grub2-mkconfig} will generate menu entries that use universally-unique identifiers (UUIDs) to identify the root filesystem to the Linux kernel, using a @samp{root=UUID=...} kernel parameter. This is usually more reliable, but in some cases it may not be appropriate. To @@ -1471,7 +1478,7 @@ If this option is set to @samp{true}, disable the generation of recovery mode menu entries. @item GRUB_DISABLE_UUID -Normally, @command{grub-mkconfig} will generate menu entries that use +Normally, @command{grub2-mkconfig} will generate menu entries that use universally-unique identifiers (UUIDs) to identify various filesystems to search for files. This is usually more reliable, but in some cases it may not be appropriate. To disable this use of UUIDs, set this option to @@ -1482,12 +1489,12 @@ not be appropriate. To disable this use of UUIDs, set this option to @item GRUB_VIDEO_BACKEND If graphical video support is required, either because the @samp{gfxterm} graphical terminal is in use or because @samp{GRUB_GFXPAYLOAD_LINUX} is set, -then @command{grub-mkconfig} will normally load all available GRUB video +then @command{grub2-mkconfig} will normally load all available GRUB video drivers and use the one most appropriate for your hardware. If you need to override this for some reason, then you can set this option. -After @command{grub-install} has been run, the available video drivers are -listed in @file{/boot/grub/video.lst}. +After @command{grub2-install} has been run, the available video drivers are +listed in @file{/boot/grub2/video.lst}. @item GRUB_GFXMODE Set the resolution used on the @samp{gfxterm} graphical terminal. Note that @@ -1519,7 +1526,7 @@ boot sequence. If you have problems, set this option to @samp{text} and GRUB will tell Linux to boot in normal text mode. @item GRUB_DISABLE_OS_PROBER -Normally, @command{grub-mkconfig} will try to use the external +Normally, @command{grub2-mkconfig} will try to use the external @command{os-prober} program, if installed, to discover other operating systems installed on the same system and generate appropriate menu entries for them. Set this option to @samp{true} to disable this. @@ -1529,7 +1536,7 @@ List of space-separated FS UUIDs of filesystems to be ignored from os-prober output. For efi chainloaders it's @@ @item GRUB_DISABLE_SUBMENU -Normally, @command{grub-mkconfig} will generate top level menu entry for +Normally, @command{grub2-mkconfig} will generate top level menu entry for the kernel with highest version number and put all other found kernels or alternative menu entries for recovery mode in submenu. For entries returned by @command{os-prober} first entry will be put on top level and all others @@ -1537,11 +1544,11 @@ in submenu. If this option is set to @samp{true}, flat menu with all entries on top level will be generated instead. Changing this option will require changing existing values of @samp{GRUB_DEFAULT}, @samp{fallback} (@pxref{fallback}) and @samp{default} (@pxref{default}) environment variables as well as saved -default entry using @command{grub-set-default} and value used with -@command{grub-reboot}. +default entry using @command{grub2-set-default} and value used with +@command{grub2-reboot}. @item GRUB_ENABLE_CRYPTODISK -If set to @samp{y}, @command{grub-mkconfig} and @command{grub-install} will +If set to @samp{y}, @command{grub2-mkconfig} and @command{grub2-install} will check for encrypted disks and generate additional commands needed to access them during boot. Note that in this case unattended boot is not possible because GRUB will wait for passphrase to unlock encrypted container. @@ -1600,7 +1607,7 @@ confusing @samp{GRUB_TIMEOUT_STYLE=countdown} or @end table -For more detailed customisation of @command{grub-mkconfig}'s output, you may +For more detailed customisation of @command{grub2-mkconfig}'s output, you may edit the scripts in @file{/etc/grub.d} directly. @file{/etc/grub.d/40_custom} is particularly useful for adding entire custom menu entries; simply type the menu entries you want to add at the end of @@ -1862,7 +1869,7 @@ images as well. Mount this partition on/mnt/boot and disable GRUB in all OSes and manually install self-compiled latest GRUB with: -@code{grub-install --boot-directory=/mnt/boot /dev/sda} +@code{grub2-install --boot-directory=/mnt/boot /dev/sda} In all the OSes install GRUB tools but disable installing GRUB in bootsector, so you'll have menu.lst and grub.cfg available for use. Also disable os-prober @@ -1872,20 +1879,20 @@ use by setting: in /etc/default/grub -Then write a grub.cfg (/mnt/boot/grub/grub.cfg): +Then write a grub.cfg (/mnt/boot/grub2/grub.cfg): @example menuentry "OS using grub2" @{ insmod xfs search --set=root --label OS1 --hint hd0,msdos8 - configfile /boot/grub/grub.cfg + configfile /boot/grub2/grub.cfg @} menuentry "OS using grub2-legacy" @{ insmod ext2 search --set=root --label OS2 --hint hd0,msdos6 - legacy_configfile /boot/grub/menu.lst + legacy_configfile /boot/grub2/menu.lst @} menuentry "Windows XP" @{ @@ -1948,15 +1955,15 @@ GRUB supports embedding a configuration file directly into the core image, so that it is loaded before entering normal mode. This is useful, for example, when it is not straightforward to find the real configuration file, or when you need to debug problems with loading that file. -@command{grub-install} uses this feature when it is not using BIOS disk +@command{grub2-install} uses this feature when it is not using BIOS disk functions or when installing to a different disk from the one containing @file{/boot/grub}, in which case it needs to use the @command{search} command (@pxref{search}) to find @file{/boot/grub}. To embed a configuration file, use the @option{-c} option to -@command{grub-mkimage}. The file is copied into the core image, so it may +@command{grub2-mkimage}. The file is copied into the core image, so it may reside anywhere on the file system, and may be removed after running -@command{grub-mkimage}. +@command{grub2-mkimage}. After the embedded configuration file (if any) is executed, GRUB will load the @samp{normal} module (@pxref{normal}), which will then read the real @@ -1991,13 +1998,13 @@ included in the core image: @example @group search.fs_label grub root -if [ -e /boot/grub/example/test1.cfg ]; then +if [ -e /boot/grub2/example/test1.cfg ]; then set prefix=($root)/boot/grub - configfile /boot/grub/example/test1.cfg + configfile /boot/grub2/example/test1.cfg else - if [ -e /boot/grub/example/test2.cfg ]; then + if [ -e /boot/grub2/example/test2.cfg ]; then set prefix=($root)/boot/grub - configfile /boot/grub/example/test2.cfg + configfile /boot/grub2/example/test2.cfg else echo "Could not find an example configuration file!" fi @@ -2521,7 +2528,7 @@ grub-mknetdir --net-directory=/srv/tftp --subdir=/boot/grub -d /usr/lib/grub/i38 @end group @end example -Then follow instructions printed out by grub-mknetdir on configuring your DHCP +Then follow instructions printed out by grub2-mknetdir on configuring your DHCP server. The grub.cfg file is placed in the same directory as the path output by @@ -2715,7 +2722,7 @@ team are: @end table To take full advantage of this function, install GRUB into the MBR -(@pxref{Installing GRUB using grub-install}). +(@pxref{Installing GRUB using grub2-install}). If you have a laptop which has a similar feature and not in the above list could you figure your address and contribute? @@ -2776,7 +2783,7 @@ bytes. The sole function of @file{boot.img} is to read the first sector of the core image from a local disk and jump to it. Because of the size restriction, @file{boot.img} cannot understand any file system structure, so -@command{grub-install} hardcodes the location of the first sector of the +@command{grub2-install} hardcodes the location of the first sector of the core image into @file{boot.img} when installing GRUB. @item diskboot.img @@ -2806,7 +2813,7 @@ images. @item core.img This is the core image of GRUB. It is built dynamically from the kernel -image and an arbitrary list of modules by the @command{grub-mkimage} +image and an arbitrary list of modules by the @command{grub2-mkimage} program. Usually, it contains enough modules to access @file{/boot/grub}, and loads everything else (including menu handling, the ability to load target operating systems, and so on) from the file system at run-time. The @@ -2858,7 +2865,7 @@ GRUB 2 has no single Stage 2 image. Instead, it loads modules from In GRUB 2, images for booting from CD-ROM drives are now constructed using @file{cdboot.img} and @file{core.img}, making sure that the core image contains the @samp{iso9660} module. It is usually best to use the -@command{grub-mkrescue} program for this. +@command{grub2-mkrescue} program for this. @item nbgrub There is as yet no equivalent for @file{nbgrub} in GRUB 2; it was used by @@ -3014,8 +3021,8 @@ There are two ways to specify files, by @dfn{absolute file name} and by An absolute file name resembles a Unix absolute file name, using @samp{/} for the directory separator (not @samp{\} as in DOS). One -example is @samp{(hd0,1)/boot/grub/grub.cfg}. This means the file -@file{/boot/grub/grub.cfg} in the first partition of the first hard +example is @samp{(hd0,1)/boot/grub2/grub.cfg}. This means the file +@file{/boot/grub2/grub.cfg} in the first partition of the first hard disk. If you omit the device name in an absolute file name, GRUB uses GRUB's @dfn{root device} implicitly. So if you set the root device to, say, @samp{(hd1,1)} by the command @samp{set root=(hd1,1)} (@pxref{set}), @@ -3023,8 +3030,8 @@ then @code{/boot/kernel} is the same as @code{(hd1,1)/boot/kernel}. On ZFS filesystem the first path component must be @var{volume}@samp{@@}[@var{snapshot}]. -So @samp{/rootvol@@snap-129/boot/grub/grub.cfg} refers to file -@samp{/boot/grub/grub.cfg} in snapshot of volume @samp{rootvol} with name +So @samp{/rootvol@@snap-129/boot/grub2/grub.cfg} refers to file +@samp{/boot/grub2/grub.cfg} in snapshot of volume @samp{rootvol} with name @samp{snap-129}. Trailing @samp{@@} after volume name is mandatory even if snapshot name is omitted. @@ -3427,7 +3434,7 @@ The more recent release of Minix would then be identified as @samp{other>minix>minix-3.4.0}. This variable is often set by @samp{GRUB_DEFAULT} (@pxref{Simple -configuration}), @command{grub-set-default}, or @command{grub-reboot}. +configuration}), @command{grub2-set-default}, or @command{grub2-reboot}. @node fallback @@ -3517,7 +3524,7 @@ If this variable is set, it names the language code that the example, French would be named as @samp{fr}, and Simplified Chinese as @samp{zh_CN}. -@command{grub-mkconfig} (@pxref{Simple configuration}) will try to set a +@command{grub2-mkconfig} (@pxref{Simple configuration}) will try to set a reasonable default for this variable based on the system locale. @@ -3525,10 +3532,10 @@ reasonable default for this variable based on the system locale. @subsection locale_dir If this variable is set, it names the directory where translation files may -be found (@pxref{gettext}), usually @file{/boot/grub/locale}. Otherwise, +be found (@pxref{gettext}), usually @file{/boot/grub2/locale}. Otherwise, internationalization is disabled. -@command{grub-mkconfig} (@pxref{Simple configuration}) will set a reasonable +@command{grub2-mkconfig} (@pxref{Simple configuration}) will set a reasonable default for this variable if internationalization is needed and any translation files are available. @@ -3646,7 +3653,7 @@ input. The default is not to pause output. The location of the @samp{/boot/grub} directory as an absolute file name (@pxref{File name syntax}). This is normally set by GRUB at startup based -on information provided by @command{grub-install}. GRUB modules are +on information provided by @command{grub2-install}. GRUB modules are dynamically loaded from this directory, so it must be set correctly in order for many parts of GRUB to work. @@ -3737,17 +3744,17 @@ GRUB provides an ``environment block'' which can be used to save a small amount of state. The environment block is a preallocated 1024-byte file, which normally lives -in @file{/boot/grub/grubenv} (although you should not assume this). At boot +in @file{/boot/grub2/grubenv} (although you should not assume this). At boot time, the @command{load_env} command (@pxref{load_env}) loads environment variables from it, and the @command{save_env} (@pxref{save_env}) command saves environment variables to it. From a running system, the -@command{grub-editenv} utility can be used to edit the environment block. +@command{grub2-editenv} utility can be used to edit the environment block. For safety reasons, this storage is only available when installed on a plain disk (no LVM or RAID), using a non-checksumming filesystem (no ZFS), and using BIOS or EFI functions (no ATA, USB or IEEE1275). -@command{grub-mkconfig} uses this facility to implement +@command{grub2-mkconfig} uses this facility to implement @samp{GRUB_SAVEDEFAULT} (@pxref{Simple configuration}). @@ -4476,7 +4483,7 @@ Translate @var{string} into the current language. The current language code is stored in the @samp{lang} variable in GRUB's environment (@pxref{lang}). Translation files in MO format are read from -@samp{locale_dir} (@pxref{locale_dir}), usually @file{/boot/grub/locale}. +@samp{locale_dir} (@pxref{locale_dir}), usually @file{/boot/grub2/locale}. @end deffn @@ -4871,7 +4878,7 @@ Define a user named @var{user} with password @var{clear-password}. @deffn Command password_pbkdf2 user hashed-password Define a user named @var{user} with password hash @var{hashed-password}. -Use @command{grub-mkpasswd-pbkdf2} (@pxref{Invoking grub-mkpasswd-pbkdf2}) +Use @command{grub2-mkpasswd-pbkdf2} (@pxref{Invoking grub2-mkpasswd-pbkdf2}) to generate password hashes. @xref{Security}. @end deffn @@ -5814,8 +5821,8 @@ The @samp{password} (@pxref{password}) and @samp{password_pbkdf2} which has an associated password. @samp{password} sets the password in plain text, requiring @file{grub.cfg} to be secure; @samp{password_pbkdf2} sets the password hashed using the Password-Based Key Derivation Function -(RFC 2898), requiring the use of @command{grub-mkpasswd-pbkdf2} -(@pxref{Invoking grub-mkpasswd-pbkdf2}) to generate password hashes. +(RFC 2898), requiring the use of @command{grub2-mkpasswd-pbkdf2} +(@pxref{Invoking grub2-mkpasswd-pbkdf2}) to generate password hashes. In order to enable authentication support, the @samp{superusers} environment variable must be set to a list of usernames, separated by any of spaces, @@ -5860,7 +5867,7 @@ menuentry "May be run by user1 or a superuser" --users user1 @{ @end group @end example -The @command{grub-mkconfig} program does not yet have built-in support for +The @command{grub2-mkconfig} program does not yet have built-in support for generating configuration files with authentication. You can use @file{/etc/grub.d/40_custom} to add simple superuser authentication, by adding @kbd{set superusers=} and @kbd{password} or @kbd{password_pbkdf2} @@ -5887,7 +5894,17 @@ may halt or otherwise impact the boot process. An initial trusted public key can be embedded within the GRUB @file{core.img} using the @code{--pubkey} option to @command{grub-install} -(@pxref{Invoking grub-install}). +(@pxref{Invoking grub2-install}). + +@comment Unfortunately --pubkey is not yet supported by grub2-install, +@comment but we should not bring up internal detail grub2-mkimage here +@comment in the user guide (as opposed to developer's manual). + +@comment An initial trusted public key can be embedded within the GRUB +@comment @file{core.img} using the @code{--pubkey} option to +@comment @command{grub2-mkimage} (@pxref{Invoking grub2-install}). Presently it +@comment is necessary to write a custom wrapper around @command{grub2-mkimage} +@comment using the @code{--grub-mkimage} flag to @command{grub2-install}. GRUB uses GPG-style detached signatures (meaning that a file @file{foo.sig} will be produced when file @file{foo} is signed), and @@ -5907,8 +5924,8 @@ gpg --detach-sign /path/to/file For successful validation of all of GRUB's subcomponents and the loaded OS kernel, they must all be signed. One way to accomplish this is the following (after having already produced the desired -@file{grub.cfg} file, e.g., by running @command{grub-mkconfig} -(@pxref{Invoking grub-mkconfig}): +@file{grub.cfg} file, e.g., by running @command{grub2-mkconfig} +(@pxref{Invoking grub2-mkconfig}): @example @group @@ -5930,7 +5947,7 @@ See also: @ref{check_signatures}, @ref{verify_detached}, @ref{trust}, Note that internally signature enforcement is controlled by setting the environment variable @code{check_signatures} equal to @code{enforce}. Passing one or more @code{--pubkey} options to -@command{grub-mkimage} implicitly defines @code{check_signatures} +@command{grub2-mkimage} implicitly defines @code{check_signatures} equal to @code{enforce} in @file{core.img} prior to processing any configuration files. @@ -6388,10 +6405,10 @@ Required files are: GRUB's normal start-up procedure involves setting the @samp{prefix} environment variable to a value set in the core image by -@command{grub-install}, setting the @samp{root} variable to match, loading +@command{grub2-install}, setting the @samp{root} variable to match, loading the @samp{normal} module from the prefix, and running the @samp{normal} command (@pxref{normal}). This command is responsible for reading -@file{/boot/grub/grub.cfg}, running the menu, and doing all the useful +@file{/boot/grub2/grub.cfg}, running the menu, and doing all the useful things GRUB is supposed to do. If, instead, you only get a rescue shell, this usually means that GRUB @@ -6417,8 +6434,8 @@ normal However, any problem that leaves you in the rescue shell probably means that GRUB was not correctly installed. It may be more useful to try to reinstall -it properly using @kbd{grub-install @var{device}} (@pxref{Invoking -grub-install}). When doing this, there are a few things to remember: +it properly using @kbd{grub2-install @var{device}} (@pxref{Invoking +grub2-install}). When doing this, there are a few things to remember: @itemize @bullet{} @item @@ -6430,7 +6447,7 @@ is usually better to use UUIDs or file system labels and avoid depending on drive ordering entirely. @item -At least on BIOS systems, if you tell @command{grub-install} to install GRUB +At least on BIOS systems, if you tell @command{grub2-install} to install GRUB to a partition but GRUB has already been installed in the master boot record, then the GRUB installation in the partition will be ignored. @@ -6461,21 +6478,21 @@ entry which claims partition start at block 0. This change will not hamper bootability on other machines. -@node Invoking grub-install -@chapter Invoking grub-install +@node Invoking grub2-install +@chapter Invoking grub2-install -The program @command{grub-install} generates a GRUB core image using -@command{grub-mkimage} and installs it on your system. You must specify the +The program @command{grub2-install} generates a GRUB core image using +@command{grub2-mkimage} and installs it on your system. You must specify the device name on which you want to install GRUB, like this: @example -grub-install @var{install_device} +grub2-install @var{install_device} @end example The device name @var{install_device} is an OS device name or a GRUB device name. -@command{grub-install} accepts the following options: +@command{grub2-install} accepts the following options: @table @option @item --help @@ -6491,13 +6508,13 @@ separate partition or a removable disk. If this option is not specified then it defaults to @file{/boot}, so @example -@kbd{grub-install /dev/sda} +@kbd{grub2-install /dev/sda} @end example is equivalent to @example -@kbd{grub-install --boot-directory=/boot/ /dev/sda} +@kbd{grub2-install --boot-directory=/boot/ /dev/sda} @end example Here is an example in which you have a separate @dfn{boot} partition which is @@ -6505,16 +6522,16 @@ mounted on @file{/mnt/boot}: @example -@kbd{grub-install --boot-directory=/mnt/boot /dev/sdb} +@kbd{grub2-install --boot-directory=/mnt/boot /dev/sdb} @end example @item --recheck -Recheck the device map, even if @file{/boot/grub/device.map} already +Recheck the device map, even if @file{/boot/grub2/device.map} already exists. You should use this option whenever you add/remove a disk into/from your computer. @item --no-rs-codes -By default on x86 BIOS systems, @command{grub-install} will use some +By default on x86 BIOS systems, @command{grub2-install} will use some extra space in the bootloader embedding area for Reed-Solomon error-correcting codes. This enables GRUB to still boot successfully if some blocks are corrupted. The exact amount of protection offered @@ -6527,17 +6544,17 @@ installation}) where GRUB does not reside in any unpartitioned space outside of the MBR. Disable the Reed-Solomon codes with this option. @end table -@node Invoking grub-mkconfig -@chapter Invoking grub-mkconfig +@node Invoking grub2-mkconfig +@chapter Invoking grub2-mkconfig -The program @command{grub-mkconfig} generates a configuration file for GRUB +The program @command{grub2-mkconfig} generates a configuration file for GRUB (@pxref{Simple configuration}). @example -grub-mkconfig -o /boot/grub/grub.cfg +grub-mkconfig -o /boot/grub2/grub.cfg @end example -@command{grub-mkconfig} accepts the following options: +@command{grub2-mkconfig} accepts the following options: @table @option @item --help @@ -6553,17 +6570,17 @@ it to standard output. @end table -@node Invoking grub-mkpasswd-pbkdf2 -@chapter Invoking grub-mkpasswd-pbkdf2 +@node Invoking grub2-mkpasswd-pbkdf2 +@chapter Invoking grub2-mkpasswd-pbkdf2 -The program @command{grub-mkpasswd-pbkdf2} generates password hashes for +The program @command{grub2-mkpasswd-pbkdf2} generates password hashes for GRUB (@pxref{Security}). @example grub-mkpasswd-pbkdf2 @end example -@command{grub-mkpasswd-pbkdf2} accepts the following options: +@command{grub2-mkpasswd-pbkdf2} accepts the following options: @table @option @item -c @var{number} @@ -6581,23 +6598,23 @@ Length of the salt. Defaults to 64. @end table -@node Invoking grub-mkrelpath -@chapter Invoking grub-mkrelpath +@node Invoking grub2-mkrelpath +@chapter Invoking grub2-mkrelpath -The program @command{grub-mkrelpath} makes a file system path relative to +The program @command{grub2-mkrelpath} makes a file system path relative to the root of its containing file system. For instance, if @file{/usr} is a mount point, then: @example -$ @kbd{grub-mkrelpath /usr/share/grub/unicode.pf2} +$ @kbd{grub2-mkrelpath /usr/share/grub/unicode.pf2} @samp{/share/grub/unicode.pf2} @end example This is mainly used internally by other GRUB utilities such as -@command{grub-mkconfig} (@pxref{Invoking grub-mkconfig}), but may +@command{grub2-mkconfig} (@pxref{Invoking grub2-mkconfig}), but may occasionally also be useful for debugging. -@command{grub-mkrelpath} accepts the following options: +@command{grub2-mkrelpath} accepts the following options: @table @option @item --help @@ -6608,17 +6625,17 @@ Print the version number of GRUB and exit. @end table -@node Invoking grub-mkrescue -@chapter Invoking grub-mkrescue +@node Invoking grub2-mkrescue +@chapter Invoking grub2-mkrescue -The program @command{grub-mkrescue} generates a bootable GRUB rescue image +The program @command{grub2-mkrescue} generates a bootable GRUB rescue image (@pxref{Making a GRUB bootable CD-ROM}). @example grub-mkrescue -o grub.iso @end example -All arguments not explicitly listed as @command{grub-mkrescue} options are +All arguments not explicitly listed as @command{grub2-mkrescue} options are passed on directly to @command{xorriso} in @command{mkisofs} emulation mode. Options passed to @command{xorriso} will normally be interpreted as @command{mkisofs} options; if the option @samp{--} is used, then anything @@ -6633,7 +6650,7 @@ mkdir -p disk/boot/grub grub-mkrescue -o grub.iso disk @end example -@command{grub-mkrescue} accepts the following options: +@command{grub2-mkrescue} accepts the following options: @table @option @item --help @@ -6661,15 +6678,15 @@ Use @var{file} as the @command{xorriso} program, rather than the built-in default. @item --grub-mkimage=@var{file} -Use @var{file} as the @command{grub-mkimage} program, rather than the +Use @var{file} as the @command{grub2-mkimage} program, rather than the built-in default. @end table -@node Invoking grub-mount -@chapter Invoking grub-mount +@node Invoking grub2-mount +@chapter Invoking grub2-mount -The program @command{grub-mount} performs a read-only mount of any file +The program @command{grub2-mount} performs a read-only mount of any file system or file system image that GRUB understands, using GRUB's file system drivers via FUSE. (It is only available if FUSE development files were present when GRUB was built.) This has a number of uses: @@ -6701,13 +6718,13 @@ even if nobody has yet written a FUSE module specifically for that file system type. @end itemize -Using @command{grub-mount} is normally as simple as: +Using @command{grub2-mount} is normally as simple as: @example grub-mount /dev/sda1 /mnt @end example -@command{grub-mount} must be given one or more images and a mount point as +@command{grub2-mount} must be given one or more images and a mount point as non-option arguments (if it is given more than one image, it will treat them as a RAID set), and also accepts the following options: @@ -6729,13 +6746,13 @@ Show debugging output for conditions matching @var{string}. @item -K prompt|@var{file} @itemx --zfs-key=prompt|@var{file} Load a ZFS encryption key. If you use @samp{prompt} as the argument, -@command{grub-mount} will read a passphrase from the terminal; otherwise, it +@command{grub2-mount} will read a passphrase from the terminal; otherwise, it will read key material from the specified file. @item -r @var{device} @itemx --root=@var{device} Set the GRUB root device to @var{device}. You do not normally need to set -this; @command{grub-mount} will automatically set the root device to the +this; @command{grub2-mount} will automatically set the root device to the root of the supplied file system. If @var{device} is just a number, then it will be treated as a partition @@ -6753,10 +6770,10 @@ Print verbose messages. @end table -@node Invoking grub-probe -@chapter Invoking grub-probe +@node Invoking grub2-probe +@chapter Invoking grub2-probe -The program @command{grub-probe} probes device information for a given path +The program @command{grub2-probe} probes device information for a given path or device. @example @@ -6764,7 +6781,7 @@ grub-probe --target=fs /boot/grub grub-probe --target=drive --device /dev/sda1 @end example -@command{grub-probe} must be given a path or device as a non-option +@command{grub2-probe} must be given a path or device as a non-option argument, and also accepts the following options: @table @option @@ -6777,16 +6794,16 @@ Print the version number of GRUB and exit. @item -d @itemx --device If this option is given, then the non-option argument is a system device -name (such as @samp{/dev/sda1}), and @command{grub-probe} will print +name (such as @samp{/dev/sda1}), and @command{grub2-probe} will print information about that device. If it is not given, then the non-option argument is a filesystem path (such as @samp{/boot/grub}), and -@command{grub-probe} will print information about the device containing that +@command{grub2-probe} will print information about the device containing that part of the filesystem. @item -m @var{file} @itemx --device-map=@var{file} Use @var{file} as the device map (@pxref{Device map}) rather than the -default, usually @samp{/boot/grub/device.map}. +default, usually @samp{/boot/grub2/device.map}. @item -t @var{target} @itemx --target=@var{target} @@ -6839,19 +6856,19 @@ Print verbose messages. @end table -@node Invoking grub-script-check -@chapter Invoking grub-script-check +@node Invoking grub2-script-check +@chapter Invoking grub2-script-check -The program @command{grub-script-check} takes a GRUB script file +The program @command{grub2-script-check} takes a GRUB script file (@pxref{Shell-like scripting}) and checks it for syntax errors, similar to commands such as @command{sh -n}. It may take a @var{path} as a non-option argument; if none is supplied, it will read from standard input. @example -grub-script-check /boot/grub/grub.cfg +grub-script-check /boot/grub2/grub.cfg @end example -@command{grub-script-check} accepts the following options: +@command{grub2-script-check} accepts the following options: @table @option @item --help From 5d6756875a7fd5162da57facde7d231ff04740b5 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 24 May 2017 12:42:32 -0400 Subject: [PATCH 040/367] macos: just build chainloader entries, don't try any xnu xnu. Since our bugs tell us that the xnu boot entries really just don't work most of the time, and they create piles of extra boot entries, because they can't quite figure out 32-vs-64 and other stuff like that. It's rediculous, and we should just boot their bootloader through the chainloader instead. So this patch does that. Resolves: rhbz#893179 Signed-off-by: Peter Jones --- util/grub.d/30_os-prober.in | 78 +++++++++---------------------------- 1 file changed, 18 insertions(+), 60 deletions(-) diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in index 1b91c102f3..4b27bd2015 100644 --- a/util/grub.d/30_os-prober.in +++ b/util/grub.d/30_os-prober.in @@ -42,68 +42,25 @@ if [ -z "${OSPROBED}" ] ; then fi osx_entry() { - if [ x$2 = x32 ]; then - # TRANSLATORS: it refers to kernel architecture (32-bit) - bitstr="$(gettext "(32-bit)")" - else - # TRANSLATORS: it refers to kernel architecture (64-bit) - bitstr="$(gettext "(64-bit)")" - fi # TRANSLATORS: it refers on the OS residing on device %s onstr="$(gettext_printf "(on %s)" "${DEVICE}")" - cat << EOF -menuentry '$(echo "${LONGNAME} $bitstr $onstr" | grub_quote)' --class osx --class darwin --class os \$menuentry_id_option 'osprober-xnu-$2-$(grub_get_device_id "${DEVICE}")' { + hints="" + for hint in `"${grub_probe}" --device ${device} --target=efi_hints 2> /dev/null` ; do + hints="${hints} --hint=${hint}" + done + cat << EOF +menuentry '$(echo "${LONGNAME} $onstr" | grub_quote)' --class osx --class darwin --class os \$menuentry_id_option 'osprober-xnu-$2-$(grub_get_device_id "${DEVICE}")' { EOF save_default_entry | grub_add_tab prepare_grub_to_access_device ${DEVICE} | grub_add_tab cat << EOF + set gfxpayload=keep load_video - set do_resume=0 - if [ /var/vm/sleepimage -nt10 / ]; then - if xnu_resume /var/vm/sleepimage; then - set do_resume=1 - fi - fi - if [ \$do_resume = 0 ]; then - xnu_uuid ${OSXUUID} uuid - if [ -f /Extra/DSDT.aml ]; then - acpi -e /Extra/DSDT.aml - fi - if [ /kernelcache -nt /System/Library/Extensions ]; then - $1 /kernelcache boot-uuid=\${uuid} rd=*uuid - elif [ -f /System/Library/Kernels/kernel ]; then - $1 /System/Library/Kernels/kernel boot-uuid=\${uuid} rd=*uuid - xnu_kextdir /System/Library/Extensions - else - $1 /mach_kernel boot-uuid=\${uuid} rd=*uuid - if [ /System/Library/Extensions.mkext -nt /System/Library/Extensions ]; then - xnu_mkext /System/Library/Extensions.mkext - else - xnu_kextdir /System/Library/Extensions - fi - fi - if [ -f /Extra/Extensions.mkext ]; then - xnu_mkext /Extra/Extensions.mkext - fi - if [ -d /Extra/Extensions ]; then - xnu_kextdir /Extra/Extensions - fi - if [ -f /Extra/devprop.bin ]; then - xnu_devprop_load /Extra/devprop.bin - fi - if [ -f /Extra/splash.jpg ]; then - insmod jpeg - xnu_splash /Extra/splash.jpg - fi - if [ -f /Extra/splash.png ]; then - insmod png - xnu_splash /Extra/splash.png - fi - if [ -f /Extra/splash.tga ]; then - insmod tga - xnu_splash /Extra/splash.tga - fi - fi + insmod part_gpt + insmod hfsplus + search --no-floppy --fs-uuid --set=root ${hints} $(grub_get_device_id "${DEVICE}") + chainloader (\$root)/System/Library/CoreServices/boot.efi + boot } EOF } @@ -292,11 +249,12 @@ EOF echo "$title_correction_code" ;; macosx) - if [ "${UUID}" ]; then - OSXUUID="${UUID}" - osx_entry xnu_kernel 32 - osx_entry xnu_kernel64 64 - fi + for subdevice in ${DEVICE%[[:digit:]]*}* ; do + parttype="`"${grub_probe}" --device ${device} --target=gpt_parttype "${subdevice}" 2> /dev/null`" + if [[ "$parttype" = "426f6f74-0000-11aa-aa11-00306543ecac" ]]; then + DEVICE="${subdevice}" osx_entry + fi + done ;; hurd) onstr="$(gettext_printf "(on %s)" "${DEVICE}")" From 2352788ec3f6ed31e921a4905f8a5f71cfea0ff5 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Tue, 9 Jul 2019 13:39:45 +0200 Subject: [PATCH 041/367] grub2/btrfs: Add ability to boot from subvolumes This patch adds the ability to specify a different root on a btrfs filesystem too boot from other than the default one. btrfs-list-snapshots will list the subvolumes available on the filesystem. set btrfs_subvol= and set btrfs_subvolid= will specify which subvolume to use and any pathnames provided with either of those variables set will start using that root. If the subvolume or subvolume id doesn't exist, then an error case will result. It is possible to boot into a separate GRUB instance by exporting the variable and loading the config file from the subvolume. Signed-off-by: Jeff Mahoney --- grub-core/fs/btrfs.c | 548 +++++++++++++++++++++++++++++++++++++++++-- include/grub/btrfs.h | 1 + 2 files changed, 531 insertions(+), 18 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 63203034df..f1fff7385b 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -79,9 +82,11 @@ struct grub_btrfs_superblock grub_uint64_t generation; grub_uint64_t root_tree; grub_uint64_t chunk_tree; - grub_uint8_t dummy2[0x20]; + grub_uint8_t dummy2[0x18]; + grub_uint64_t bytes_used; grub_uint64_t root_dir_objectid; - grub_uint8_t dummy3[0x41]; + grub_uint64_t num_devices; + grub_uint8_t dummy3[0x39]; struct grub_btrfs_device this_device; char label[0x100]; grub_uint8_t dummy4[0x100]; @@ -121,6 +126,7 @@ struct grub_btrfs_data grub_uint64_t exttree; grub_size_t extsize; struct grub_btrfs_extent_data *extent; + grub_uint64_t fs_tree; }; struct grub_btrfs_chunk_item @@ -191,6 +197,14 @@ struct grub_btrfs_leaf_descriptor } *data; }; +struct grub_btrfs_root_ref +{ + grub_uint64_t dirid; + grub_uint64_t sequence; + grub_uint16_t name_len; + const char name[0]; +} __attribute__ ((packed)); + struct grub_btrfs_time { grub_int64_t sec; @@ -236,6 +250,14 @@ struct grub_btrfs_extent_data #define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100 +#define GRUB_BTRFS_ROOT_TREE_OBJECTID 1ULL +#define GRUB_BTRFS_FS_TREE_OBJECTID 5ULL +#define GRUB_BTRFS_ROOT_REF_KEY 156 +#define GRUB_BTRFS_ROOT_ITEM_KEY 132 + +static grub_uint64_t btrfs_default_subvolid = 0; +static char *btrfs_default_subvol = NULL; + static grub_disk_addr_t superblock_sectors[] = { 64 * 2, 64 * 1024 * 2, 256 * 1048576 * 2, 1048576ULL * 1048576ULL * 2 }; @@ -1173,6 +1195,62 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, return GRUB_ERR_NONE; } +static grub_err_t +get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree, + grub_uint64_t objectid, grub_uint64_t offset, + grub_uint64_t *fs_root); + +static grub_err_t +lookup_root_by_id(struct grub_btrfs_data *data, grub_uint64_t id) +{ + grub_err_t err; + grub_uint64_t tree; + + err = get_fs_root(data, data->sblock.root_tree, id, -1, &tree); + if (!err) + data->fs_tree = tree; + return err; +} + +static grub_err_t +find_path (struct grub_btrfs_data *data, + const char *path, struct grub_btrfs_key *key, + grub_uint64_t *tree, grub_uint8_t *type); + +static grub_err_t +lookup_root_by_name(struct grub_btrfs_data *data, const char *path) +{ + grub_err_t err; + grub_uint64_t tree = 0; + grub_uint8_t type; + struct grub_btrfs_key key; + + err = find_path (data, path, &key, &tree, &type); + if (err) + return grub_error(GRUB_ERR_FILE_NOT_FOUND, "couldn't locate %s\n", path); + + if (key.object_id != grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0) + return grub_error(GRUB_ERR_BAD_FILE_TYPE, "%s: not a subvolume\n", path); + + data->fs_tree = tree; + return GRUB_ERR_NONE; +} + +static grub_err_t +btrfs_handle_subvol(struct grub_btrfs_data *data __attribute__ ((unused))) +{ + if (btrfs_default_subvol) + return lookup_root_by_name(data, btrfs_default_subvol); + + if (btrfs_default_subvolid) + return lookup_root_by_id(data, btrfs_default_subvolid); + + data->fs_tree = 0; + + return GRUB_ERR_NONE; +} + + static struct grub_btrfs_data * grub_btrfs_mount (grub_device_t dev) { @@ -1208,6 +1286,13 @@ grub_btrfs_mount (grub_device_t dev) data->devices_attached[0].dev = dev; data->devices_attached[0].id = data->sblock.this_device.device_id; + err = btrfs_handle_subvol (data); + if (err) + { + grub_free (data); + return NULL; + } + return data; } @@ -1673,6 +1758,91 @@ get_root (struct grub_btrfs_data *data, struct grub_btrfs_key *key, return GRUB_ERR_NONE; } +static grub_err_t +find_pathname(struct grub_btrfs_data *data, grub_uint64_t objectid, + grub_uint64_t fs_root, const char *name, char **pathname) +{ + grub_err_t err; + struct grub_btrfs_key key = { + .object_id = objectid, + .type = GRUB_BTRFS_ITEM_TYPE_INODE_REF, + .offset = 0, + }; + struct grub_btrfs_key key_out; + struct grub_btrfs_leaf_descriptor desc; + char *p = grub_strdup (name); + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + grub_size_t alloc = grub_strlen(name) + 1; + + err = lower_bound(data, &key, &key_out, fs_root, + &elemaddr, &elemsize, &desc, 0); + if (err) + return grub_error(err, "lower_bound caught %d\n", err); + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) + next(data, &desc, &elemaddr, &elemsize, &key_out); + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) + { + return grub_error(GRUB_ERR_FILE_NOT_FOUND, + "Can't find inode ref for {%"PRIuGRUB_UINT64_T + ", %u, %"PRIuGRUB_UINT64_T"} %"PRIuGRUB_UINT64_T + "/%"PRIuGRUB_SIZE"\n", + key_out.object_id, key_out.type, + key_out.offset, elemaddr, elemsize); + } + + + while (key_out.type == GRUB_BTRFS_ITEM_TYPE_INODE_REF && + key_out.object_id != key_out.offset) { + struct grub_btrfs_inode_ref *inode_ref; + char *new; + + inode_ref = grub_malloc(elemsize + 1); + if (!inode_ref) + return grub_error(GRUB_ERR_OUT_OF_MEMORY, + "couldn't allocate memory for inode_ref (%"PRIuGRUB_SIZE")\n", elemsize); + + err = grub_btrfs_read_logical(data, elemaddr, inode_ref, elemsize, 0); + if (err) + return grub_error(err, "read_logical caught %d\n", err); + + alloc += grub_le_to_cpu16 (inode_ref->n) + 2; + new = grub_malloc(alloc); + if (!new) + return grub_error(GRUB_ERR_OUT_OF_MEMORY, + "couldn't allocate memory for name (%"PRIuGRUB_SIZE")\n", alloc); + + grub_memcpy(new, inode_ref->name, grub_le_to_cpu16 (inode_ref->n)); + if (p) + { + new[grub_le_to_cpu16 (inode_ref->n)] = '/'; + grub_strcpy (new + grub_le_to_cpu16 (inode_ref->n) + 1, p); + grub_free(p); + } + else + new[grub_le_to_cpu16 (inode_ref->n)] = 0; + grub_free(inode_ref); + + p = new; + + key.object_id = key_out.offset; + + err = lower_bound(data, &key, &key_out, fs_root, &elemaddr, + &elemsize, &desc, 0); + if (err) + return grub_error(err, "lower_bound caught %d\n", err); + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) + next(data, &desc, &elemaddr, &elemsize, &key_out); + + } + + *pathname = p; + return 0; +} + static grub_err_t find_path (struct grub_btrfs_data *data, const char *path, struct grub_btrfs_key *key, @@ -1691,14 +1861,26 @@ find_path (struct grub_btrfs_data *data, char *origpath = NULL; unsigned symlinks_max = 32; - err = get_root (data, key, tree, type); - if (err) - return err; - origpath = grub_strdup (path); if (!origpath) return grub_errno; + if (data->fs_tree) + { + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + *tree = data->fs_tree; + /* This is a tree root, so everything starts at objectid 256 */ + key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + } + else + { + err = get_root (data, key, tree, type); + if (err) + return err; + } + while (1) { while (path[0] == '/') @@ -1871,9 +2053,21 @@ find_path (struct grub_btrfs_data *data, path = path_alloc = tmp; if (path[0] == '/') { - err = get_root (data, key, tree, type); - if (err) - return err; + if (data->fs_tree) + { + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + *tree = data->fs_tree; + /* This is a tree root, so everything starts at objectid 256 */ + key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + } + else + { + err = get_root (data, key, tree, type); + if (err) + return err; + } } continue; } @@ -2114,6 +2308,20 @@ grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len) data->tree, file->offset, buf, len); } +static char * +btrfs_unparse_uuid(struct grub_btrfs_data *data) +{ + return grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", + grub_be_to_cpu16 (data->sblock.uuid[0]), + grub_be_to_cpu16 (data->sblock.uuid[1]), + grub_be_to_cpu16 (data->sblock.uuid[2]), + grub_be_to_cpu16 (data->sblock.uuid[3]), + grub_be_to_cpu16 (data->sblock.uuid[4]), + grub_be_to_cpu16 (data->sblock.uuid[5]), + grub_be_to_cpu16 (data->sblock.uuid[6]), + grub_be_to_cpu16 (data->sblock.uuid[7])); +} + static grub_err_t grub_btrfs_uuid (grub_device_t device, char **uuid) { @@ -2125,15 +2333,7 @@ grub_btrfs_uuid (grub_device_t device, char **uuid) if (!data) return grub_errno; - *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", - grub_be_to_cpu16 (data->sblock.uuid[0]), - grub_be_to_cpu16 (data->sblock.uuid[1]), - grub_be_to_cpu16 (data->sblock.uuid[2]), - grub_be_to_cpu16 (data->sblock.uuid[3]), - grub_be_to_cpu16 (data->sblock.uuid[4]), - grub_be_to_cpu16 (data->sblock.uuid[5]), - grub_be_to_cpu16 (data->sblock.uuid[6]), - grub_be_to_cpu16 (data->sblock.uuid[7])); + *uuid = btrfs_unparse_uuid(data); grub_btrfs_unmount (data); @@ -2190,6 +2390,242 @@ grub_btrfs_embed (grub_device_t device __attribute__ ((unused)), } #endif +static grub_err_t +grub_cmd_btrfs_info (grub_command_t cmd __attribute__ ((unused)), int argc, + char **argv) +{ + grub_device_t dev; + char *devname; + struct grub_btrfs_data *data; + char *uuid; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + devname = grub_file_get_device_name(argv[0]); + + if (!devname) + return grub_errno; + + dev = grub_device_open (devname); + grub_free (devname); + if (!dev) + return grub_errno; + + data = grub_btrfs_mount (dev); + if (!data) + { + grub_device_close(dev); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to open fs"); + } + + if (data->sblock.label) + grub_printf("Label: '%s' ", data->sblock.label); + else + grub_printf("Label: none "); + + uuid = btrfs_unparse_uuid(data); + + grub_printf(" uuid: %s\n\tTotal devices %" PRIuGRUB_UINT64_T + " FS bytes used %" PRIuGRUB_UINT64_T "\n", + uuid, grub_cpu_to_le64(data->sblock.num_devices), + grub_cpu_to_le64(data->sblock.bytes_used)); + + grub_btrfs_unmount (data); + + return 0; +} + +static grub_err_t +get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree, + grub_uint64_t objectid, grub_uint64_t offset, + grub_uint64_t *fs_root) +{ + grub_err_t err; + struct grub_btrfs_key key_in = { + .object_id = objectid, + .type = GRUB_BTRFS_ROOT_ITEM_KEY, + .offset = offset, + }, key_out; + struct grub_btrfs_leaf_descriptor desc; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + struct grub_btrfs_root_item ri; + + err = lower_bound(data, &key_in, &key_out, tree, + &elemaddr, &elemsize, &desc, 0); + + if (err) + return err; + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM || elemaddr == 0) + return grub_error(GRUB_ERR_FILE_NOT_FOUND, + N_("can't find fs root for subvol %"PRIuGRUB_UINT64_T"\n"), + key_in.object_id); + + err = grub_btrfs_read_logical (data, elemaddr, &ri, sizeof (ri), 0); + if (err) + return err; + + *fs_root = ri.tree; + + return GRUB_ERR_NONE; +} + +static const struct grub_arg_option options[] = { + {"output", 'o', 0, N_("Output to a variable instead of the console."), + N_("VARNAME"), ARG_TYPE_STRING}, + {"path-only", 'p', 0, N_("Show only the path of the subvolume."), 0, 0}, + {"id-only", 'i', 0, N_("Show only the id of the subvolume."), 0, 0}, + {0, 0, 0, 0, 0, 0} +}; + +static grub_err_t +grub_cmd_btrfs_list_subvols (struct grub_extcmd_context *ctxt, + int argc, char **argv) +{ + struct grub_btrfs_data *data; + grub_device_t dev; + char *devname; + grub_uint64_t tree; + struct grub_btrfs_key key_in = { + .object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_FS_TREE_OBJECTID), + .type = GRUB_BTRFS_ROOT_REF_KEY, + .offset = 0, + }, key_out; + struct grub_btrfs_leaf_descriptor desc; + grub_disk_addr_t elemaddr; + grub_uint64_t fs_root = 0; + grub_size_t elemsize; + grub_size_t allocated = 0; + int r = 0; + grub_err_t err; + char *buf = NULL; + int print = 1; + int path_only = ctxt->state[1].set; + int num_only = ctxt->state[2].set; + char *varname = NULL; + char *output = NULL; + + if (ctxt->state[0].set) { + varname = ctxt->state[0].arg; + print = 0; + } + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + devname = grub_file_get_device_name(argv[0]); + if (!devname) + return grub_errno; + + dev = grub_device_open (devname); + grub_free (devname); + if (!dev) + return grub_errno; + + data = grub_btrfs_mount(dev); + if (!data) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "could not open device"); + + tree = data->sblock.root_tree; + err = get_fs_root(data, tree, grub_cpu_to_le64_compile_time (GRUB_BTRFS_FS_TREE_OBJECTID), + 0, &fs_root); + if (err) + goto out; + + err = lower_bound(data, &key_in, &key_out, tree, + &elemaddr, &elemsize, &desc, 0); + + if (err) + { + grub_btrfs_unmount(data); + return err; + } + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF || elemaddr == 0) + { + r = next(data, &desc, &elemaddr, &elemsize, &key_out); + } + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF) { + err = GRUB_ERR_FILE_NOT_FOUND; + grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("can't find root refs")); + goto out; + } + + do + { + struct grub_btrfs_root_ref *ref; + char *p = NULL; + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF) + { + r = 0; + break; + } + + if (elemsize > allocated) + { + grub_free(buf); + allocated = 2 * elemsize; + buf = grub_malloc(allocated + 1); + if (!buf) + { + r = -grub_errno; + break; + } + } + ref = (struct grub_btrfs_root_ref *)buf; + + err = grub_btrfs_read_logical(data, elemaddr, buf, elemsize, 0); + if (err) + { + r = -err; + break; + } + buf[elemsize] = 0; + + find_pathname(data, ref->dirid, fs_root, ref->name, &p); + + if (print) + { + if (num_only) + grub_printf("ID %"PRIuGRUB_UINT64_T"\n", key_out.offset); + else if (path_only) + grub_printf("%s\n", p); + else + grub_printf("ID %"PRIuGRUB_UINT64_T" path %s\n", key_out.offset, p); + } else { + char *old = output; + if (num_only) + output = grub_xasprintf("%s%"PRIuGRUB_UINT64_T"\n", + old ?: "", key_out.offset); + else if (path_only) + output = grub_xasprintf("%s%s\n", old ?: "", p); + else + output = grub_xasprintf("%sID %"PRIuGRUB_UINT64_T" path %s\n", + old ?: "", key_out.offset, p); + + if (old) + grub_free(old); + } + + r = next(data, &desc, &elemaddr, &elemsize, &key_out); + } while(r > 0); + + if (output) + grub_env_set(varname, output); + +out: + free_iterator(&desc); + grub_btrfs_unmount(data); + + grub_device_close (dev); + + return 0; +} + static struct grub_fs grub_btrfs_fs = { .name = "btrfs", .fs_dir = grub_btrfs_dir, @@ -2205,12 +2641,88 @@ static struct grub_fs grub_btrfs_fs = { #endif }; +static grub_command_t cmd_info; +static grub_extcmd_t cmd_list_subvols; + +static char * +subvolid_set_env (struct grub_env_var *var __attribute__ ((unused)), + const char *val) +{ + unsigned long long result = 0; + + grub_errno = GRUB_ERR_NONE; + if (*val) + { + result = grub_strtoull(val, NULL, 10); + if (grub_errno) + return NULL; + } + + grub_free (btrfs_default_subvol); + btrfs_default_subvol = NULL; + btrfs_default_subvolid = result; + return grub_strdup(val); +} + +static const char * +subvolid_get_env (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + if (btrfs_default_subvol) + return grub_xasprintf("subvol:%s", btrfs_default_subvol); + else if (btrfs_default_subvolid) + return grub_xasprintf("%"PRIuGRUB_UINT64_T, btrfs_default_subvolid); + else + return ""; +} + +static char * +subvol_set_env (struct grub_env_var *var __attribute__ ((unused)), + const char *val) +{ + grub_free (btrfs_default_subvol); + btrfs_default_subvol = grub_strdup (val); + btrfs_default_subvolid = 0; + return grub_strdup(val); +} + +static const char * +subvol_get_env (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + if (btrfs_default_subvol) + return btrfs_default_subvol; + else if (btrfs_default_subvolid) + return grub_xasprintf("subvolid:%" PRIuGRUB_UINT64_T, + btrfs_default_subvolid); + else + return ""; +} + GRUB_MOD_INIT (btrfs) { grub_fs_register (&grub_btrfs_fs); + cmd_info = grub_register_command("btrfs-info", grub_cmd_btrfs_info, + "DEVICE", + "Print BtrFS info about DEVICE."); + cmd_list_subvols = grub_register_extcmd("btrfs-list-subvols", + grub_cmd_btrfs_list_subvols, 0, + "[-p|-n] [-o var] DEVICE", + "Print list of BtrFS subvolumes on " + "DEVICE.", options); + grub_register_variable_hook ("btrfs_subvol", subvol_get_env, + subvol_set_env); + grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env, + subvolid_set_env); } GRUB_MOD_FINI (btrfs) { + grub_register_variable_hook ("btrfs_subvol", NULL, NULL); + grub_register_variable_hook ("btrfs_subvolid", NULL, NULL); + grub_unregister_command (cmd_info); + grub_unregister_extcmd (cmd_list_subvols); grub_fs_unregister (&grub_btrfs_fs); } + +// vim: si et sw=2: diff --git a/include/grub/btrfs.h b/include/grub/btrfs.h index 9d93fb6c18..234ad97677 100644 --- a/include/grub/btrfs.h +++ b/include/grub/btrfs.h @@ -29,6 +29,7 @@ enum GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM = 0x84, GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF = 0x90, GRUB_BTRFS_ITEM_TYPE_DEVICE = 0xd8, + GRUB_BTRFS_ITEM_TYPE_ROOT_REF = 0x9c, GRUB_BTRFS_ITEM_TYPE_CHUNK = 0xe4 }; From d1d177632d544d4e0fb0f7c31d82d40bbaec6fb0 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Wed, 18 Dec 2013 09:57:04 +0000 Subject: [PATCH 042/367] export btrfs_subvol and btrfs_subvolid We should export btrfs_subvol and btrfs_subvolid to have both visible to subsidiary configuration files loaded using configfile. Signed-off-by: Michael Chang --- grub-core/fs/btrfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index f1fff7385b..ad1b56b716 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -2714,6 +2714,8 @@ GRUB_MOD_INIT (btrfs) subvol_set_env); grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env, subvolid_set_env); + grub_env_export ("btrfs_subvol"); + grub_env_export ("btrfs_subvolid"); } GRUB_MOD_FINI (btrfs) From 31dd39cd711df53267844991a90c25569f27e02e Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Thu, 21 Aug 2014 03:39:11 +0000 Subject: [PATCH 043/367] grub2-btrfs-03-follow_default Signed-off-by: Michael Chang Signed-off-by: Robbie Harwood --- grub-core/fs/btrfs.c | 107 ++++++++++++++++++++++++++++++------------- 1 file changed, 76 insertions(+), 31 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index ad1b56b716..113c1f746c 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -1256,6 +1256,7 @@ grub_btrfs_mount (grub_device_t dev) { struct grub_btrfs_data *data; grub_err_t err; + const char *relpath = grub_env_get ("btrfs_relative_path"); if (!dev->disk) { @@ -1286,11 +1287,14 @@ grub_btrfs_mount (grub_device_t dev) data->devices_attached[0].dev = dev; data->devices_attached[0].id = data->sblock.this_device.device_id; - err = btrfs_handle_subvol (data); - if (err) + if (relpath && (relpath[0] == '1' || relpath[0] == 'y')) { - grub_free (data); - return NULL; + err = btrfs_handle_subvol (data); + if (err) + { + grub_free (data); + return NULL; + } } return data; @@ -1855,24 +1859,39 @@ find_path (struct grub_btrfs_data *data, grub_size_t allocated = 0; struct grub_btrfs_dir_item *direl = NULL; struct grub_btrfs_key key_out; + int follow_default; const char *ctoken; grub_size_t ctokenlen; char *path_alloc = NULL; char *origpath = NULL; unsigned symlinks_max = 32; + const char *relpath = grub_env_get ("btrfs_relative_path"); + follow_default = 0; origpath = grub_strdup (path); if (!origpath) return grub_errno; - if (data->fs_tree) + if (relpath && (relpath[0] == '1' || relpath[0] == 'y')) { - *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; - *tree = data->fs_tree; - /* This is a tree root, so everything starts at objectid 256 */ - key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); - key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; - key->offset = 0; + if (data->fs_tree) + { + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + *tree = data->fs_tree; + /* This is a tree root, so everything starts at objectid 256 */ + key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + } + else + { + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + *tree = data->sblock.root_tree; + key->object_id = data->sblock.root_dir_objectid; + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + follow_default = 1; + } } else { @@ -1883,15 +1902,23 @@ find_path (struct grub_btrfs_data *data, while (1) { - while (path[0] == '/') - path++; - if (!path[0]) - break; - slash = grub_strchr (path, '/'); - if (!slash) - slash = path + grub_strlen (path); - ctoken = path; - ctokenlen = slash - path; + if (!follow_default) + { + while (path[0] == '/') + path++; + if (!path[0]) + break; + slash = grub_strchr (path, '/'); + if (!slash) + slash = path + grub_strlen (path); + ctoken = path; + ctokenlen = slash - path; + } + else + { + ctoken = "default"; + ctokenlen = sizeof ("default") - 1; + } if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) { @@ -1902,7 +1929,9 @@ find_path (struct grub_btrfs_data *data, if (ctokenlen == 1 && ctoken[0] == '.') { - path = slash; + if (!follow_default) + path = slash; + follow_default = 0; continue; } if (ctokenlen == 2 && ctoken[0] == '.' && ctoken[1] == '.') @@ -1933,8 +1962,9 @@ find_path (struct grub_btrfs_data *data, *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; key->object_id = key_out.offset; - path = slash; - + if (!follow_default) + path = slash; + follow_default = 0; continue; } @@ -2003,7 +2033,9 @@ find_path (struct grub_btrfs_data *data, return err; } - path = slash; + if (!follow_default) + path = slash; + follow_default = 0; if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK) { struct grub_btrfs_inode inode; @@ -2053,14 +2085,26 @@ find_path (struct grub_btrfs_data *data, path = path_alloc = tmp; if (path[0] == '/') { - if (data->fs_tree) + if (relpath && (relpath[0] == '1' || relpath[0] == 'y')) { - *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; - *tree = data->fs_tree; - /* This is a tree root, so everything starts at objectid 256 */ - key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); - key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; - key->offset = 0; + if (data->fs_tree) + { + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + *tree = data->fs_tree; + /* This is a tree root, so everything starts at objectid 256 */ + key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + } + else + { + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + *tree = data->sblock.root_tree; + key->object_id = data->sblock.root_dir_objectid; + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + follow_default = 1; + } } else { @@ -2716,6 +2760,7 @@ GRUB_MOD_INIT (btrfs) subvolid_set_env); grub_env_export ("btrfs_subvol"); grub_env_export ("btrfs_subvolid"); + grub_env_export ("btrfs_relative_path"); } GRUB_MOD_FINI (btrfs) From c22bfb000bef3248fe89938643a8bef764cfbed3 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Thu, 21 Aug 2014 03:39:11 +0000 Subject: [PATCH 044/367] grub2-btrfs-04-grub2-install Signed-off-by: Michael Chang Signed-off-by: Robbie Harwood --- grub-core/osdep/linux/getroot.c | 7 +++++++ grub-core/osdep/unix/config.c | 17 +++++++++++++++-- include/grub/emu/config.h | 1 + util/config.c | 10 ++++++++++ util/grub-install.c | 15 +++++++++++++++ util/grub-mkrelpath.c | 6 ++++++ 6 files changed, 54 insertions(+), 2 deletions(-) diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c index 001b818fe5..caf9b1ccd3 100644 --- a/grub-core/osdep/linux/getroot.c +++ b/grub-core/osdep/linux/getroot.c @@ -376,6 +376,7 @@ get_btrfs_fs_prefix (const char *mount_path) return NULL; } +int use_relative_path_on_btrfs = 0; char ** grub_find_root_devices_from_mountinfo (const char *dir, char **relroot) @@ -519,6 +520,12 @@ grub_find_root_devices_from_mountinfo (const char *dir, char **relroot) { ret = grub_find_root_devices_from_btrfs (dir); fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path); + if (use_relative_path_on_btrfs) + { + if (fs_prefix) + free (fs_prefix); + fs_prefix = xstrdup ("/"); + } } else if (!retry && grub_strcmp (entries[i].fstype, "autofs") == 0) { diff --git a/grub-core/osdep/unix/config.c b/grub-core/osdep/unix/config.c index 7d6325138c..46a881530c 100644 --- a/grub-core/osdep/unix/config.c +++ b/grub-core/osdep/unix/config.c @@ -82,6 +82,19 @@ grub_util_load_config (struct grub_util_config *cfg) if (v) cfg->grub_distributor = xstrdup (v); + v = getenv ("SUSE_BTRFS_SNAPSHOT_BOOTING"); + if (v) + { + if (grub_strncmp(v, "true", sizeof ("true") - 1) == 0) + { + cfg->is_suse_btrfs_snapshot_enabled = 1; + } + else + { + cfg->is_suse_btrfs_snapshot_enabled = 0; + } + } + cfgfile = grub_util_get_config_filename (); if (!grub_util_is_regular (cfgfile)) return; @@ -105,8 +118,8 @@ grub_util_load_config (struct grub_util_config *cfg) *ptr++ = *iptr; } - strcpy (ptr, "'; printf \"GRUB_ENABLE_CRYPTODISK=%s\\nGRUB_DISTRIBUTOR=%s\\n\" " - "\"$GRUB_ENABLE_CRYPTODISK\" \"$GRUB_DISTRIBUTOR\""); + strcpy (ptr, "'; printf \"GRUB_ENABLE_CRYPTODISK=%s\\nGRUB_DISTRIBUTOR=%s\\nSUSE_BTRFS_SNAPSHOT_BOOTING=%s\\n\" " + "\"$GRUB_ENABLE_CRYPTODISK\" \"$GRUB_DISTRIBUTOR\" \"$SUSE_BTRFS_SNAPSHOT_BOOTING\""); argv[2] = script; argv[3] = '\0'; diff --git a/include/grub/emu/config.h b/include/grub/emu/config.h index 875d5896ce..c9a7e5f4ad 100644 --- a/include/grub/emu/config.h +++ b/include/grub/emu/config.h @@ -37,6 +37,7 @@ struct grub_util_config { int is_cryptodisk_enabled; char *grub_distributor; + int is_suse_btrfs_snapshot_enabled; }; void diff --git a/util/config.c b/util/config.c index ebcdd8f5e2..f044a880a7 100644 --- a/util/config.c +++ b/util/config.c @@ -42,6 +42,16 @@ grub_util_parse_config (FILE *f, struct grub_util_config *cfg, int simple) cfg->is_cryptodisk_enabled = 1; continue; } + if (grub_strncmp (ptr, "SUSE_BTRFS_SNAPSHOT_BOOTING=", + sizeof ("SUSE_BTRFS_SNAPSHOT_BOOTING=") - 1) == 0) + { + ptr += sizeof ("SUSE_BTRFS_SNAPSHOT_BOOTING=") - 1; + if (*ptr == '"' || *ptr == '\'') + ptr++; + if (grub_strncmp(ptr, "true", sizeof ("true") - 1) == 0) + cfg->is_suse_btrfs_snapshot_enabled = 1; + continue; + } if (grub_strncmp (ptr, "GRUB_DISTRIBUTOR=", sizeof ("GRUB_DISTRIBUTOR=") - 1) == 0) { diff --git a/util/grub-install.c b/util/grub-install.c index 0fbe7f78c6..0f66f36d23 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -827,6 +827,8 @@ fill_core_services (const char *core_services) free (sysv_plist); } +extern int use_relative_path_on_btrfs; + int main (int argc, char *argv[]) { @@ -860,6 +862,9 @@ main (int argc, char *argv[]) grub_util_load_config (&config); + if (config.is_suse_btrfs_snapshot_enabled) + use_relative_path_on_btrfs = 1; + if (!bootloader_id && config.grub_distributor) { char *ptr; @@ -1352,6 +1357,16 @@ main (int argc, char *argv[]) fprintf (load_cfg_f, "set debug='%s'\n", debug_image); } + + if (config.is_suse_btrfs_snapshot_enabled + && grub_strncmp(grub_fs->name, "btrfs", sizeof ("btrfs") - 1) == 0) + { + if (!load_cfg_f) + load_cfg_f = grub_util_fopen (load_cfg, "wb"); + have_load_cfg = 1; + fprintf (load_cfg_f, "set btrfs_relative_path='y'\n"); + } + char *prefix_drive = NULL; char *install_drive = NULL; diff --git a/util/grub-mkrelpath.c b/util/grub-mkrelpath.c index 47a241a391..5db7a9a7d9 100644 --- a/util/grub-mkrelpath.c +++ b/util/grub-mkrelpath.c @@ -40,9 +40,12 @@ struct arguments }; static struct argp_option options[] = { + {"relative", 'r', 0, 0, "use relative path on btrfs", 0}, { 0, 0, 0, 0, 0, 0 } }; +extern int use_relative_path_on_btrfs; + static error_t argp_parser (int key, char *arg, struct argp_state *state) { @@ -52,6 +55,9 @@ argp_parser (int key, char *arg, struct argp_state *state) switch (key) { + case 'r': + use_relative_path_on_btrfs = 1; + break; case ARGP_KEY_ARG: if (state->arg_num == 0) arguments->pathname = xstrdup (arg); From 933fb3ca01821ac9328fa99509026563064ded21 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Thu, 21 Aug 2014 03:39:11 +0000 Subject: [PATCH 045/367] grub2-btrfs-05-grub2-mkconfig Signed-off-by: Michael Chang --- util/grub-mkconfig.in | 3 ++- util/grub-mkconfig_lib.in | 4 ++++ util/grub.d/00_header.in | 25 ++++++++++++++++++++++++- util/grub.d/10_linux.in | 4 ++++ util/grub.d/20_linux_xen.in | 4 ++++ 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index 005f093809..535c0f0249 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -252,7 +252,8 @@ export GRUB_DEFAULT \ GRUB_BADRAM \ GRUB_OS_PROBER_SKIP_LIST \ GRUB_DISABLE_SUBMENU \ - GRUB_DEFAULT_DTB + GRUB_DEFAULT_DTB \ + SUSE_BTRFS_SNAPSHOT_BOOTING if test "x${grub_cfg}" != "x"; then rm -f "${grub_cfg}.new" diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in index 0f6505bf3b..5e96f6cc5d 100644 --- a/util/grub-mkconfig_lib.in +++ b/util/grub-mkconfig_lib.in @@ -49,7 +49,11 @@ grub_warn () make_system_path_relative_to_its_root () { + if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ] ; then + "${grub_mkrelpath}" -r "$1" + else "${grub_mkrelpath}" "$1" + fi } is_path_readable_by_grub () diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in index 858b526c92..de727e6ee6 100644 --- a/util/grub.d/00_header.in +++ b/util/grub.d/00_header.in @@ -27,6 +27,14 @@ export TEXTDOMAINDIR="@localedir@" . "$pkgdatadir/grub-mkconfig_lib" +if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ] && + [ "x${GRUB_FS}" = "xbtrfs" ] ; then + cat </dev/null || true` diff --git a/util/grub.d/20_linux_xen.in b/util/grub.d/20_linux_xen.in index ada20775a1..e9e73b815f 100644 --- a/util/grub.d/20_linux_xen.in +++ b/util/grub.d/20_linux_xen.in @@ -73,10 +73,14 @@ fi case x"$GRUB_FS" in xbtrfs) + if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ]; then + GRUB_CMDLINE_LINUX="${GRUB_CMDLINE_LINUX} \${extra_cmdline}" + else rootsubvol="`make_system_path_relative_to_its_root /`" rootsubvol="${rootsubvol#/}" if [ "x${rootsubvol}" != x ]; then GRUB_CMDLINE_LINUX="rootflags=subvol=${rootsubvol} ${GRUB_CMDLINE_LINUX}" + fi fi;; xzfs) rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || true` From c23332d5f6dd449f8f2ea775452e789ef06e3899 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Tue, 9 Jul 2019 13:56:16 +0200 Subject: [PATCH 046/367] grub2-btrfs-06-subvol-mount Signed-off-by: Michael Chang Signed-off-by: Robbie Harwood --- grub-core/fs/btrfs.c | 195 +++++++++++++++++++++++++++++++- grub-core/osdep/linux/getroot.c | 148 +++++++++++++++++++++++- include/grub/emu/getroot.h | 5 + util/grub-install.c | 49 ++++++++ 4 files changed, 392 insertions(+), 5 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 113c1f746c..d323746ecf 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -266,6 +267,12 @@ static grub_err_t grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, void *buf, grub_size_t size, int recursion_depth); +static grub_err_t +get_root (struct grub_btrfs_data *data, struct grub_btrfs_key *key, + grub_uint64_t *tree, grub_uint8_t *type); + +grub_uint64_t +find_mtab_subvol_tree (const char *path, char **path_in_subvol); static grub_err_t read_sblock (grub_disk_t disk, struct grub_btrfs_superblock *sb) @@ -1223,9 +1230,26 @@ lookup_root_by_name(struct grub_btrfs_data *data, const char *path) grub_err_t err; grub_uint64_t tree = 0; grub_uint8_t type; + grub_uint64_t saved_tree; struct grub_btrfs_key key; + if (path[0] == '\0') + { + data->fs_tree = 0; + return GRUB_ERR_NONE; + } + + err = get_root (data, &key, &tree, &type); + if (err) + return err; + + saved_tree = data->fs_tree; + data->fs_tree = tree; + err = find_path (data, path, &key, &tree, &type); + + data->fs_tree = saved_tree; + if (err) return grub_error(GRUB_ERR_FILE_NOT_FOUND, "couldn't locate %s\n", path); @@ -2199,11 +2223,20 @@ grub_btrfs_dir (grub_device_t device, const char *path, int r = 0; grub_uint64_t tree; grub_uint8_t type; + char *new_path = NULL; if (!data) return grub_errno; - err = find_path (data, path, &key_in, &tree, &type); + tree = find_mtab_subvol_tree (path, &new_path); + + if (tree) + data->fs_tree = tree; + + err = find_path (data, new_path ? new_path : path, &key_in, &tree, &type); + if (new_path) + grub_free (new_path); + if (err) { grub_btrfs_unmount (data); @@ -2305,11 +2338,21 @@ grub_btrfs_open (struct grub_file *file, const char *name) struct grub_btrfs_inode inode; grub_uint8_t type; struct grub_btrfs_key key_in; + grub_uint64_t tree; + char *new_path = NULL; if (!data) return grub_errno; - err = find_path (data, name, &key_in, &data->tree, &type); + tree = find_mtab_subvol_tree (name, &new_path); + + if (tree) + data->fs_tree = tree; + + err = find_path (data, new_path ? new_path : name, &key_in, &data->tree, &type); + if (new_path) + grub_free (new_path); + if (err) { grub_btrfs_unmount (data); @@ -2480,6 +2523,150 @@ grub_cmd_btrfs_info (grub_command_t cmd __attribute__ ((unused)), int argc, return 0; } +struct grub_btrfs_mtab +{ + struct grub_btrfs_mtab *next; + struct grub_btrfs_mtab **prev; + char *path; + char *subvol; + grub_uint64_t tree; +}; + +typedef struct grub_btrfs_mtab* grub_btrfs_mtab_t; + +static struct grub_btrfs_mtab *btrfs_mtab; + +#define FOR_GRUB_MTAB(var) FOR_LIST_ELEMENTS (var, btrfs_mtab) +#define FOR_GRUB_MTAB_SAFE(var, next) FOR_LIST_ELEMENTS_SAFE((var), (next), btrfs_mtab) + +static void +add_mountpoint (const char *path, const char *subvol, grub_uint64_t tree) +{ + grub_btrfs_mtab_t m = grub_malloc (sizeof (*m)); + + m->path = grub_strdup (path); + m->subvol = grub_strdup (subvol); + m->tree = tree; + grub_list_push (GRUB_AS_LIST_P (&btrfs_mtab), GRUB_AS_LIST (m)); +} + +static grub_err_t +grub_cmd_btrfs_mount_subvol (grub_command_t cmd __attribute__ ((unused)), int argc, + char **argv) +{ + char *devname, *dirname, *subvol; + struct grub_btrfs_key key_in; + grub_uint8_t type; + grub_uint64_t tree; + grub_uint64_t saved_tree; + grub_err_t err; + struct grub_btrfs_data *data = NULL; + grub_device_t dev = NULL; + + if (argc < 3) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "required and "); + + devname = grub_file_get_device_name(argv[0]); + dev = grub_device_open (devname); + grub_free (devname); + + if (!dev) + { + err = grub_errno; + goto err_out; + } + + dirname = argv[1]; + subvol = argv[2]; + + data = grub_btrfs_mount (dev); + if (!data) + { + err = grub_errno; + goto err_out; + } + + err = find_path (data, dirname, &key_in, &tree, &type); + if (err) + goto err_out; + + if (type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); + goto err_out; + } + + err = get_root (data, &key_in, &tree, &type); + + if (err) + goto err_out; + + saved_tree = data->fs_tree; + data->fs_tree = tree; + err = find_path (data, subvol, &key_in, &tree, &type); + data->fs_tree = saved_tree; + + if (err) + goto err_out; + + if (key_in.object_id != grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "%s: not a subvolume\n", subvol); + goto err_out; + } + + grub_btrfs_unmount (data); + grub_device_close (dev); + add_mountpoint (dirname, subvol, tree); + + return GRUB_ERR_NONE; + +err_out: + + if (data) + grub_btrfs_unmount (data); + + if (dev) + grub_device_close (dev); + + return err; +} + +grub_uint64_t +find_mtab_subvol_tree (const char *path, char **path_in_subvol) +{ + grub_btrfs_mtab_t m, cm; + grub_uint64_t tree; + + if (!path || !path_in_subvol) + return 0; + + *path_in_subvol = NULL; + tree = 0; + cm = NULL; + + FOR_GRUB_MTAB (m) + { + if (grub_strncmp (path, m->path, grub_strlen (m->path)) == 0) + { + if (!cm) + cm = m; + else + if (grub_strcmp (m->path, cm->path) > 0) + cm = m; + } + } + + if (cm) + { + const char *s = path + grub_strlen (cm->path); + *path_in_subvol = (s[0] == '\0') ? grub_strdup ("/") : grub_strdup (s); + tree = cm->tree; + } + + return tree; +} + static grub_err_t get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree, grub_uint64_t objectid, grub_uint64_t offset, @@ -2686,6 +2873,7 @@ static struct grub_fs grub_btrfs_fs = { }; static grub_command_t cmd_info; +static grub_command_t cmd_mount_subvol; static grub_extcmd_t cmd_list_subvols; static char * @@ -2749,6 +2937,9 @@ GRUB_MOD_INIT (btrfs) cmd_info = grub_register_command("btrfs-info", grub_cmd_btrfs_info, "DEVICE", "Print BtrFS info about DEVICE."); + cmd_mount_subvol = grub_register_command("btrfs-mount-subvol", grub_cmd_btrfs_mount_subvol, + "DEVICE DIRECTORY SUBVOL", + "Set btrfs DEVICE the DIRECTORY a mountpoint of SUBVOL."); cmd_list_subvols = grub_register_extcmd("btrfs-list-subvols", grub_cmd_btrfs_list_subvols, 0, "[-p|-n] [-o var] DEVICE", diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c index caf9b1ccd3..28790307e0 100644 --- a/grub-core/osdep/linux/getroot.c +++ b/grub-core/osdep/linux/getroot.c @@ -107,6 +107,14 @@ struct btrfs_ioctl_search_key grub_uint32_t unused[9]; }; +struct btrfs_ioctl_search_header { + grub_uint64_t transid; + grub_uint64_t objectid; + grub_uint64_t offset; + grub_uint32_t type; + grub_uint32_t len; +}; + struct btrfs_ioctl_search_args { struct btrfs_ioctl_search_key key; grub_uint64_t buf[(4096 - sizeof(struct btrfs_ioctl_search_key)) @@ -378,6 +386,109 @@ get_btrfs_fs_prefix (const char *mount_path) int use_relative_path_on_btrfs = 0; +static char * +get_btrfs_subvol (const char *path) +{ + struct btrfs_ioctl_ino_lookup_args args; + grub_uint64_t tree_id; + int fd = -1; + char *ret = NULL; + + fd = open (path, O_RDONLY); + + if (fd < 0) + return NULL; + + memset (&args, 0, sizeof(args)); + args.objectid = GRUB_BTRFS_TREE_ROOT_OBJECTID; + + if (ioctl (fd, BTRFS_IOC_INO_LOOKUP, &args) < 0) + goto error; + + tree_id = args.treeid; + + while (tree_id != GRUB_BTRFS_ROOT_VOL_OBJECTID) + { + struct btrfs_ioctl_search_args sargs; + struct grub_btrfs_root_backref *br; + struct btrfs_ioctl_search_header *search_header; + char *old; + grub_uint16_t len; + grub_uint64_t inode_id; + + memset (&sargs, 0, sizeof(sargs)); + + sargs.key.tree_id = 1; + sargs.key.min_objectid = tree_id; + sargs.key.max_objectid = tree_id; + + sargs.key.min_offset = 0; + sargs.key.max_offset = ~0ULL; + sargs.key.min_transid = 0; + sargs.key.max_transid = ~0ULL; + sargs.key.min_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF; + sargs.key.max_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF; + + sargs.key.nr_items = 1; + + if (ioctl (fd, BTRFS_IOC_TREE_SEARCH, &sargs) < 0) + goto error; + + if (sargs.key.nr_items == 0) + goto error; + + search_header = (struct btrfs_ioctl_search_header *)sargs.buf; + br = (struct grub_btrfs_root_backref *) (search_header + 1); + + len = grub_le_to_cpu16 (br->n); + inode_id = grub_le_to_cpu64 (br->inode_id); + tree_id = search_header->offset; + + old = ret; + ret = malloc (len + 1); + memcpy (ret, br->name, len); + ret[len] = '\0'; + + if (inode_id != GRUB_BTRFS_TREE_ROOT_OBJECTID) + { + char *s; + + memset(&args, 0, sizeof(args)); + args.treeid = search_header->offset; + args.objectid = inode_id; + + if (ioctl (fd, BTRFS_IOC_INO_LOOKUP, &args) < 0) + goto error; + + s = xasprintf ("%s%s", args.name, ret); + free (ret); + ret = s; + } + + if (old) + { + char *s = xasprintf ("%s/%s", ret, old); + free (ret); + free (old); + ret = s; + } + } + + close (fd); + return ret; + +error: + + if (fd >= 0) + close (fd); + if (ret) + free (ret); + + return NULL; +} + +void (*grub_find_root_btrfs_mount_path_hook)(const char *mount_path); + char ** grub_find_root_devices_from_mountinfo (const char *dir, char **relroot) { @@ -519,12 +630,15 @@ grub_find_root_devices_from_mountinfo (const char *dir, char **relroot) else if (grub_strcmp (entries[i].fstype, "btrfs") == 0) { ret = grub_find_root_devices_from_btrfs (dir); - fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path); if (use_relative_path_on_btrfs) { - if (fs_prefix) - free (fs_prefix); fs_prefix = xstrdup ("/"); + if (grub_find_root_btrfs_mount_path_hook) + grub_find_root_btrfs_mount_path_hook (entries[i].enc_path); + } + else + { + fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path); } } else if (!retry && grub_strcmp (entries[i].fstype, "autofs") == 0) @@ -1150,6 +1264,34 @@ grub_util_get_grub_dev_os (const char *os_dev) return grub_dev; } + +char * +grub_util_get_btrfs_subvol (const char *path, char **mount_path) +{ + char *mp = NULL; + + if (mount_path) + *mount_path = NULL; + + auto void + mount_path_hook (const char *m) + { + mp = strdup (m); + } + + grub_find_root_btrfs_mount_path_hook = mount_path_hook; + grub_free (grub_find_root_devices_from_mountinfo (path, NULL)); + grub_find_root_btrfs_mount_path_hook = NULL; + + if (!mp) + return NULL; + + if (mount_path) + *mount_path = mp; + + return get_btrfs_subvol (mp); +} + char * grub_make_system_path_relative_to_its_root_os (const char *path) { diff --git a/include/grub/emu/getroot.h b/include/grub/emu/getroot.h index 73fa2d34ab..9c642ae3fe 100644 --- a/include/grub/emu/getroot.h +++ b/include/grub/emu/getroot.h @@ -53,6 +53,11 @@ char ** grub_find_root_devices_from_mountinfo (const char *dir, char **relroot); #endif +#ifdef __linux__ +char * +grub_util_get_btrfs_subvol (const char *path, char **mount_path); +#endif + /* Devmapper functions provided by getroot_devmapper.c. */ void grub_util_pull_devmapper (const char *os_dev); diff --git a/util/grub-install.c b/util/grub-install.c index 0f66f36d23..84ed6e88ec 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -1569,6 +1569,55 @@ main (int argc, char *argv[]) prefix_drive = xasprintf ("(%s)", grub_drives[0]); } +#ifdef __linux__ + + if (config.is_suse_btrfs_snapshot_enabled + && grub_strncmp(grub_fs->name, "btrfs", sizeof ("btrfs") - 1) == 0) + { + char *subvol = NULL; + char *mount_path = NULL; + char **rootdir_devices = NULL; + char *rootdir_path = grub_util_path_concat (2, "/", rootdir); + + if (grub_util_is_directory (rootdir_path)) + rootdir_devices = grub_guess_root_devices (rootdir_path); + + free (rootdir_path); + + if (rootdir_devices && rootdir_devices[0]) + if (grub_strcmp (rootdir_devices[0], grub_devices[0]) == 0) + subvol = grub_util_get_btrfs_subvol (platdir, &mount_path); + + if (subvol && mount_path) + { + char *def_subvol; + + def_subvol = grub_util_get_btrfs_subvol ("/", NULL); + + if (def_subvol) + { + if (!load_cfg_f) + load_cfg_f = grub_util_fopen (load_cfg, "wb"); + have_load_cfg = 1; + + if (grub_strcmp (subvol, def_subvol) != 0) + fprintf (load_cfg_f, "btrfs-mount-subvol ($root) %s %s\n", mount_path, subvol); + free (def_subvol); + } + } + + for (curdev = rootdir_devices; *curdev; curdev++) + free (*curdev); + if (rootdir_devices) + free (rootdir_devices); + if (subvol) + free (subvol); + if (mount_path) + free (mount_path); + } + +#endif + char mkimage_target[200]; const char *core_name = NULL; From 207b99010b2c5d8fc4e480b1b8a7fc157b77ddc7 Mon Sep 17 00:00:00 2001 From: Andrei Borzenkov Date: Tue, 21 Jun 2016 16:44:17 +0000 Subject: [PATCH 047/367] Fallback to old subvol name scheme to support old snapshot config Ref: bsc#953538 --- grub-core/fs/btrfs.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index d323746ecf..673ded0352 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -1260,11 +1260,41 @@ lookup_root_by_name(struct grub_btrfs_data *data, const char *path) return GRUB_ERR_NONE; } +static grub_err_t +lookup_root_by_name_fallback(struct grub_btrfs_data *data, const char *path) +{ + grub_err_t err; + grub_uint64_t tree = 0; + grub_uint8_t type; + struct grub_btrfs_key key; + + err = find_path (data, path, &key, &tree, &type); + if (err) + return grub_error(GRUB_ERR_FILE_NOT_FOUND, "couldn't locate %s\n", path); + + if (key.object_id != grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0) + return grub_error(GRUB_ERR_BAD_FILE_TYPE, "%s: not a subvolume\n", path); + + data->fs_tree = tree; + return GRUB_ERR_NONE; +} + static grub_err_t btrfs_handle_subvol(struct grub_btrfs_data *data __attribute__ ((unused))) { if (btrfs_default_subvol) - return lookup_root_by_name(data, btrfs_default_subvol); + { + grub_err_t err; + err = lookup_root_by_name(data, btrfs_default_subvol); + + /* Fallback to old schemes */ + if (err == GRUB_ERR_FILE_NOT_FOUND) + { + err = GRUB_ERR_NONE; + return lookup_root_by_name_fallback(data, btrfs_default_subvol); + } + return err; + } if (btrfs_default_subvolid) return lookup_root_by_id(data, btrfs_default_subvolid); From 0ef7cf5e8e912a8413380f85b129ef6f96f9f373 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Thu, 11 May 2017 08:56:57 +0000 Subject: [PATCH 048/367] Grub not working correctly with btrfs snapshots (bsc#1026511) Signed-off-by: Michael Chang Signed-off-by: Robbie Harwood --- grub-core/fs/btrfs.c | 238 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 238 insertions(+) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 673ded0352..2b21cbaa67 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -2887,6 +2887,238 @@ grub_cmd_btrfs_list_subvols (struct grub_extcmd_context *ctxt, return 0; } +static grub_err_t +grub_btrfs_get_parent_subvol_path (struct grub_btrfs_data *data, + grub_uint64_t child_id, + const char *child_path, + grub_uint64_t *parent_id, + char **path_out) +{ + grub_uint64_t fs_root = 0; + struct grub_btrfs_key key_in = { + .object_id = child_id, + .type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF, + .offset = 0, + }, key_out; + struct grub_btrfs_root_ref *ref; + char *buf; + struct grub_btrfs_leaf_descriptor desc; + grub_size_t elemsize; + grub_disk_addr_t elemaddr; + grub_err_t err; + char *parent_path; + + *parent_id = 0; + *path_out = 0; + + err = lower_bound(data, &key_in, &key_out, data->sblock.root_tree, + &elemaddr, &elemsize, &desc, 0); + if (err) + return err; + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF || elemaddr == 0) + next(data, &desc, &elemaddr, &elemsize, &key_out); + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF) + { + free_iterator(&desc); + return grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("can't find root backrefs")); + } + + buf = grub_malloc(elemsize + 1); + if (!buf) + { + free_iterator(&desc); + return grub_errno; + } + + err = grub_btrfs_read_logical(data, elemaddr, buf, elemsize, 0); + if (err) + { + grub_free(buf); + free_iterator(&desc); + return err; + } + + buf[elemsize] = 0; + ref = (struct grub_btrfs_root_ref *)buf; + + err = get_fs_root(data, data->sblock.root_tree, grub_le_to_cpu64 (key_out.offset), + 0, &fs_root); + if (err) + { + grub_free(buf); + free_iterator(&desc); + return err; + } + + find_pathname(data, grub_le_to_cpu64 (ref->dirid), fs_root, ref->name, &parent_path); + + if (child_path) + { + *path_out = grub_xasprintf ("%s/%s", parent_path, child_path); + grub_free (parent_path); + } + else + *path_out = parent_path; + + *parent_id = grub_le_to_cpu64 (key_out.offset); + + grub_free(buf); + free_iterator(&desc); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_btrfs_get_default_subvolume_id (struct grub_btrfs_data *data, grub_uint64_t *id) +{ + grub_err_t err; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + struct grub_btrfs_key key, key_out; + struct grub_btrfs_dir_item *direl = NULL; + const char *ctoken = "default"; + grub_size_t ctokenlen = sizeof ("default") - 1; + + *id = 0; + key.object_id = data->sblock.root_dir_objectid; + key.type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key.offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen)); + err = lower_bound (data, &key, &key_out, data->sblock.root_tree, &elemaddr, &elemsize, + NULL, 0); + if (err) + return err; + + if (key_cmp (&key, &key_out) != 0) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found")); + + struct grub_btrfs_dir_item *cdirel; + direl = grub_malloc (elemsize + 1); + err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize, 0); + if (err) + { + grub_free (direl); + return err; + } + for (cdirel = direl; + (grub_uint8_t *) cdirel - (grub_uint8_t *) direl + < (grub_ssize_t) elemsize; + cdirel = (void *) ((grub_uint8_t *) (direl + 1) + + grub_le_to_cpu16 (cdirel->n) + + grub_le_to_cpu16 (cdirel->m))) + { + if (ctokenlen == grub_le_to_cpu16 (cdirel->n) + && grub_memcmp (cdirel->name, ctoken, ctokenlen) == 0) + break; + } + if ((grub_uint8_t *) cdirel - (grub_uint8_t *) direl + >= (grub_ssize_t) elemsize) + { + grub_free (direl); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found")); + return err; + } + + if (cdirel->key.type != GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM) + { + grub_free (direl); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found")); + return err; + } + + *id = grub_le_to_cpu64 (cdirel->key.object_id); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_btrfs_get_default_subvol (struct grub_extcmd_context *ctxt, + int argc, char **argv) +{ + char *devname; + grub_device_t dev; + struct grub_btrfs_data *data; + grub_err_t err; + grub_uint64_t id; + char *subvol = NULL; + grub_uint64_t subvolid = 0; + char *varname = NULL; + char *output = NULL; + int path_only = ctxt->state[1].set; + int num_only = ctxt->state[2].set; + + if (ctxt->state[0].set) + varname = ctxt->state[0].arg; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + devname = grub_file_get_device_name(argv[0]); + if (!devname) + return grub_errno; + + dev = grub_device_open (devname); + grub_free (devname); + if (!dev) + return grub_errno; + + data = grub_btrfs_mount(dev); + if (!data) + { + grub_device_close (dev); + grub_dprintf ("btrfs", "failed to open fs\n"); + grub_errno = GRUB_ERR_NONE; + return 0; + } + + err = grub_btrfs_get_default_subvolume_id (data, &subvolid); + if (err) + { + grub_btrfs_unmount (data); + grub_device_close (dev); + return err; + } + + id = subvolid; + while (id != GRUB_BTRFS_ROOT_VOL_OBJECTID) + { + grub_uint64_t parent_id; + char *path_out; + + err = grub_btrfs_get_parent_subvol_path (data, grub_cpu_to_le64 (id), subvol, &parent_id, &path_out); + if (err) + { + grub_btrfs_unmount (data); + grub_device_close (dev); + return err; + } + + if (subvol) + grub_free (subvol); + subvol = path_out; + id = parent_id; + } + + if (num_only && path_only) + output = grub_xasprintf ("%"PRIuGRUB_UINT64_T" /%s", subvolid, subvol); + else if (num_only) + output = grub_xasprintf ("%"PRIuGRUB_UINT64_T, subvolid); + else + output = grub_xasprintf ("/%s", subvol); + + if (varname) + grub_env_set(varname, output); + else + grub_printf ("%s\n", output); + + grub_free (output); + grub_free (subvol); + + grub_btrfs_unmount (data); + grub_device_close (dev); + + return GRUB_ERR_NONE; +} + static struct grub_fs grub_btrfs_fs = { .name = "btrfs", .fs_dir = grub_btrfs_dir, @@ -2905,6 +3137,7 @@ static struct grub_fs grub_btrfs_fs = { static grub_command_t cmd_info; static grub_command_t cmd_mount_subvol; static grub_extcmd_t cmd_list_subvols; +static grub_extcmd_t cmd_get_default_subvol; static char * subvolid_set_env (struct grub_env_var *var __attribute__ ((unused)), @@ -2975,6 +3208,11 @@ GRUB_MOD_INIT (btrfs) "[-p|-n] [-o var] DEVICE", "Print list of BtrFS subvolumes on " "DEVICE.", options); + cmd_get_default_subvol = grub_register_extcmd("btrfs-get-default-subvol", + grub_cmd_btrfs_get_default_subvol, 0, + "[-p|-n] [-o var] DEVICE", + "Print default BtrFS subvolume on " + "DEVICE.", options); grub_register_variable_hook ("btrfs_subvol", subvol_get_env, subvol_set_env); grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env, From 05d39b8cd67c68502ed1daae6e61b8211c973205 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 1 Jun 2017 09:59:56 -0400 Subject: [PATCH 049/367] Add grub_efi_allocate_pool() and grub_efi_free_pool() wrappers. Signed-off-by: Peter Jones --- include/grub/efi/efi.h | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index 585fa6662b..03f9a9d011 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -24,6 +24,10 @@ #include #include +/* Variables. */ +extern grub_efi_system_table_t *EXPORT_VAR(grub_efi_system_table); +extern grub_efi_handle_t EXPORT_VAR(grub_efi_image_handle); + /* Functions. */ void *EXPORT_FUNC(grub_efi_locate_protocol) (grub_efi_guid_t *protocol, void *registration); @@ -60,6 +64,33 @@ EXPORT_FUNC(grub_efi_get_memory_map) (grub_efi_uintn_t *memory_map_size, grub_efi_uintn_t *descriptor_size, grub_efi_uint32_t *descriptor_version); void grub_efi_memory_fini (void); + +static inline grub_efi_status_t +__attribute__((__unused__)) +grub_efi_allocate_pool (grub_efi_memory_type_t pool_type, + grub_efi_uintn_t buffer_size, + void **buffer) +{ + grub_efi_boot_services_t *b; + grub_efi_status_t status; + + b = grub_efi_system_table->boot_services; + status = efi_call_3 (b->allocate_pool, pool_type, buffer_size, buffer); + return status; +} + +static inline grub_efi_status_t +__attribute__((__unused__)) +grub_efi_free_pool (void *buffer) +{ + grub_efi_boot_services_t *b; + grub_efi_status_t status; + + b = grub_efi_system_table->boot_services; + status = efi_call_1 (b->free_pool, buffer); + return status; +} + grub_efi_loaded_image_t *EXPORT_FUNC(grub_efi_get_loaded_image) (grub_efi_handle_t image_handle); void EXPORT_FUNC(grub_efi_print_device_path) (grub_efi_device_path_t *dp); char *EXPORT_FUNC(grub_efi_get_filename) (grub_efi_device_path_t *dp); @@ -115,10 +146,7 @@ void grub_efi_init (void); void grub_efi_fini (void); void grub_efi_set_prefix (void); -/* Variables. */ -extern grub_efi_system_table_t *EXPORT_VAR(grub_efi_system_table); -extern grub_efi_handle_t EXPORT_VAR(grub_efi_image_handle); - +/* More variables. */ extern int EXPORT_VAR(grub_efi_is_finished); struct grub_net_card; From 2ffcce335e221d56aece250bf36e2c1ef93ba0cc Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 1 Jun 2017 10:06:38 -0400 Subject: [PATCH 050/367] Use grub_efi_...() memory helpers where reasonable. This uses grub_efi_allocate_pool(), grub_efi_free_pool(), and grub_efi_free_pages() instead of open-coded efi_call_N() calls, so we get more reasonable type checking. Signed-off-by: Peter Jones --- grub-core/loader/efi/chainloader.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index 07c4937898..89ac84cc66 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -65,7 +65,7 @@ grub_chainloader_unload (void) b = grub_efi_system_table->boot_services; efi_call_1 (b->unload_image, image_handle); - efi_call_2 (b->free_pages, address, pages); + grub_efi_free_pages (address, pages); grub_free (file_path); grub_free (cmdline); @@ -108,7 +108,7 @@ grub_chainloader_boot (void) } if (exit_data) - efi_call_1 (b->free_pool, exit_data); + grub_efi_free_pool (exit_data); grub_loader_unset (); @@ -527,10 +527,9 @@ grub_efi_get_media_file_path (grub_efi_device_path_t *dp) static grub_efi_boolean_t handle_image (void *data, grub_efi_uint32_t datasize) { - grub_efi_boot_services_t *b; grub_efi_loaded_image_t *li, li_bak; grub_efi_status_t efi_status; - char *buffer = NULL; + void *buffer = NULL; char *buffer_aligned = NULL; grub_efi_uint32_t i; struct grub_pe32_section_table *section; @@ -541,8 +540,6 @@ handle_image (void *data, grub_efi_uint32_t datasize) int found_entry_point = 0; int rc; - b = grub_efi_system_table->boot_services; - rc = read_header (data, datasize, &context); if (rc < 0) { @@ -582,8 +579,8 @@ handle_image (void *data, grub_efi_uint32_t datasize) grub_dprintf ("chain", "image size is %08"PRIxGRUB_UINT64_T", datasize is %08x\n", context.image_size, datasize); - efi_status = efi_call_3 (b->allocate_pool, GRUB_EFI_LOADER_DATA, - buffer_size, &buffer); + efi_status = grub_efi_allocate_pool (GRUB_EFI_LOADER_DATA, buffer_size, + &buffer); if (efi_status != GRUB_EFI_SUCCESS) { @@ -815,14 +812,14 @@ handle_image (void *data, grub_efi_uint32_t datasize) grub_dprintf ("chain", "entry_point returned %ld\n", efi_status); grub_memcpy (li, &li_bak, sizeof (grub_efi_loaded_image_t)); - efi_status = efi_call_1 (b->free_pool, buffer); + efi_status = grub_efi_free_pool (buffer); return 1; error_exit: grub_dprintf ("chain", "error_exit: grub_errno: %d\n", grub_errno); if (buffer) - efi_call_1 (b->free_pool, buffer); + grub_efi_free_pool (buffer); return 0; } @@ -830,10 +827,7 @@ handle_image (void *data, grub_efi_uint32_t datasize) static grub_err_t grub_secureboot_chainloader_unload (void) { - grub_efi_boot_services_t *b; - - b = grub_efi_system_table->boot_services; - efi_call_2 (b->free_pages, address, pages); + grub_efi_free_pages (address, pages); grub_free (file_path); grub_free (cmdline); cmdline = 0; @@ -1100,7 +1094,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_free (file_path); if (address) - efi_call_2 (b->free_pages, address, pages); + grub_efi_free_pages (address, pages); if (cmdline) grub_free (cmdline); From adbe7126c7c57f4d62e065961a105673ddd4ee14 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 1 Jun 2017 10:07:50 -0400 Subject: [PATCH 051/367] Add PRIxGRUB_EFI_STATUS and use it. This avoids syntax checkers getting confused about if it's llx or lx. Signed-off-by: Peter Jones --- grub-core/loader/efi/chainloader.c | 3 ++- include/grub/efi/api.h | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index 89ac84cc66..ac8dfd40c6 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -810,7 +810,8 @@ handle_image (void *data, grub_efi_uint32_t datasize) efi_status = efi_call_2 (entry_point, grub_efi_image_handle, grub_efi_system_table); - grub_dprintf ("chain", "entry_point returned %ld\n", efi_status); + grub_dprintf ("chain", "entry_point returned 0x%"PRIxGRUB_EFI_STATUS"\n", + efi_status); grub_memcpy (li, &li_bak, sizeof (grub_efi_loaded_image_t)); efi_status = grub_efi_free_pool (buffer); diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index 117469450d..9962880147 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -546,7 +546,16 @@ typedef grub_uint64_t grub_efi_uint64_t; typedef grub_uint8_t grub_efi_char8_t; typedef grub_uint16_t grub_efi_char16_t; + typedef grub_efi_uintn_t grub_efi_status_t; +/* Make grub_efi_status_t reasonably printable. */ +#if GRUB_CPU_SIZEOF_VOID_P == 8 +#define PRIxGRUB_EFI_STATUS "lx" +#define PRIdGRUB_EFI_STATUS "ld" +#else +#define PRIxGRUB_EFI_STATUS "llx" +#define PRIdGRUB_EFI_STATUS "lld" +#endif #define GRUB_EFI_ERROR_CODE(value) \ ((((grub_efi_status_t) 1) << (sizeof (grub_efi_status_t) * 8 - 1)) | (value)) From b224eae7c8b6ac45a95de7e4c40233f0aefb287e Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 26 Jun 2017 12:44:59 -0400 Subject: [PATCH 052/367] don't use int for efi status --- grub-core/kern/efi/efi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index 05d8237a9b..ae9885edb8 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -167,7 +167,7 @@ grub_reboot (void) void grub_exit (int retval) { - int rc = GRUB_EFI_LOAD_ERROR; + grub_efi_status_t rc = GRUB_EFI_LOAD_ERROR; if (retval == 0) rc = GRUB_EFI_SUCCESS; From 8cbd2ce2df22bb15cb4d0a0519c044599f9311ae Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 26 Jun 2017 12:46:23 -0400 Subject: [PATCH 053/367] make GRUB_MOD_INIT() declare its function prototypes. --- include/grub/dl.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/grub/dl.h b/include/grub/dl.h index b3753c9ca2..91933b85f2 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -54,6 +54,7 @@ grub_mod_fini (void) #define GRUB_MOD_INIT(name) \ static void grub_mod_init (grub_dl_t mod __attribute__ ((unused))) __attribute__ ((used)); \ +extern void grub_##name##_init (void); \ void \ grub_##name##_init (void) { grub_mod_init (0); } \ static void \ @@ -61,6 +62,7 @@ grub_mod_init (grub_dl_t mod __attribute__ ((unused))) #define GRUB_MOD_FINI(name) \ static void grub_mod_fini (void) __attribute__ ((used)); \ +extern void grub_##name##_fini (void); \ void \ grub_##name##_fini (void) { grub_mod_fini (); } \ static void \ From d7e5e309d5a1fbe138958604724baa6e5cf3f13d Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 20 Apr 2017 13:29:06 -0400 Subject: [PATCH 054/367] Don't guess /boot/efi/ as HFS+ on ppc machines in grub-install This should never be trying this, and since we've consolidated the grubenv to always be on /boot/efi/EFI/fedora/, this code causes it to always make the wrong decision. Resolves: rhbz#1484474 Signed-off-by: Peter Jones --- util/grub-install.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/util/grub-install.c b/util/grub-install.c index 84ed6e88ec..a2bec7446c 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -1190,18 +1190,8 @@ main (int argc, char *argv[]) char *d; is_guess = 1; - d = grub_util_path_concat (2, bootdir, "macppc"); - if (!grub_util_is_directory (d)) - { - free (d); - d = grub_util_path_concat (2, bootdir, "efi"); - } /* Find the Mac HFS(+) System Partition. */ - if (!grub_util_is_directory (d)) - { - free (d); - d = grub_util_path_concat (2, bootdir, "EFI"); - } + d = grub_util_path_concat (2, bootdir, "macppc"); if (!grub_util_is_directory (d)) { free (d); From 03f9153f84ab216a30bea297f7f2ad547369ea2f Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 9 Jul 2019 14:31:19 +0200 Subject: [PATCH 055/367] 20_linux_xen: load xen or multiboot{,2} modules as needed. Signed-off-by: Peter Jones --- util/grub.d/20_linux_xen.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/util/grub.d/20_linux_xen.in b/util/grub.d/20_linux_xen.in index e9e73b815f..c23b064be6 100644 --- a/util/grub.d/20_linux_xen.in +++ b/util/grub.d/20_linux_xen.in @@ -153,6 +153,7 @@ linux_entry_xsm () else xen_rm_opts="no-real-mode edd=off" fi + insmod ${xen_module} ${xen_loader} ${rel_xen_dirname}/${xen_basename} placeholder ${xen_args} \${xen_rm_opts} echo '$(echo "$lmessage" | grub_quote)' ${module_loader} ${rel_dirname}/${basename} placeholder root=${linux_root_device_thisversion} ro ${args} @@ -166,6 +167,7 @@ EOF done sed "s/^/$submenu_indentation/" << EOF echo '$(echo "$message" | grub_quote)' + insmod ${xen_module} ${module_loader} --nounzip $(echo $initrd_path) EOF fi @@ -253,13 +255,16 @@ while [ "x${xen_list}" != "x" ] ; do echo " submenu '$(gettext_printf "Xen hypervisor, version %s" "${xen_version}" | grub_quote)' \$menuentry_id_option 'xen-hypervisor-$xen_version-$boot_device_id' {" fi if ($grub_file --is-arm64-efi $current_xen); then + xen_module="xen_boot" xen_loader="xen_hypervisor" module_loader="xen_module" else if ($grub_file --is-x86-multiboot2 $current_xen); then + xen_module="multiboot2" xen_loader="multiboot2" module_loader="module2" else + xen_module="multiboot" xen_loader="multiboot" module_loader="module" fi From 89bd9a8c46a240bfc822969dfcb7daa6b6262f87 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 7 Nov 2017 17:12:17 -0500 Subject: [PATCH 056/367] Make pmtimer tsc calibration not take 51 seconds to fail. On my laptop running at 2.4GHz, if I run a VM where tsc calibration using pmtimer will fail presuming a broken pmtimer, it takes ~51 seconds to do so (as measured with the stopwatch on my phone), with a tsc delta of 0x1cd1c85300, or around 125 billion cycles. If instead of trying to wait for 5-200ms to show up on the pmtimer, we try to wait for 5-200us, it decides it's broken in ~0x2626aa0 TSCs, aka ~2.4 million cycles, or more or less instantly. Additionally, this reading the pmtimer was returning 0xffffffff anyway, and that's obviously an invalid return. I've added a check for that and 0 so we don't bother waiting for the test if what we're seeing is dead pins with no response at all. If "debug" is includes "pmtimer", you will see one of the following three outcomes. If pmtimer gives all 0 or all 1 bits, you will see: kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 1 kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 2 kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 3 kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 4 kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 5 kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 6 kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 7 kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 8 kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 9 kern/i386/tsc_pmtimer.c:77: pmtimer: 0xffffff bad_reads: 10 kern/i386/tsc_pmtimer.c:78: timer is broken; giving up. This outcome was tested using qemu+kvm with UEFI (OVMF) firmware and these options: -machine pc-q35-2.10 -cpu Broadwell-noTSX If pmtimer gives any other bit patterns but is not actually marching forward fast enough to use for clock calibration, you will see: kern/i386/tsc_pmtimer.c:121: pmtimer delta is 0x0 (1904 iterations) kern/i386/tsc_pmtimer.c:124: tsc delta is implausible: 0x2626aa0 This outcome was tested using grub compiled with GRUB_PMTIMER_IGNORE_BAD_READS defined (so as not to trip the bad read test) using qemu+kvm with UEFI (OVMF) firmware, and these options: -machine pc-q35-2.10 -cpu Broadwell-noTSX If pmtimer actually works, you'll see something like: kern/i386/tsc_pmtimer.c:121: pmtimer delta is 0x0 (1904 iterations) kern/i386/tsc_pmtimer.c:124: tsc delta is implausible: 0x2626aa0 This outcome was tested using qemu+kvm with UEFI (OVMF) firmware, and these options: -machine pc-i440fx-2.4 -cpu Broadwell-noTSX I've also tested this outcome on a real Intel Xeon E3-1275v3 on an Intel Server Board S1200V3RPS using the SDV.RP.B8 "Release" build here: https://firmware.intel.com/sites/default/files/UEFIDevKit_S1200RP_vB8.zip Signed-off-by: Peter Jones --- grub-core/kern/i386/tsc_pmtimer.c | 109 ++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 20 deletions(-) diff --git a/grub-core/kern/i386/tsc_pmtimer.c b/grub-core/kern/i386/tsc_pmtimer.c index c9c3616997..ca15c3aacd 100644 --- a/grub-core/kern/i386/tsc_pmtimer.c +++ b/grub-core/kern/i386/tsc_pmtimer.c @@ -28,40 +28,101 @@ #include #include +/* + * Define GRUB_PMTIMER_IGNORE_BAD_READS if you're trying to test a timer that's + * present but doesn't keep time well. + */ +// #define GRUB_PMTIMER_IGNORE_BAD_READS + grub_uint64_t grub_pmtimer_wait_count_tsc (grub_port_t pmtimer, grub_uint16_t num_pm_ticks) { grub_uint32_t start; - grub_uint32_t last; - grub_uint32_t cur, end; + grub_uint64_t cur, end; grub_uint64_t start_tsc; grub_uint64_t end_tsc; - int num_iter = 0; + unsigned int num_iter = 0; +#ifndef GRUB_PMTIMER_IGNORE_BAD_READS + int bad_reads = 0; +#endif - start = grub_inl (pmtimer) & 0xffffff; - last = start; + /* + * Some timers are 24-bit and some are 32-bit, but it doesn't make much + * difference to us. Caring which one we have isn't really worth it since + * the low-order digits will give us enough data to calibrate TSC. So just + * mask the top-order byte off. + */ + cur = start = grub_inl (pmtimer) & 0xffffffUL; end = start + num_pm_ticks; start_tsc = grub_get_tsc (); while (1) { - cur = grub_inl (pmtimer) & 0xffffff; - if (cur < last) - cur |= 0x1000000; - num_iter++; + cur &= 0xffffffffff000000ULL; + cur |= grub_inl (pmtimer) & 0xffffffUL; + + end_tsc = grub_get_tsc(); + +#ifndef GRUB_PMTIMER_IGNORE_BAD_READS + /* + * If we get 10 reads in a row that are obviously dead pins, there's no + * reason to do this thousands of times. + */ + if (cur == 0xffffffUL || cur == 0) + { + bad_reads++; + grub_dprintf ("pmtimer", + "pmtimer: 0x%"PRIxGRUB_UINT64_T" bad_reads: %d\n", + cur, bad_reads); + grub_dprintf ("pmtimer", "timer is broken; giving up.\n"); + + if (bad_reads == 10) + return 0; + } +#endif + + if (cur < start) + cur += 0x1000000; + if (cur >= end) { - end_tsc = grub_get_tsc (); + grub_dprintf ("pmtimer", "pmtimer delta is 0x%"PRIxGRUB_UINT64_T"\n", + cur - start); + grub_dprintf ("pmtimer", "tsc delta is 0x%"PRIxGRUB_UINT64_T"\n", + end_tsc - start_tsc); return end_tsc - start_tsc; } - /* Check for broken PM timer. - 50000000 TSCs is between 5 ms (10GHz) and 200 ms (250 MHz) - if after this time we still don't have 1 ms on pmtimer, then - pmtimer is broken. + + /* + * Check for broken PM timer. 1ms at 10GHz should be 1E+7 TSCs; at + * 250MHz it should be 2.5E6. So if after 4E+7 TSCs on a 10GHz machine, + * we should have seen pmtimer show 4ms of change (i.e. cur =~ + * start+14320); on a 250MHz machine that should be 16ms (start+57280). + * If after this a time we still don't have 1ms on pmtimer, then pmtimer + * is broken. + * + * Likewise, if our code is perfectly efficient and introduces no delays + * whatsoever, on a 10GHz system we should see a TSC delta of 3580 in + * ~3580 iterations. On a 250MHz machine that should be ~900 iterations. + * + * With those factors in mind, there are two limits here. There's a hard + * limit here at 8x our desired pm timer delta, picked as an arbitrarily + * large value that's still not a lot of time to humans, because if we + * get that far this is either an implausibly fast machine or the pmtimer + * is not running. And there's another limit on 4x our 10GHz tsc delta + * without seeing cur converge on our target value. */ - if ((num_iter & 0xffffff) == 0 && grub_get_tsc () - start_tsc > 5000000) { - return 0; - } + if ((++num_iter > (grub_uint32_t)num_pm_ticks << 3UL) || + end_tsc - start_tsc > 40000000) + { + grub_dprintf ("pmtimer", + "pmtimer delta is 0x%"PRIxGRUB_UINT64_T" (%u iterations)\n", + cur - start, num_iter); + grub_dprintf ("pmtimer", + "tsc delta is implausible: 0x%"PRIxGRUB_UINT64_T"\n", + end_tsc - start_tsc); + return 0; + } } } @@ -74,12 +135,20 @@ grub_tsc_calibrate_from_pmtimer (void) fadt = grub_acpi_find_fadt (); if (!fadt) - return 0; + { + grub_dprintf ("pmtimer", "No FADT found; not using pmtimer.\n"); + return 0; + } pmtimer = fadt->pmtimer; if (!pmtimer) - return 0; + { + grub_dprintf ("pmtimer", "FADT does not specify pmtimer; skipping.\n"); + return 0; + } - /* It's 3.579545 MHz clock. Wait 1 ms. */ + /* + * It's 3.579545 MHz clock. Wait 1 ms. + */ tsc_diff = grub_pmtimer_wait_count_tsc (pmtimer, 3580); if (tsc_diff == 0) return 0; From 84bb03823171c21d238bdaff7635781c8bf723d4 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 27 Feb 2018 13:55:35 -0500 Subject: [PATCH 057/367] align struct efi_variable better... --- include/grub/efiemu/runtime.h | 2 +- include/grub/types.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/grub/efiemu/runtime.h b/include/grub/efiemu/runtime.h index 36d2dedf47..9d93ba88ba 100644 --- a/include/grub/efiemu/runtime.h +++ b/include/grub/efiemu/runtime.h @@ -33,5 +33,5 @@ struct efi_variable grub_uint32_t namelen; grub_uint32_t size; grub_efi_uint32_t attributes; -} GRUB_PACKED; +} GRUB_PACKED GRUB_ALIGNED(8); #endif /* ! GRUB_EFI_EMU_RUNTIME_HEADER */ diff --git a/include/grub/types.h b/include/grub/types.h index 0a3ff15913..ba446d9904 100644 --- a/include/grub/types.h +++ b/include/grub/types.h @@ -29,6 +29,7 @@ #else #define GRUB_PACKED __attribute__ ((packed)) #endif +#define GRUB_ALIGNED(x) __attribute__((aligned (x))) #ifdef GRUB_BUILD # define GRUB_CPU_SIZEOF_VOID_P BUILD_SIZEOF_VOID_P From 7080964abc67dc972618165f4b3e62d7bec4f473 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 9 Dec 2016 15:40:29 -0500 Subject: [PATCH 058/367] Add BLS support to grub-mkconfig GRUB now has BootLoaderSpec support, the user can choose to use this by setting GRUB_ENABLE_BLSCFG to true in /etc/default/grub. On this setup, the boot menu entries are not added to the grub.cfg, instead BLS config files are parsed by blscfg command and the entries created dynamically. A 10_linux_bls grub.d snippet to generate menu entries from BLS files is also added that can be used on platforms where the bootloader doesn't have BLS support and only can parse a normal grub configuration file. Portions of the 10_linux_bls were taken from the ostree-grub-generator script that's included in the OSTree project. Fixes to support multi-devices and generate a BLS section even if no kernels are found in the boot directory were proposed by Yclept Nemo and Tom Gundersen respectively. Signed-off-by: Peter Jones [javierm: remove outdated URL for BLS document] Signed-off-by: Javier Martinez Canillas [iwienand@redhat.com: skip machine ID check when updating entries] Signed-off-by: Ian Wienand [rharwood: use sort(1), commit message composits, drop man pages] Signed-off-by: Robbie Harwood --- util/grub-mkconfig.in | 9 +- util/grub-mkconfig_lib.in | 22 +++- util/grub.d/10_linux.in | 218 +++++++++++++++++++++++++++++++++++++- 3 files changed, 243 insertions(+), 6 deletions(-) diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index 535c0f0249..f55339a3f6 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -50,6 +50,8 @@ grub_get_kernel_settings="${sbindir}/@grub_get_kernel_settings@" export TEXTDOMAIN=@PACKAGE@ export TEXTDOMAINDIR="@localedir@" +export GRUB_GRUBENV_UPDATE="yes" + . "${pkgdatadir}/grub-mkconfig_lib" # Usage: usage @@ -59,6 +61,7 @@ usage () { gettext "Generate a grub config file"; echo echo print_option_help "-o, --output=$(gettext FILE)" "$(gettext "output generated config to FILE [default=stdout]")" + print_option_help "--no-grubenv-update" "$(gettext "do not update variables in the grubenv file")" print_option_help "-h, --help" "$(gettext "print this message and exit")" print_option_help "-V, --version" "$(gettext "print the version information and exit")" echo @@ -94,6 +97,9 @@ do --output=*) grub_cfg=`echo "$option" | sed 's/--output=//'` ;; + --no-grubenv-update) + GRUB_GRUBENV_UPDATE="no" + ;; -*) gettext_printf "Unrecognized option \`%s'\n" "$option" 1>&2 usage @@ -253,7 +259,8 @@ export GRUB_DEFAULT \ GRUB_OS_PROBER_SKIP_LIST \ GRUB_DISABLE_SUBMENU \ GRUB_DEFAULT_DTB \ - SUSE_BTRFS_SNAPSHOT_BOOTING + SUSE_BTRFS_SNAPSHOT_BOOTING \ + GRUB_ENABLE_BLSCFG if test "x${grub_cfg}" != "x"; then rm -f "${grub_cfg}.new" diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in index 5e96f6cc5d..301d8a8a1e 100644 --- a/util/grub-mkconfig_lib.in +++ b/util/grub-mkconfig_lib.in @@ -30,6 +30,9 @@ fi if test "x$grub_file" = x; then grub_file="${bindir}/@grub_file@" fi +if test "x$grub_editenv" = x; then + grub_editenv="${bindir}/@grub_editenv@" +fi if test "x$grub_mkrelpath" = x; then grub_mkrelpath="${bindir}/@grub_mkrelpath@" fi @@ -122,8 +125,19 @@ EOF fi } +prepare_grub_to_access_device_with_variable () +{ + device_variable="$1" + shift + prepare_grub_to_access_device "$@" + unset "device_variable" +} + prepare_grub_to_access_device () { + if [ -z "$device_variable" ]; then + device_variable="root" + fi old_ifs="$IFS" IFS=' ' @@ -158,18 +172,18 @@ prepare_grub_to_access_device () # otherwise set root as per value in device.map. fs_hint="`"${grub_probe}" --device $@ --target=compatibility_hint`" if [ "x$fs_hint" != x ]; then - echo "set root='$fs_hint'" + echo "set ${device_variable}='$fs_hint'" fi if [ "x${GRUB_DISABLE_UUID}" != "xtrue" ] && fs_uuid="`"${grub_probe}" --device $@ --target=fs_uuid 2> /dev/null`" ; then hints="`"${grub_probe}" --device $@ --target=hints_string 2> /dev/null`" || hints= if [ "x$hints" != x ]; then echo "if [ x\$feature_platform_search_hint = xy ]; then" - echo " search --no-floppy --fs-uuid --set=root ${hints} ${fs_uuid}" + echo " search --no-floppy --fs-uuid --set=${device_variable} ${hints} ${fs_uuid}" echo "else" - echo " search --no-floppy --fs-uuid --set=root ${fs_uuid}" + echo " search --no-floppy --fs-uuid --set=${device_variable} ${fs_uuid}" echo "fi" else - echo "search --no-floppy --fs-uuid --set=root ${fs_uuid}" + echo "search --no-floppy --fs-uuid --set=${device_variable} ${fs_uuid}" fi fi IFS="$old_ifs" diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 7bb3a211a7..2851952659 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -82,6 +82,218 @@ case x"$GRUB_FS" in ;; esac +populate_header_warn() +{ +if [ "x${BLS_POPULATE_MENU}" = "xtrue" ]; then + bls_parser="10_linux script" +else + bls_parser="blscfg command" +fi +cat </dev/null)) || : + + echo "${files[@]}" +} + +update_bls_cmdline() +{ + local cmdline="root=${LINUX_ROOT_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" + local -a files=($(get_sorted_bls)) + + for bls in "${files[@]}"; do + local options="${cmdline}" + if [ -z "${bls##*debug*}" ]; then + options="${options} ${GRUB_CMDLINE_LINUX_DEBUG}" + fi + options="$(echo "${options}" | sed -e 's/\//\\\//g')" + sed -i -e "s/^options.*/options ${options}/" "${blsdir}/${bls}.conf" + done +} + +populate_menu() +{ + local -a files=($(get_sorted_bls)) + + gettext_printf "Generating boot entries from BLS files...\n" >&2 + + for bls in "${files[@]}"; do + read_config "${blsdir}/${bls}.conf" + + menu="${menu}menuentry '${title}' ${grub_arg} --id=${bls} {\n" + menu="${menu}\t linux ${linux} ${options}\n" + if [ -n "${initrd}" ] ; then + menu="${menu}\t initrd ${boot_prefix}${initrd}\n" + fi + menu="${menu}}\n\n" + done + # The printf command seems to be more reliable across shells for special character (\n, \t) evaluation + printf "$menu" +} + +# Make BLS the default if GRUB_ENABLE_BLSCFG was not set and grubby is not installed. +if [ -z "${GRUB_ENABLE_BLSCFG}" ] && ! command -v new-kernel-pkg >/dev/null; then + GRUB_ENABLE_BLSCFG="true" +fi + +if [ "x${GRUB_ENABLE_BLSCFG}" = "xtrue" ]; then + if [ x$dirname = x/ ]; then + if [ -z "${prepare_root_cache}" ]; then + prepare_grub_to_access_device ${GRUB_DEVICE} + fi + else + if [ -z "${prepare_boot_cache}" ]; then + prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} + fi + fi + + if [ -d /sys/firmware/efi ]; then + bootefi_device="`${grub_probe} --target=device /boot/efi/`" + prepare_grub_to_access_device_with_variable boot ${bootefi_device} + else + boot_device="`${grub_probe} --target=device /boot/`" + prepare_grub_to_access_device_with_variable boot ${boot_device} + fi + + arch="$(uname -m)" + if [ "x${arch}" = "xppc64le" ] && [ -d /sys/firmware/opal ]; then + + BLS_POPULATE_MENU="true" + petitboot_path="/sys/firmware/devicetree/base/ibm,firmware-versions/petitboot" + + if test -e ${petitboot_path}; then + read -r -d '' petitboot_version < ${petitboot_path} + petitboot_version="$(echo ${petitboot_version//v})" + + if test -n ${petitboot_version}; then + major_version="$(echo ${petitboot_version} | cut -d . -f1)" + minor_version="$(echo ${petitboot_version} | cut -d . -f2)" + + re='^[0-9]+$' + if [[ $major_version =~ $re ]] && [[ $minor_version =~ $re ]] && + ([[ ${major_version} -gt 1 ]] || + [[ ${major_version} -eq 1 && + ${minor_version} -ge 8 ]]); then + BLS_POPULATE_MENU="false" + fi + fi + fi + fi + + populate_header_warn + + cat << EOF +# The kernelopts variable should be defined in the grubenv file. But to ensure that menu +# entries populated from BootLoaderSpec files that use this variable work correctly even +# without a grubenv file, define a fallback kernelopts variable if this has not been set. +# +# The kernelopts variable in the grubenv file can be modified using the grubby tool or by +# executing the grub2-mkconfig tool. For the latter, the values of the GRUB_CMDLINE_LINUX +# and GRUB_CMDLINE_LINUX_DEFAULT options from /etc/default/grub file are used to set both +# the kernelopts variable in the grubenv file and the fallback kernelopts variable. +if [ -z "\${kernelopts}" ]; then + set kernelopts="root=${LINUX_ROOT_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" +fi +EOF + + update_bls_cmdline + + if [ "x${BLS_POPULATE_MENU}" = "xtrue" ]; then + populate_menu + else + cat << EOF + +insmod blscfg +blscfg +EOF + fi + + if [ "x${GRUB_GRUBENV_UPDATE}" = "xyes" ]; then + blsdir="/boot/loader/entries" + [ -d "${blsdir}" ] && GRUB_BLS_FS="$(${grub_probe} --target=fs ${blsdir})" + if [ "x${GRUB_BLS_FS}" = "xbtrfs" ] || [ "x${GRUB_BLS_FS}" = "xzfs" ]; then + blsdir=$(make_system_path_relative_to_its_root "${blsdir}") + if [ "x${blsdir}" != "x/loader/entries" ] && [ "x${blsdir}" != "x/boot/loader/entries" ]; then + ${grub_editenv} - set blsdir="${blsdir}" + fi + fi + + if [ -n "${GRUB_EARLY_INITRD_LINUX_CUSTOM}" ]; then + ${grub_editenv} - set early_initrd="${GRUB_EARLY_INITRD_LINUX_CUSTOM}" + fi + + if [ -n "${GRUB_DEFAULT_DTB}" ]; then + ${grub_editenv} - set devicetree="${GRUB_DEFAULT_DTB}" + fi + + if [ -n "${GRUB_SAVEDEFAULT}" ]; then + ${grub_editenv} - set save_default="${GRUB_SAVEDEFAULT}" + fi + fi + + exit 0 +fi + mktitle () { local title_type @@ -121,6 +333,7 @@ linux_entry () if [ -z "$boot_device_id" ]; then boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")" fi + if [ x$type != xsimple ] ; then title=$(mktitle "$type" "$version") if [ x"$title" = x"$GRUB_ACTUAL_DEFAULT" ] || [ x"Previous Linux versions>$title" = x"$GRUB_ACTUAL_DEFAULT" ]; then @@ -231,6 +444,7 @@ is_top_level=true while [ "x$list" != "x" ] ; do linux=`version_find_latest $list` gettext_printf "Found linux image: %s\n" "$linux" >&2 + basename=`basename $linux` dirname=`dirname $linux` rel_dirname=`make_system_path_relative_to_its_root $dirname` @@ -269,7 +483,9 @@ while [ "x$list" != "x" ] ; do for i in ${initrd}; do initrd_display="${initrd_display} ${dirname}/${i}" done - gettext_printf "Found initrd image: %s\n" "$(echo $initrd_display)" >&2 + if [ "x${GRUB_ENABLE_BLSCFG}" != "xtrue" ]; then + gettext_printf "Found initrd image: %s\n" "$(echo $initrd_display)" >&2 + fi fi fdt= From 0eee063d97410ecc5d4b25610e54c413f0d81380 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 6 Feb 2018 11:16:28 +0100 Subject: [PATCH 059/367] Don't attempt to backtrace on grub_abort() for grub-emu The emu platform doesn't have a grub_backtrace() implementation, so this causes a build error. Don't attempt to call this when building grub-emu. Signed-off-by: Javier Martinez Canillas --- grub-core/kern/misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index a3e215155b..c60601b699 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -1201,7 +1201,7 @@ static void __attribute__ ((noreturn)) grub_abort (void) { #ifndef GRUB_UTIL -#if defined(__i386__) || defined(__x86_64__) +#if (defined(__i386__) || defined(__x86_64__)) && !defined(GRUB_MACHINE_EMU) grub_backtrace(); #endif #endif From 85a90056f5dee89a11c00ffa89cd8576357e54aa Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 15 Mar 2018 14:12:40 -0400 Subject: [PATCH 060/367] Add grub2-switch-to-blscfg Signed-off-by: Peter Jones Signed-off-by: Javier Martinez Canillas [jhlavac: Use ${etcdefaultgrub} instead of /etc/default/grub] Signed-off-by: Jan Hlavac [rharwood: skip on ostree installations, migrate man to h2m] Signed-off-by: Robbie Harwood --- Makefile.util.def | 7 + docs/man/grub-switch-to-blscfg.h2m | 2 + util/grub-set-password.in | 2 +- util/grub-switch-to-blscfg.in | 317 +++++++++++++++++++++++++++++ util/grub.d/10_linux.in | 2 +- 5 files changed, 328 insertions(+), 2 deletions(-) create mode 100644 docs/man/grub-switch-to-blscfg.h2m create mode 100644 util/grub-switch-to-blscfg.in diff --git a/Makefile.util.def b/Makefile.util.def index 18a9242776..88f55e35c4 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -1348,6 +1348,13 @@ program = { ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; +script = { + name = grub-switch-to-blscfg; + common = util/grub-switch-to-blscfg.in; + mansection = 8; + installdir = sbin; +}; + program = { name = grub-glue-efi; mansection = 1; diff --git a/docs/man/grub-switch-to-blscfg.h2m b/docs/man/grub-switch-to-blscfg.h2m new file mode 100644 index 0000000000..fa341426a5 --- /dev/null +++ b/docs/man/grub-switch-to-blscfg.h2m @@ -0,0 +1,2 @@ +[NAME] +grub-switch-to-blscfg \- switch to using BLS config files diff --git a/util/grub-set-password.in b/util/grub-set-password.in index 5ebf50576d..c0b5ebbfdc 100644 --- a/util/grub-set-password.in +++ b/util/grub-set-password.in @@ -1,6 +1,6 @@ #!/bin/sh -e -EFIDIR=$(grep ^ID= /etc/os-release | sed -e 's/^ID=//' -e 's/rhel/redhat/') +EFIDIR=$(grep ^ID= /etc/os-release | sed -e 's/^ID=//' -e 's/rhel/redhat/' -e 's/\"//g') if [ -d /sys/firmware/efi/efivars/ ]; then grubdir=`echo "/@bootdirname@/efi/EFI/${EFIDIR}/" | sed 's,//*,/,g'` else diff --git a/util/grub-switch-to-blscfg.in b/util/grub-switch-to-blscfg.in new file mode 100644 index 0000000000..a851424beb --- /dev/null +++ b/util/grub-switch-to-blscfg.in @@ -0,0 +1,317 @@ +#! /bin/sh +# +# Set a default boot entry for GRUB. +# Copyright (C) 2004,2009 Free Software Foundation, Inc. +# +# GRUB is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# GRUB is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GRUB. If not, see . + +#set -eu + +# Initialize some variables. +prefix=@prefix@ +exec_prefix=@exec_prefix@ +sbindir=@sbindir@ +bindir=@bindir@ +sysconfdir="@sysconfdir@" +PACKAGE_NAME=@PACKAGE_NAME@ +PACKAGE_VERSION=@PACKAGE_VERSION@ +datarootdir="@datarootdir@" +datadir="@datadir@" +if [ ! -v pkgdatadir ]; then + pkgdatadir="${datadir}/@PACKAGE@" +fi + +self=`basename $0` + +grub_get_kernel_settings="${sbindir}/@grub_get_kernel_settings@" +grub_editenv=${bindir}/@grub_editenv@ +etcdefaultgrub=/etc/default/grub + +eval "$("${grub_get_kernel_settings}")" || true + +EFIDIR=$(grep ^ID= /etc/os-release | sed -e 's/^ID=//' -e 's/rhel/redhat/' -e 's/\"//g') +if [ -d /sys/firmware/efi/efivars/ ]; then + startlink=/etc/grub2-efi.cfg + grubdir=`echo "/@bootdirname@/efi/EFI/${EFIDIR}/" | sed 's,//*,/,g'` +else + startlink=/etc/grub2.cfg + grubdir=`echo "/@bootdirname@/@grubdirname@" | sed 's,//*,/,g'` +fi + +blsdir=`echo "/@bootdirname@/loader/entries" | sed 's,//*,/,g'` + +backupsuffix=.bak + +arch="$(uname -m)" + +export TEXTDOMAIN=@PACKAGE@ +export TEXTDOMAINDIR="@localedir@" + +. "${pkgdatadir}/grub-mkconfig_lib" + +# Usage: usage +# Print the usage. +usage () { + gettext_printf "Usage: %s\n" "$self" + gettext "Switch to BLS config files.\n"; echo + echo + print_option_help "-h, --help" "$(gettext "print this message and exit")" + print_option_help "-V, --version" "$(gettext "print the version information and exit")" + echo + print_option_help "--backup-suffix=$(gettext "SUFFIX")" "$backupsuffix" + print_option_help "--bls-directory=$(gettext "DIR")" "$blsdir" + print_option_help "--config-file=$(gettext "FILE")" "$startlink" + print_option_help "--grub-defaults=$(gettext "FILE")" "$etcdefaultgrub" + print_option_help "--grub-directory=$(gettext "DIR")" "$grubdir" + # echo + # gettext "Report bugs to ."; echo +} + +argument () { + opt=$1 + shift + + if test $# -eq 0; then + gettext_printf "%s: option requires an argument -- \`%s'\n" "$self" "$opt" 1>&2 + exit 1 + fi + echo $1 +} + +# Check the arguments. +while test $# -gt 0 +do + option=$1 + shift + + case "$option" in + -h | --help) + usage + exit 0 ;; + -V | --version) + echo "$self (${PACKAGE_NAME}) ${PACKAGE_VERSION}" + exit 0 ;; + + --backup-suffix) + backupsuffix=`argument $option "$@"` + shift + ;; + --backup-suffix=*) + backupsuffix=`echo "$option" | sed 's/--backup-suffix=//'` + ;; + + --bls-directory) + blsdir=`argument $option "$@"` + shift + ;; + --bls-directory=*) + blsdir=`echo "$option" | sed 's/--bls-directory=//'` + ;; + + --config-file) + startlink=`argument $option "$@"` + shift + ;; + --config-file=*) + startlink=`echo "$option" | sed 's/--config-file=//'` + ;; + + --grub-defaults) + etcdefaultgrub=`argument $option "$@"` + shift + ;; + --grub-defaults=*) + etcdefaultgrub=`echo "$option" | sed 's/--grub-defaults=//'` + ;; + + --grub-directory) + grubdir=`argument $option "$@"` + shift + ;; + --grub-directory=*) + grubdir=`echo "$option" | sed 's/--grub-directory=//'` + ;; + + *) + gettext_printf "Unrecognized option \`%s'\n" "$option" 1>&2 + usage + exit 1 + ;; + esac +done + +find_grub_cfg() { + local candidate="" + while [ -e "${candidate}" -o $# -gt 0 ] + do + if [ ! -e "${candidate}" ] ; then + candidate="$1" + shift + fi + + if [ -L "${candidate}" ]; then + candidate="$(realpath "${candidate}")" + fi + + if [ -f "${candidate}" ]; then + export GRUB_CONFIG_FILE="${candidate}" + return 0 + fi + done + return 1 +} + +if ! find_grub_cfg ${startlink} ${grubdir}/grub.cfg ; then + gettext_printf "Couldn't find config file\n" 1>&2 + exit 1 +fi + +if [ ! -d "${blsdir}" ]; then + install -m 700 -d "${blsdir}" +fi + +if [ -f /etc/machine-id ]; then + MACHINE_ID=$(cat /etc/machine-id) +else + MACHINE_ID=$(dmesg | sha256sum) +fi + +mkbls() { + local kernelver=$1 && shift + local datetime=$1 && shift + local kernelopts=$1 && shift + + local debugname="" + local debugid="" + local flavor="" + + if [ "$kernelver" == *\+* ] ; then + local flavor=-"${kernelver##*+}" + if [ "${flavor}" == "-debug" ]; then + local debugname=" with debugging" + local debugid="-debug" + fi + fi + ( + source /etc/os-release + + cat <"${bls_target}" + + if [ "x$GRUB_LINUX_MAKE_DEBUG" = "xtrue" ]; then + bls_debug="$(echo ${bls_target} | sed -e "s/${kernelver}/${kernelver}~debug/")" + cp -aT "${bls_target}" "${bls_debug}" + title="$(grep '^title[ \t]' "${bls_debug}" | sed -e 's/^title[ \t]*//')" + options="$(echo "${cmdline} ${GRUB_CMDLINE_LINUX_DEBUG}" | sed -e 's/\//\\\//g')" + sed -i -e "s/^title.*/title ${title}${GRUB_LINUX_DEBUG_TITLE_POSTFIX}/" "${bls_debug}" + sed -i -e "s/^options.*/options ${options}/" "${bls_debug}" + fi + done + + if [ -f "/boot/vmlinuz-0-rescue-${MACHINE_ID}" ]; then + mkbls "0-rescue-${MACHINE_ID}" "0" "${bootprefix}" >"${blsdir}/${MACHINE_ID}-0-rescue.conf" + fi +} + +# The grub2 EFI binary is not copied to the ESP as a part of an ostree +# transaction. Make sure a grub2 version with BLS support is installed +# but only do this if the blsdir is not set, to make sure that the BLS +# parsing module will search for the BLS snippets in the default path. +if test -f /run/ostree-booted && test -d /sys/firmware/efi/efivars && \ + ! ${grub_editenv} - list | grep -q blsdir && \ + mountpoint -q /boot; then + grub_binary="$(find /usr/lib/ostree-boot/efi/EFI/${EFIDIR}/ -name grub*.efi)" + install -m 700 ${grub_binary} ${grubdir} || exit 1 + # Create a hidden file to indicate that grub2 now has BLS support. + touch /boot/grub2/.grub2-blscfg-supported +fi + +GENERATE=0 +if grep '^GRUB_ENABLE_BLSCFG=.*' "${etcdefaultgrub}" \ + | grep -vq '^GRUB_ENABLE_BLSCFG="*true"*\s*$' ; then + if ! sed -i"${backupsuffix}" \ + -e 's,^GRUB_ENABLE_BLSCFG=.*,GRUB_ENABLE_BLSCFG=true,' \ + "${etcdefaultgrub}" ; then + gettext_printf "Updating %s failed\n" "${etcdefaultgrub}" + exit 1 + fi + GENERATE=1 +elif ! grep -q '^GRUB_ENABLE_BLSCFG=.*' "${etcdefaultgrub}" ; then + if ! echo 'GRUB_ENABLE_BLSCFG=true' >> "${etcdefaultgrub}" ; then + gettext_printf "Updating %s failed\n" "${etcdefaultgrub}" + exit 1 + fi + GENERATE=1 +fi + +if [ "${GENERATE}" -eq 1 ] ; then + copy_bls + + if [ $arch = "x86_64" ] && [ ! -d /sys/firmware/efi ]; then + mod_dir="i386-pc" + elif [ $arch = "ppc64" -o $arch = "ppc64le" ] && [ ! -d /sys/firmware/opal ]; then + mod_dir="powerpc-ieee1275" + fi + + if [ -n "${mod_dir}" ]; then + for mod in blscfg increment; do + install -m 700 ${prefix}/lib/grub/${mod_dir}/${mod}.mod ${grubdir}/$mod_dir/ || exit 1 + done + fi + + cp -af "${GRUB_CONFIG_FILE}" "${GRUB_CONFIG_FILE}${backupsuffix}" + if ! grub2-mkconfig -o "${GRUB_CONFIG_FILE}" ; then + install -m 700 "${GRUB_CONFIG_FILE}${backupsuffix}" "${GRUB_CONFIG_FILE}" + sed -i"${backupsuffix}" \ + -e 's,^GRUB_ENABLE_BLSCFG=.*,GRUB_ENABLE_BLSCFG=false,' \ + "${etcdefaultgrub}" + gettext_printf "Updating %s failed\n" "${GRUB_CONFIG_FILE}" + exit 1 + fi +fi + +# Bye. +exit 0 diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 2851952659..e490e1a43a 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -138,7 +138,7 @@ blsdir="/boot/loader/entries" get_sorted_bls() { - if ! [ -d "${blsdir}" ]; then + if ! [ -d "${blsdir}" ] || [ -f /run/ostree-booted ] || [ -d /ostree/repo ]; then return fi From 02785087379e2b8899d28f099f8329dc45c66b91 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 9 Jul 2019 17:05:03 +0200 Subject: [PATCH 061/367] make better backtraces Signed-off-by: Peter Jones --- Makefile.util.def | 6 ++ grub-core/Makefile.am | 1 + grub-core/Makefile.core.def | 16 +-- grub-core/{lib => commands}/backtrace.c | 2 +- grub-core/gdb/cstub.c | 1 - grub-core/kern/arm/efi/startup.S | 2 + grub-core/kern/arm/startup.S | 2 + grub-core/kern/arm64/backtrace.c | 94 ++++++++++++++++++ grub-core/kern/arm64/efi/startup.S | 2 + grub-core/kern/backtrace.c | 97 ++++++++++++++++++ grub-core/kern/dl.c | 45 +++++++++ grub-core/kern/i386/backtrace.c | 125 ++++++++++++++++++++++++ grub-core/kern/i386/pc/init.c | 4 +- grub-core/kern/i386/qemu/startup.S | 3 +- grub-core/kern/ia64/efi/startup.S | 3 +- grub-core/kern/ieee1275/init.c | 1 - grub-core/kern/misc.c | 13 +-- grub-core/kern/mm.c | 6 +- grub-core/kern/sparc64/ieee1275/crt0.S | 3 +- grub-core/lib/arm64/backtrace.c | 62 ------------ grub-core/lib/i386/backtrace.c | 78 --------------- include/grub/backtrace.h | 10 +- include/grub/dl.h | 2 + include/grub/kernel.h | 3 + 24 files changed, 414 insertions(+), 167 deletions(-) rename grub-core/{lib => commands}/backtrace.c (98%) create mode 100644 grub-core/kern/arm64/backtrace.c create mode 100644 grub-core/kern/backtrace.c create mode 100644 grub-core/kern/i386/backtrace.c delete mode 100644 grub-core/lib/arm64/backtrace.c delete mode 100644 grub-core/lib/i386/backtrace.c diff --git a/Makefile.util.def b/Makefile.util.def index 88f55e35c4..bda9fd1211 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -51,6 +51,12 @@ library = { common = grub-core/partmap/msdos.c; common = grub-core/fs/proc.c; common = grub-core/fs/archelp.c; + common = grub-core/kern/backtrace.c; + + x86 = grub-core/kern/i386/backtrace.c; + i386_xen = grub-core/kern/i386/backtrace.c; + x86_64_xen = grub-core/kern/i386/backtrace.c; + arm64 = grub-core/kern/arm64/backtrace.c; }; library = { diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index ee88e44e97..bfd29a3bf0 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -66,6 +66,7 @@ CLEANFILES += grub_script.yy.c grub_script.yy.h include $(srcdir)/Makefile.core.am +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/backtrace.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/cache.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/command.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/device.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 058c88ac3a..52ec0fafcd 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -142,6 +142,12 @@ kernel = { common = kern/rescue_reader.c; common = kern/term.c; common = kern/verifiers.c; + common = kern/backtrace.c; + + x86 = kern/i386/backtrace.c; + i386_xen = kern/i386/backtrace.c; + x86_64_xen = kern/i386/backtrace.c; + arm64 = kern/arm64/backtrace.c; noemu = kern/compiler-rt.c; noemu = kern/mm.c; @@ -188,9 +194,6 @@ kernel = { softdiv = lib/division.c; - x86 = lib/i386/backtrace.c; - x86 = lib/backtrace.c; - i386 = kern/i386/dl.c; i386_xen = kern/i386/dl.c; i386_xen_pvh = kern/i386/dl.c; @@ -2399,15 +2402,12 @@ module = { module = { name = backtrace; - x86 = lib/i386/backtrace.c; - i386_xen_pvh = lib/i386/backtrace.c; - i386_xen = lib/i386/backtrace.c; - x86_64_xen = lib/i386/backtrace.c; - common = lib/backtrace.c; + common = commands/backtrace.c; enable = x86; enable = i386_xen_pvh; enable = i386_xen; enable = x86_64_xen; + enable = arm64; }; module = { diff --git a/grub-core/lib/backtrace.c b/grub-core/commands/backtrace.c similarity index 98% rename from grub-core/lib/backtrace.c rename to grub-core/commands/backtrace.c index c0ad6ab8be..8b5ec3913b 100644 --- a/grub-core/lib/backtrace.c +++ b/grub-core/commands/backtrace.c @@ -54,7 +54,7 @@ grub_cmd_backtrace (grub_command_t cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) { - grub_backtrace (); + grub_backtrace (1); return 0; } diff --git a/grub-core/gdb/cstub.c b/grub-core/gdb/cstub.c index b64acd70fe..99281472d3 100644 --- a/grub-core/gdb/cstub.c +++ b/grub-core/gdb/cstub.c @@ -215,7 +215,6 @@ grub_gdb_trap (int trap_no) grub_printf ("Unhandled exception 0x%x at ", trap_no); grub_backtrace_print_address ((void *) grub_gdb_regs[PC]); grub_printf ("\n"); - grub_backtrace_pointer ((void *) grub_gdb_regs[EBP]); grub_fatal ("Unhandled exception"); } diff --git a/grub-core/kern/arm/efi/startup.S b/grub-core/kern/arm/efi/startup.S index 9f8265315a..f3bc41f9d0 100644 --- a/grub-core/kern/arm/efi/startup.S +++ b/grub-core/kern/arm/efi/startup.S @@ -23,6 +23,8 @@ .file "startup.S" .text .arm + .globl start, _start +FUNCTION(start) FUNCTION(_start) /* * EFI_SYSTEM_TABLE and EFI_HANDLE are passed in r1/r0. diff --git a/grub-core/kern/arm/startup.S b/grub-core/kern/arm/startup.S index 3946fe8e18..5679a1d00a 100644 --- a/grub-core/kern/arm/startup.S +++ b/grub-core/kern/arm/startup.S @@ -48,6 +48,8 @@ .text .arm + .globl start, _start +FUNCTION(start) FUNCTION(_start) b codestart diff --git a/grub-core/kern/arm64/backtrace.c b/grub-core/kern/arm64/backtrace.c new file mode 100644 index 0000000000..019c6fdfef --- /dev/null +++ b/grub-core/kern/arm64/backtrace.c @@ -0,0 +1,94 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_STACK_FRAME 102400 + +struct fplr +{ + void *lr; + struct fplr *fp; +}; + +void +grub_backtrace_pointer (void *frame, unsigned int skip) +{ + unsigned int x = 0; + struct fplr *fplr = (struct fplr *)frame; + + while (fplr) + { + const char *name = NULL; + char *addr = NULL; + + grub_dprintf("backtrace", "fp is %p next_fp is %p\n", + fplr, fplr->fp); + + if (x >= skip) + { + name = grub_get_symbol_by_addr (fplr->lr, 1); + if (name) + addr = grub_resolve_symbol (name); + grub_backtrace_print_address (fplr->lr); + + if (addr && addr != fplr->lr) + grub_printf (" %s() %p+%p \n", name ? name : "unknown", addr, + (void *)((grub_uint64_t)fplr->lr - (grub_uint64_t)addr)); + else + grub_printf(" %s() %p \n", name ? name : "unknown", addr); + + } + + x += 1; + + if (fplr->fp < fplr || + (grub_uint64_t)fplr->fp - (grub_uint64_t)fplr > MAX_STACK_FRAME || + fplr->fp == fplr) + { + break; + } + fplr = fplr->fp; + } +} + +asm ("\t.global \"_text\"\n" + "_text:\n" + "\t.quad .text\n" + "\t.global \"_data\"\n" + "_data:\n" + "\t.quad .data\n" + ); + +extern grub_uint64_t _text; +extern grub_uint64_t _data; + +void +grub_backtrace_arch (unsigned int skip) +{ + grub_printf ("Backtrace (.text %p .data %p):\n", + (void *)_text, (void *)_data); + skip += 1; + grub_backtrace_pointer(__builtin_frame_address(0), skip); +} diff --git a/grub-core/kern/arm64/efi/startup.S b/grub-core/kern/arm64/efi/startup.S index 666a7ee3c9..41676bdb2b 100644 --- a/grub-core/kern/arm64/efi/startup.S +++ b/grub-core/kern/arm64/efi/startup.S @@ -19,7 +19,9 @@ #include .file "startup.S" + .globl start, _start .text +FUNCTION(start) FUNCTION(_start) /* * EFI_SYSTEM_TABLE and EFI_HANDLE are passed in x1/x0. diff --git a/grub-core/kern/backtrace.c b/grub-core/kern/backtrace.c new file mode 100644 index 0000000000..4a82e865cc --- /dev/null +++ b/grub-core/kern/backtrace.c @@ -0,0 +1,97 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static void +grub_backtrace_print_address_default (void *addr) +{ +#ifndef GRUB_UTIL + grub_dl_t mod; + void *start_addr; + + FOR_DL_MODULES (mod) + { + grub_dl_segment_t segment; + for (segment = mod->segment; segment; segment = segment->next) + if (segment->addr <= addr && (grub_uint8_t *) segment->addr + + segment->size > (grub_uint8_t *) addr) + { + grub_printf ("%s.%x+%" PRIxGRUB_SIZE, mod->name, + segment->section, + (grub_size_t) + ((grub_uint8_t *)addr - (grub_uint8_t *)segment->addr)); + return; + } + } + + start_addr = grub_resolve_symbol ("_start"); + if (start_addr && start_addr < addr) + grub_printf ("kernel+%" PRIxGRUB_SIZE, + (grub_size_t) + ((grub_uint8_t *)addr - (grub_uint8_t *)start_addr)); + else +#endif + grub_printf ("%p", addr); +} + +static void +grub_backtrace_pointer_default (void *frame __attribute__((__unused__)), + unsigned int skip __attribute__((__unused__))) +{ + return; +} + +void +grub_backtrace_pointer (void *frame, unsigned int skip) + __attribute__((__weak__, + __alias__(("grub_backtrace_pointer_default")))); + +void +grub_backtrace_print_address (void *addr) + __attribute__((__weak__, + __alias__(("grub_backtrace_print_address_default")))); + +static void +grub_backtrace_arch_default(unsigned int skip) +{ + grub_backtrace_pointer(__builtin_frame_address(0), skip + 1); +} + +void grub_backtrace_arch (unsigned int skip) + __attribute__((__weak__, __alias__(("grub_backtrace_arch_default")))); + +void grub_backtrace (unsigned int skip) +{ + grub_backtrace_arch(skip + 1); +} + +void grub_debug_backtrace (const char * const debug, + unsigned int skip) +{ + if (grub_debug_enabled (debug)) + grub_backtrace (skip + 1); +} diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index 7afb9e6f72..88d2077709 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -124,6 +124,50 @@ grub_dl_resolve_symbol (const char *name) return 0; } +void * +grub_resolve_symbol (const char *name) +{ + grub_symbol_t sym; + + sym = grub_dl_resolve_symbol (name); + if (sym) + return sym->addr; + return NULL; +} + +const char * +grub_get_symbol_by_addr(const void *addr, int isfunc) +{ + unsigned int i; + grub_symbol_t before = NULL, after = NULL; + for (i = 0; i < GRUB_SYMTAB_SIZE; i++) + { + grub_symbol_t sym; + for (sym = grub_symtab[i]; sym; sym = sym->next) + { + //grub_printf ("addr 0x%08llx symbol %s\n", (unsigned long long)sym->addr, sym->name); + if (sym->addr > addr) + { + if (!after || sym->addr > after->addr) + after = sym; + } + + if (isfunc != sym->isfunc) + continue; + if (sym->addr > addr) + continue; + + if ((!before && sym->addr <= addr) || (before && before->addr <= sym->addr)) + before = sym; + } + } + + if (before && addr < after->addr) + return before->name; + + return NULL; +} + /* Register a symbol with the name NAME and the address ADDR. */ grub_err_t grub_dl_register_symbol (const char *name, void *addr, int isfunc, @@ -336,6 +380,7 @@ grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e) const char *str; Elf_Word size, entsize; + grub_dprintf ("modules", "Resolving symbols for \"%s\"\n", mod->name); for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); i < e->e_shnum; i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) diff --git a/grub-core/kern/i386/backtrace.c b/grub-core/kern/i386/backtrace.c new file mode 100644 index 0000000000..2413f9a57d --- /dev/null +++ b/grub-core/kern/i386/backtrace.c @@ -0,0 +1,125 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_STACK_FRAME 102400 + +void +grub_backtrace_pointer (void *frame, unsigned int skip) +{ + void **ebp = (void **)frame; + unsigned long x = 0; + + while (ebp) + { + void **next_ebp = (void **)ebp[0]; + const char *name = NULL; + char *addr = NULL; + + grub_dprintf("backtrace", "ebp is %p next_ebp is %p\n", ebp, next_ebp); + + if (x >= skip) + { + name = grub_get_symbol_by_addr (ebp[1], 1); + if (name) + addr = grub_resolve_symbol (name); + grub_backtrace_print_address (ebp[1]); + + if (addr && addr != ebp[1]) + grub_printf (" %s() %p+%p \n", name ? name : "unknown", addr, + (char *)((char *)ebp[1] - addr)); + else + grub_printf(" %s() %p \n", name ? name : "unknown", addr); + +#if 0 + grub_printf ("("); + for (i = 0, arg = ebp[2]; arg != next_ebp && i < 12; arg++, i++) + grub_printf ("%p,", arg); + grub_printf (")\n"); +#endif + } + + x += 1; + + if (next_ebp < ebp || next_ebp - ebp > MAX_STACK_FRAME || next_ebp == ebp) + { + //grub_printf ("Invalid stack frame at %p (%p)\n", ebp, next_ebp); + break; + } + ebp = next_ebp; + } +} + +#if defined (__x86_64__) +asm ("\t.global \"_text\"\n" + "_text:\n" + "\t.quad .text\n" + "\t.global \"_data\"\n" + "_data:\n" + "\t.quad .data\n" + ); +#elif defined(__i386__) +asm ("\t.global \"_text\"\n" + "_text:\n" + "\t.long .text\n" + "\t.global \"_data\"\n" + "_data:\n" + "\t.long .data\n" + ); +#else +#warning I dunno... +#endif + +extern unsigned long _text; +extern unsigned long _data; + +#ifdef GRUB_UTIL +#define EXT_C(x) x +#endif + +void +grub_backtrace_arch (unsigned int skip) +{ + grub_printf ("Backtrace (.text %p .data %p):\n", + (void *)_text, (void *)_data); + skip += 1; +#if defined (__x86_64__) + asm volatile ("movq %%rbp, %%rdi\n" + "movq 0, %%rsi\n" + "movl %0, %%esi\n" + "call " EXT_C("grub_backtrace_pointer") + : + : "r" (skip)); +#elif defined(__i386__) + asm volatile ("addl $8, %%esp\n" + "pushl %0\n" + "pushl %%ebp\n" + "call " EXT_C("grub_backtrace_pointer") + : + : "r" (skip)); +#else + grub_backtrace_pointer(__builtin_frame_address(0), skip); +#endif +} diff --git a/grub-core/kern/i386/pc/init.c b/grub-core/kern/i386/pc/init.c index 27bc68b8a5..b51d0abfa6 100644 --- a/grub-core/kern/i386/pc/init.c +++ b/grub-core/kern/i386/pc/init.c @@ -153,7 +153,7 @@ compact_mem_regions (void) } grub_addr_t grub_modbase; -extern grub_uint8_t _start[], _edata[]; +extern grub_uint8_t _edata[]; /* Helper for grub_machine_init. */ static int @@ -217,7 +217,7 @@ grub_machine_init (void) /* This has to happen before any BIOS calls. */ grub_via_workaround_init (); - grub_modbase = GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR + (_edata - _start); + grub_modbase = GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR + (_edata - (grub_uint8_t *)_start); /* Initialize the console as early as possible. */ grub_console_init (); diff --git a/grub-core/kern/i386/qemu/startup.S b/grub-core/kern/i386/qemu/startup.S index 0d89858d9b..939f182fc7 100644 --- a/grub-core/kern/i386/qemu/startup.S +++ b/grub-core/kern/i386/qemu/startup.S @@ -24,7 +24,8 @@ .text .code32 - .globl _start + .globl start, _start +start: _start: jmp codestart diff --git a/grub-core/kern/ia64/efi/startup.S b/grub-core/kern/ia64/efi/startup.S index d75c6d7cc7..8f2a593e52 100644 --- a/grub-core/kern/ia64/efi/startup.S +++ b/grub-core/kern/ia64/efi/startup.S @@ -24,8 +24,9 @@ .psr lsb .lsb - .global _start + .global start, _start .proc _start +start: _start: alloc loc0=ar.pfs,2,4,0,0 mov loc1=rp diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 0cd2a62723..937c1bc44c 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -63,7 +63,6 @@ #define HEAP_MAX_ADDR (unsigned long) (32 * 1024 * 1024) #endif -extern char _start[]; extern char _end[]; #ifdef __sparc__ diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index c60601b699..a432a6be54 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -1197,15 +1197,15 @@ grub_printf_fmt_check (const char *fmt, const char *fmt_expected) /* Abort GRUB. This function does not return. */ -static void __attribute__ ((noreturn)) +static inline void __attribute__ ((noreturn)) grub_abort (void) { -#ifndef GRUB_UTIL -#if (defined(__i386__) || defined(__x86_64__)) && !defined(GRUB_MACHINE_EMU) - grub_backtrace(); -#endif +#if !defined(GRUB_MACHINE_EMU) && !defined(GRUB_UTIL) + grub_backtrace (1); +#else + grub_printf ("\n"); #endif - grub_printf ("\nAborted."); + grub_printf ("Aborted."); #ifndef GRUB_UTIL if (grub_term_inputs) @@ -1232,6 +1232,7 @@ grub_fatal (const char *fmt, ...) { va_list ap; + grub_printf ("\n"); va_start (ap, fmt); grub_vprintf (_(fmt), ap); va_end (ap); diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c index c070afc621..d8c8377578 100644 --- a/grub-core/kern/mm.c +++ b/grub-core/kern/mm.c @@ -97,13 +97,13 @@ get_header_from_pointer (void *ptr, grub_mm_header_t *p, grub_mm_region_t *r) break; if (! *r) - grub_fatal ("out of range pointer %p", ptr); + grub_fatal ("out of range pointer %p\n", ptr); *p = (grub_mm_header_t) ptr - 1; if ((*p)->magic == GRUB_MM_FREE_MAGIC) - grub_fatal ("double free at %p", *p); + grub_fatal ("double free at %p\n", *p); if ((*p)->magic != GRUB_MM_ALLOC_MAGIC) - grub_fatal ("alloc magic is broken at %p: %lx", *p, + grub_fatal ("alloc magic is broken at %p: %lx\n", *p, (unsigned long) (*p)->magic); } diff --git a/grub-core/kern/sparc64/ieee1275/crt0.S b/grub-core/kern/sparc64/ieee1275/crt0.S index 03b916f053..701bf63abc 100644 --- a/grub-core/kern/sparc64/ieee1275/crt0.S +++ b/grub-core/kern/sparc64/ieee1275/crt0.S @@ -22,7 +22,8 @@ .text .align 4 - .globl _start + .globl start, _start +start: _start: ba codestart mov %o4, %o0 diff --git a/grub-core/lib/arm64/backtrace.c b/grub-core/lib/arm64/backtrace.c deleted file mode 100644 index 1079b5380e..0000000000 --- a/grub-core/lib/arm64/backtrace.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2009 Free Software Foundation, Inc. - * - * GRUB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * GRUB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GRUB. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include - -#define MAX_STACK_FRAME 102400 - -void -grub_backtrace_pointer (int frame) -{ - while (1) - { - void *lp = __builtin_return_address (frame); - if (!lp) - break; - - lp = __builtin_extract_return_addr (lp); - - grub_printf ("%p: ", lp); - grub_backtrace_print_address (lp); - grub_printf (" ("); - for (i = 0; i < 2; i++) - grub_printf ("%p,", ((void **)ptr) [i + 2]); - grub_printf ("%p)\n", ((void **)ptr) [i + 2]); - nptr = *(void **)ptr; - if (nptr < ptr || (void **) nptr - (void **) ptr > MAX_STACK_FRAME - || nptr == ptr) - { - grub_printf ("Invalid stack frame at %p (%p)\n", ptr, nptr); - break; - } - ptr = nptr; - } -} - -void -grub_backtrace (void) -{ - grub_backtrace_pointer (1); -} - diff --git a/grub-core/lib/i386/backtrace.c b/grub-core/lib/i386/backtrace.c deleted file mode 100644 index c67273db3a..0000000000 --- a/grub-core/lib/i386/backtrace.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2009 Free Software Foundation, Inc. - * - * GRUB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * GRUB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GRUB. If not, see . - */ -#include -#ifdef GRUB_UTIL -#define REALLY_GRUB_UTIL GRUB_UTIL -#undef GRUB_UTIL -#endif - -#include -#include - -#ifdef REALLY_GRUB_UTIL -#define GRUB_UTIL REALLY_GRUB_UTIL -#undef REALLY_GRUB_UTIL -#endif - -#include -#include -#include -#include -#include -#include - -#define MAX_STACK_FRAME 102400 - -void -grub_backtrace_pointer (void *ebp) -{ - void *ptr, *nptr; - unsigned i; - - ptr = ebp; - while (1) - { - grub_printf ("%p: ", ptr); - grub_backtrace_print_address (((void **) ptr)[1]); - grub_printf (" ("); - for (i = 0; i < 2; i++) - grub_printf ("%p,", ((void **)ptr) [i + 2]); - grub_printf ("%p)\n", ((void **)ptr) [i + 2]); - nptr = *(void **)ptr; - if (nptr < ptr || (void **) nptr - (void **) ptr > MAX_STACK_FRAME - || nptr == ptr) - { - grub_printf ("Invalid stack frame at %p (%p)\n", ptr, nptr); - break; - } - ptr = nptr; - } -} - -void -grub_backtrace (void) -{ -#ifdef __x86_64__ - asm volatile ("movq %%rbp, %%rdi\n" - "callq *%%rax": :"a"(grub_backtrace_pointer)); -#else - asm volatile ("movl %%ebp, %%eax\n" - "calll *%%ecx": :"c"(grub_backtrace_pointer)); -#endif -} - diff --git a/include/grub/backtrace.h b/include/grub/backtrace.h index 395519762f..275cf85e2d 100644 --- a/include/grub/backtrace.h +++ b/include/grub/backtrace.h @@ -19,8 +19,14 @@ #ifndef GRUB_BACKTRACE_HEADER #define GRUB_BACKTRACE_HEADER 1 -void grub_backtrace (void); -void grub_backtrace_pointer (void *ptr); +#include +#include + +void EXPORT_FUNC(grub_debug_backtrace) (const char * const debug, + unsigned int skip); +void EXPORT_FUNC(grub_backtrace) (unsigned int skip); +void grub_backtrace_arch (unsigned int skip); +void grub_backtrace_pointer (void *ptr, unsigned int skip); void grub_backtrace_print_address (void *addr); #endif diff --git a/include/grub/dl.h b/include/grub/dl.h index 91933b85f2..2f76e6b043 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -259,6 +259,8 @@ grub_dl_is_persistent (grub_dl_t mod) #endif +void * EXPORT_FUNC(grub_resolve_symbol) (const char *name); +const char * EXPORT_FUNC(grub_get_symbol_by_addr) (const void *addr, int isfunc); grub_err_t grub_dl_register_symbol (const char *name, void *addr, int isfunc, grub_dl_t mod); diff --git a/include/grub/kernel.h b/include/grub/kernel.h index abbca5ea33..300a9766cd 100644 --- a/include/grub/kernel.h +++ b/include/grub/kernel.h @@ -111,6 +111,9 @@ grub_addr_t grub_modules_get_end (void); #endif +void EXPORT_FUNC(start) (void); +void EXPORT_FUNC(_start) (void); + /* The start point of the C code. */ void grub_main (void) __attribute__ ((noreturn)); From ce23583aafa14ca33f52a9166956f7421daee55e Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 9 Nov 2017 15:58:52 -0500 Subject: [PATCH 062/367] normal: don't draw our startup message if debug is set --- grub-core/normal/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 55558cc0b9..af9792c963 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -430,6 +430,9 @@ grub_normal_reader_init (int nested) const char *msg_esc = _("ESC at any time exits."); char *msg_formatted; + if (grub_env_get ("debug") != NULL) + return 0; + msg_formatted = grub_xasprintf (_("Minimal BASH-like line editing is supported. For " "the first word, TAB lists possible command completions. Anywhere " "else TAB lists possible device or file completions. %s"), From 4977abeae4ad1368489d1f7517428934bec60a94 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 16 Mar 2018 13:28:57 -0400 Subject: [PATCH 063/367] Work around some minor include path weirdnesses Signed-off-by: Peter Jones --- include/grub/arm/efi/console.h | 24 ++++++++++++++++++++++++ include/grub/arm64/efi/console.h | 24 ++++++++++++++++++++++++ include/grub/i386/efi/console.h | 24 ++++++++++++++++++++++++ include/grub/x86_64/efi/console.h | 24 ++++++++++++++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 include/grub/arm/efi/console.h create mode 100644 include/grub/arm64/efi/console.h create mode 100644 include/grub/i386/efi/console.h create mode 100644 include/grub/x86_64/efi/console.h diff --git a/include/grub/arm/efi/console.h b/include/grub/arm/efi/console.h new file mode 100644 index 0000000000..1592f6f76b --- /dev/null +++ b/include/grub/arm/efi/console.h @@ -0,0 +1,24 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2005,2006,2007 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_ARM_EFI_CONSOLE_H +#define GRUB_ARM_EFI_CONSOLE_H + +#include + +#endif /* ! GRUB_ARM_EFI_CONSOLE_H */ diff --git a/include/grub/arm64/efi/console.h b/include/grub/arm64/efi/console.h new file mode 100644 index 0000000000..9568933938 --- /dev/null +++ b/include/grub/arm64/efi/console.h @@ -0,0 +1,24 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2005,2006,2007 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_ARM64_EFI_CONSOLE_H +#define GRUB_ARM64_EFI_CONSOLE_H + +#include + +#endif /* ! GRUB_ARM64_EFI_CONSOLE_H */ diff --git a/include/grub/i386/efi/console.h b/include/grub/i386/efi/console.h new file mode 100644 index 0000000000..9231375cb0 --- /dev/null +++ b/include/grub/i386/efi/console.h @@ -0,0 +1,24 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2005,2006,2007 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_I386_EFI_CONSOLE_H +#define GRUB_I386_EFI_CONSOLE_H + +#include + +#endif /* ! GRUB_I386_EFI_CONSOLE_H */ diff --git a/include/grub/x86_64/efi/console.h b/include/grub/x86_64/efi/console.h new file mode 100644 index 0000000000..dba9d8678d --- /dev/null +++ b/include/grub/x86_64/efi/console.h @@ -0,0 +1,24 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2005,2006,2007 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_X86_64_EFI_CONSOLE_H +#define GRUB_X86_64_EFI_CONSOLE_H + +#include + +#endif /* ! GRUB_X86_64_EFI_CONSOLE_H */ From 7aca689e5360b5f2857f12c71189ed76d87f3b24 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 25 Jun 2015 15:41:06 -0400 Subject: [PATCH 064/367] Make it possible to enabled --build-id=sha1 Signed-off-by: Peter Jones --- acinclude.m4 | 19 +++++++++++++++++++ configure.ac | 8 ++++++++ 2 files changed, 27 insertions(+) diff --git a/acinclude.m4 b/acinclude.m4 index 6e14bb553c..21238fcfd0 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -136,6 +136,25 @@ if test "x$grub_cv_prog_ld_build_id_none" = xyes; then fi ]) +dnl Supply --build-id=sha1 to ld if building modules. +dnl This suppresses warnings from ld on some systems +AC_DEFUN([grub_PROG_LD_BUILD_ID_SHA1], +[AC_MSG_CHECKING([whether linker accepts --build-id=sha1]) +AC_CACHE_VAL(grub_cv_prog_ld_build_id_sha1, +[save_LDFLAGS="$LDFLAGS" +LDFLAGS="$LDFLAGS -Wl,--build-id=sha1" +AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_prog_ld_build_id_sha1=yes], + [grub_cv_prog_ld_build_id_sha1=no]) +LDFLAGS="$save_LDFLAGS" +]) +AC_MSG_RESULT([$grub_cv_prog_ld_build_id_sha1]) + +if test "x$grub_cv_prog_ld_build_id_sha1" = xyes; then + TARGET_LDFLAGS="$TARGET_LDFLAGS -Wl,--build-id=sha1" +fi +]) + dnl Check nm AC_DEFUN([grub_PROG_NM_WORKS], [AC_MSG_CHECKING([whether nm works]) diff --git a/configure.ac b/configure.ac index 0d0e6782a1..302300711f 100644 --- a/configure.ac +++ b/configure.ac @@ -1442,7 +1442,15 @@ grub_PROG_TARGET_CC if test "x$TARGET_APPLE_LINKER" != x1 ; then grub_PROG_OBJCOPY_ABSOLUTE fi + +AC_ARG_ENABLE([build-id], + [AS_HELP_STRING([--enable-build-id], + [ask the linker to supply build-id notes (default=no)])]) +if test x$enable_build_id = xyes; then +grub_PROG_LD_BUILD_ID_SHA1 +else grub_PROG_LD_BUILD_ID_NONE +fi if test "x$target_cpu" = xi386; then if test "$platform" != emu && test "x$TARGET_APPLE_LINKER" != x1 ; then if test ! -z "$TARGET_IMG_LDSCRIPT"; then From 93803e83135e074ed5a3e7f67af22538896dbefe Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Sun, 28 Jun 2015 13:09:58 -0400 Subject: [PATCH 065/367] Add grub_qdprintf() - grub_dprintf() without the file+line number. This just makes copy+paste of our debug loading info easier. Signed-off-by: Peter Jones --- grub-core/kern/misc.c | 18 ++++++++++++++++++ include/grub/misc.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index a432a6be54..9a2fae6398 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -191,6 +191,24 @@ grub_real_dprintf (const char *file, const int line, const char *condition, } } +void +grub_qdprintf (const char *condition, const char *fmt, ...) +{ + va_list args; + const char *debug = grub_env_get ("debug"); + + if (! debug) + return; + + if (grub_strword (debug, "all") || grub_strword (debug, condition)) + { + va_start (args, fmt); + grub_vprintf (fmt, args); + va_end (args); + grub_refresh (); + } +} + #define PREALLOC_SIZE 255 int diff --git a/include/grub/misc.h b/include/grub/misc.h index fd18e6320b..3adc4036e3 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -345,6 +345,8 @@ void EXPORT_FUNC(grub_real_dprintf) (const char *file, const int line, const char *condition, const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 4, 5))); +void EXPORT_FUNC(grub_qdprintf) (const char *condition, + const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 2, 3))); int EXPORT_FUNC(grub_vprintf) (const char *fmt, va_list args); int EXPORT_FUNC(grub_snprintf) (char *str, grub_size_t n, const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 3, 4))); From c314270222b4adffa16840e3ca764d793e93a0e8 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 25 Jun 2015 15:11:36 -0400 Subject: [PATCH 066/367] Make a "gdb" dprintf that tells us load addresses. This makes a grub_dprintf() call during platform init and during module loading that tells us the virtual addresses of the .text and .data sections of grub-core/kernel.exec and any modules it loads. Specifically, it displays them in the gdb "add-symbol-file" syntax, with the presumption that there's a variable $grubdir that reflects the path to any such binaries. Signed-off-by: Peter Jones --- grub-core/kern/dl.c | 50 +++++++++++++++++++++++++++++++++++++++ grub-core/kern/efi/efi.c | 4 ++-- grub-core/kern/efi/init.c | 26 +++++++++++++++++++- include/grub/efi/efi.h | 2 +- 4 files changed, 78 insertions(+), 4 deletions(-) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index 88d2077709..9557254035 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -501,6 +501,23 @@ grub_dl_find_section (Elf_Ehdr *e, const char *name) return s; return NULL; } +static long +grub_dl_find_section_index (Elf_Ehdr *e, const char *name) +{ + Elf_Shdr *s; + const char *str; + unsigned i; + + s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize); + str = (char *) e + s->sh_offset; + + for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); + i < e->e_shnum; + i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) + if (grub_strcmp (str + s->sh_name, name) == 0) + return (long)i; + return -1; +} /* Me, Vladimir Serbinenko, hereby I add this module check as per new GNU module policy. Note that this license check is informative only. @@ -653,6 +670,37 @@ grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr) return GRUB_ERR_NONE; } +static void +grub_dl_print_gdb_info (grub_dl_t mod, Elf_Ehdr *e) +{ + void *text, *data = NULL; + long idx; + + idx = grub_dl_find_section_index (e, ".text"); + if (idx < 0) + return; + + text = grub_dl_get_section_addr (mod, idx); + if (!text) + return; + + idx = grub_dl_find_section_index (e, ".data"); + if (idx >= 0) + data = grub_dl_get_section_addr (mod, idx); + + if (data) + grub_qdprintf ("gdb", "add-symbol-file \\\n" + "/usr/lib/debug/usr/lib/grub/%s-%s/%s.debug " + "\\\n %p -s .data %p\n", + GRUB_TARGET_CPU, GRUB_PLATFORM, + mod->name, text, data); + else + grub_qdprintf ("gdb", "add-symbol-file \\\n" + "/usr/lib/debug/usr/lib/grub/%s-%s/%s.debug " + "\\\n%p\n", + GRUB_TARGET_CPU, GRUB_PLATFORM, + mod->name, text); +} /* Load a module from core memory. */ grub_dl_t @@ -712,6 +760,8 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size) grub_dprintf ("modules", "module name: %s\n", mod->name); grub_dprintf ("modules", "init function: %p\n", mod->init); + grub_dl_print_gdb_info (mod, e); + if (grub_dl_add (mod)) { grub_dl_unload (mod); diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index ae9885edb8..d6a2fb5778 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -296,7 +296,7 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid, /* Search the mods section from the PE32/PE32+ image. This code uses a PE32 header, but should work with PE32+ as well. */ grub_addr_t -grub_efi_modules_addr (void) +grub_efi_section_addr (const char *section_name) { grub_efi_loaded_image_t *image; struct grub_pe32_header *header; @@ -321,7 +321,7 @@ grub_efi_modules_addr (void) i < coff_header->num_sections; i++, section++) { - if (grub_strcmp (section->name, "mods") == 0) + if (grub_strcmp (section->name, section_name) == 0) break; } diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c index 6d39bd3ad2..2d12e6188f 100644 --- a/grub-core/kern/efi/init.c +++ b/grub-core/kern/efi/init.c @@ -115,10 +115,33 @@ grub_efi_env_init (void) grub_free (envblk_s.buf); } +static void +grub_efi_print_gdb_info (void) +{ + grub_addr_t text; + grub_addr_t data; + + text = grub_efi_section_addr (".text"); + if (!text) + return; + + data = grub_efi_section_addr (".data"); + if (data) + grub_qdprintf ("gdb", + "add-symbol-file /usr/lib/debug/usr/lib/grub/%s-%s/" + "kernel.exec %p -s .data %p\n", + GRUB_TARGET_CPU, GRUB_PLATFORM, (void *)text, (void *)data); + else + grub_qdprintf ("gdb", + "add-symbol-file /usr/lib/debug/usr/lib/grub/%s-%s/" + "kernel.exec %p\n", + GRUB_TARGET_CPU, GRUB_PLATFORM, (void *)text); +} + void grub_efi_init (void) { - grub_modbase = grub_efi_modules_addr (); + grub_modbase = grub_efi_section_addr ("mods"); /* First of all, initialize the console so that GRUB can display messages. */ grub_console_init (); @@ -142,6 +165,7 @@ grub_efi_init (void) 0, 0, 0, NULL); grub_efi_env_init (); + grub_efi_print_gdb_info (); grub_efidisk_init (); } diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index 03f9a9d011..2e0691454b 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -138,7 +138,7 @@ grub_err_t grub_arch_efi_linux_check_image(struct linux_arch_kernel_header *lh); grub_err_t grub_arch_efi_linux_boot_image(grub_addr_t addr, char *args); #endif -grub_addr_t grub_efi_modules_addr (void); +grub_addr_t grub_efi_section_addr (const char *section); void grub_efi_mm_init (void); void grub_efi_mm_fini (void); From 5b1c80f140656c4c77da9b6e2ec209e82f32f2f1 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 10 May 2018 13:40:19 -0400 Subject: [PATCH 067/367] Fixup for newer compiler --- grub-core/fs/btrfs.c | 2 +- include/grub/gpt_partition.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 2b21cbaa67..4cc86e9b79 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -218,7 +218,7 @@ struct grub_btrfs_inode grub_uint64_t size; grub_uint8_t dummy2[0x70]; struct grub_btrfs_time mtime; -} GRUB_PACKED; +} GRUB_PACKED __attribute__ ((aligned(8))); struct grub_btrfs_extent_data { diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index 7a93f43291..8212697bf6 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -76,7 +76,7 @@ struct grub_gpt_partentry grub_uint64_t end; grub_uint64_t attrib; char name[72]; -} GRUB_PACKED; +} GRUB_PACKED __attribute__ ((aligned(8))); grub_err_t grub_gpt_partition_map_iterate (grub_disk_t disk, From 92947fd4fae177b390e98d1a71b832ec6645fb0b Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Sat, 12 May 2018 11:29:07 +0200 Subject: [PATCH 068/367] Don't attempt to export the start and _start symbols for grub-emu Commit 318ee04aadc ("make better backtraces") reworked the backtrace logic but the changes lead to the following build error on the grub-emu platform: grub_emu_lite-symlist.o:(.data+0xf08): undefined reference to `start' collect2: error: ld returned 1 exit status make[3]: *** [Makefile:25959: grub-emu-lite] Error 1 make[3]: *** Waiting for unfinished jobs.... cat kernel_syms.input | grep -v '^#' | sed -n \ -e '/EXPORT_FUNC *([a-zA-Z0-9_]*)/{s/.*EXPORT_FUNC *(\([a-zA-Z0-9_]*\)).*/defined kernel '""'\1/;p;}' \ -e '/EXPORT_VAR *([a-zA-Z0-9_]*)/{s/.*EXPORT_VAR *(\([a-zA-Z0-9_]*\)).*/defined kernel '""'\1/;p;}' \ | sort -u >kernel_syms.lst The problem is that start and _start symbols are exported unconditionally, but these aren't defined for grub-emu since is an emultaed platform so it doesn't have a startup logic. Don't attempt to export those for grub-emu. Signed-off-by: Javier Martinez Canillas --- include/grub/kernel.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/grub/kernel.h b/include/grub/kernel.h index 300a9766cd..55849777ea 100644 --- a/include/grub/kernel.h +++ b/include/grub/kernel.h @@ -111,8 +111,10 @@ grub_addr_t grub_modules_get_end (void); #endif +#if !defined(GRUB_MACHINE_EMU) void EXPORT_FUNC(start) (void); void EXPORT_FUNC(_start) (void); +#endif /* The start point of the C code. */ void grub_main (void) __attribute__ ((noreturn)); From 2fadd7b3a34d8f9ce614fa2cd394872be7ca5c4a Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 10 May 2018 13:40:19 -0400 Subject: [PATCH 069/367] Fixup for newer compiler --- conf/Makefile.common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/Makefile.common b/conf/Makefile.common index 191b1a70c6..5f0ef96985 100644 --- a/conf/Makefile.common +++ b/conf/Makefile.common @@ -38,7 +38,7 @@ CFLAGS_KERNEL = $(CFLAGS_PLATFORM) -ffreestanding LDFLAGS_KERNEL = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) CPPFLAGS_KERNEL = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) -DGRUB_KERNEL=1 CCASFLAGS_KERNEL = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM) -STRIPFLAGS_KERNEL = -R .eh_frame -R .rel.dyn -R .reginfo -R .note -R .comment -R .drectve -R .note.gnu.gold-version -R .MIPS.abiflags -R .ARM.exidx +STRIPFLAGS_KERNEL = -R .eh_frame -R .rel.dyn -R .reginfo -R .note -R .comment -R .drectve -R .note.gnu.gold-version -R .MIPS.abiflags -R .ARM.exidx -R .note.gnu.property -R .gnu.build.attributes CFLAGS_MODULE = $(CFLAGS_PLATFORM) -ffreestanding LDFLAGS_MODULE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-r,-d From 5b5944960b1826008754a0e79ffa98ca978a6d2f Mon Sep 17 00:00:00 2001 From: Andrzej Kacprowski Date: Wed, 10 Jul 2019 15:22:29 +0200 Subject: [PATCH 070/367] Add support for non-Ethernet network cards This patch replaces fixed 6-byte link layer address with up to 32-byte variable sized address. This allows supporting Infiniband and Omni-Path fabric which use 20-byte address, but other network card types can also take advantage of this change. The network card driver is responsible for replacing L2 header provided by grub2 if needed. This approach is compatible with UEFI network stack which also allows up to 32-byte variable size link address. The BOOTP/DHCP packet format is limited to 16 byte client hardware address, if link address is more that 16-bytes then chaddr field in BOOTP it will be set to 0 as per rfc4390. Resolves: rhbz#1370642 Signed-off-by: Andrzej Kacprowski [msalter: Fix max string calculation in grub_net_hwaddr_to_str] Signed-off-by: Mark Salter --- grub-core/net/arp.c | 157 ++++++++++++++++--------- grub-core/net/bootp.c | 15 +-- grub-core/net/drivers/efi/efinet.c | 8 +- grub-core/net/drivers/emu/emunet.c | 1 + grub-core/net/drivers/i386/pc/pxe.c | 13 +- grub-core/net/drivers/ieee1275/ofnet.c | 2 + grub-core/net/drivers/uboot/ubootnet.c | 1 + grub-core/net/ethernet.c | 88 +++++++------- grub-core/net/icmp6.c | 15 ++- grub-core/net/ip.c | 4 +- grub-core/net/net.c | 50 ++++---- include/grub/net.h | 19 +-- 12 files changed, 220 insertions(+), 153 deletions(-) diff --git a/grub-core/net/arp.c b/grub-core/net/arp.c index 54306e3b16..67b409a8ac 100644 --- a/grub-core/net/arp.c +++ b/grub-core/net/arp.c @@ -31,22 +31,12 @@ enum ARP_REPLY = 2 }; -enum - { - /* IANA ARP constant to define hardware type as ethernet. */ - GRUB_NET_ARPHRD_ETHERNET = 1 - }; - -struct arppkt { +struct arphdr { grub_uint16_t hrd; grub_uint16_t pro; grub_uint8_t hln; grub_uint8_t pln; grub_uint16_t op; - grub_uint8_t sender_mac[6]; - grub_uint32_t sender_ip; - grub_uint8_t recv_mac[6]; - grub_uint32_t recv_ip; } GRUB_PACKED; static int have_pending; @@ -57,12 +47,16 @@ grub_net_arp_send_request (struct grub_net_network_level_interface *inf, const grub_net_network_level_address_t *proto_addr) { struct grub_net_buff nb; - struct arppkt *arp_packet; + struct arphdr *arp_header; grub_net_link_level_address_t target_mac_addr; grub_err_t err; int i; grub_uint8_t *nbd; grub_uint8_t arp_data[128]; + grub_uint8_t hln; + grub_uint8_t pln; + grub_uint8_t arp_packet_len; + grub_uint8_t *tmp_ptr; if (proto_addr->type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) return grub_error (GRUB_ERR_BUG, "unsupported address family"); @@ -73,23 +67,39 @@ grub_net_arp_send_request (struct grub_net_network_level_interface *inf, grub_netbuff_clear (&nb); grub_netbuff_reserve (&nb, 128); - err = grub_netbuff_push (&nb, sizeof (*arp_packet)); + hln = inf->card->default_address.len; + pln = sizeof (proto_addr->ipv4); + arp_packet_len = sizeof (*arp_header) + 2 * (hln + pln); + + err = grub_netbuff_push (&nb, arp_packet_len); if (err) return err; - arp_packet = (struct arppkt *) nb.data; - arp_packet->hrd = grub_cpu_to_be16_compile_time (GRUB_NET_ARPHRD_ETHERNET); - arp_packet->hln = 6; - arp_packet->pro = grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP); - arp_packet->pln = 4; - arp_packet->op = grub_cpu_to_be16_compile_time (ARP_REQUEST); - /* Sender hardware address. */ - grub_memcpy (arp_packet->sender_mac, &inf->hwaddress.mac, 6); - arp_packet->sender_ip = inf->address.ipv4; - grub_memset (arp_packet->recv_mac, 0, 6); - arp_packet->recv_ip = proto_addr->ipv4; - /* Target protocol address */ - grub_memset (&target_mac_addr.mac, 0xff, 6); + arp_header = (struct arphdr *) nb.data; + arp_header->hrd = grub_cpu_to_be16 (inf->card->default_address.type); + arp_header->hln = hln; + arp_header->pro = grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP); + arp_header->pln = pln; + arp_header->op = grub_cpu_to_be16_compile_time (ARP_REQUEST); + tmp_ptr = nb.data + sizeof (*arp_header); + + /* The source hardware address. */ + grub_memcpy (tmp_ptr, inf->hwaddress.mac, hln); + tmp_ptr += hln; + + /* The source protocol address. */ + grub_memcpy (tmp_ptr, &inf->address.ipv4, pln); + tmp_ptr += pln; + + /* The target hardware address. */ + grub_memset (tmp_ptr, 0, hln); + tmp_ptr += hln; + + /* The target protocol address */ + grub_memcpy (tmp_ptr, &proto_addr->ipv4, pln); + tmp_ptr += pln; + + grub_memset (&target_mac_addr.mac, 0xff, hln); nbd = nb.data; send_ethernet_packet (inf, &nb, target_mac_addr, GRUB_NET_ETHERTYPE_ARP); @@ -114,28 +124,53 @@ grub_err_t grub_net_arp_receive (struct grub_net_buff *nb, struct grub_net_card *card, grub_uint16_t *vlantag) { - struct arppkt *arp_packet = (struct arppkt *) nb->data; + struct arphdr *arp_header = (struct arphdr *) nb->data; grub_net_network_level_address_t sender_addr, target_addr; grub_net_link_level_address_t sender_mac_addr; struct grub_net_network_level_interface *inf; - - if (arp_packet->pro != grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP) - || arp_packet->pln != 4 || arp_packet->hln != 6 - || nb->tail - nb->data < (int) sizeof (*arp_packet)) + grub_uint16_t hw_type; + grub_uint8_t hln; + grub_uint8_t pln; + grub_uint8_t arp_packet_len; + grub_uint8_t *tmp_ptr; + + hw_type = card->default_address.type; + hln = card->default_address.len; + pln = sizeof(sender_addr.ipv4); + arp_packet_len = sizeof (*arp_header) + 2 * (pln + hln); + + if (arp_header->pro != grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP) + || arp_header->hrd != grub_cpu_to_be16 (hw_type) + || arp_header->hln != hln || arp_header->pln != pln + || nb->tail - nb->data < (int) arp_packet_len) { return GRUB_ERR_NONE; + } + tmp_ptr = nb->data + sizeof (*arp_header); + + /* The source hardware address. */ + sender_mac_addr.type = hw_type; + sender_mac_addr.len = hln; + grub_memcpy (sender_mac_addr.mac, tmp_ptr, hln); + tmp_ptr += hln; + + /* The source protocol address. */ sender_addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - target_addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - sender_addr.ipv4 = arp_packet->sender_ip; - target_addr.ipv4 = arp_packet->recv_ip; - if (arp_packet->sender_ip == pending_req) - have_pending = 1; + grub_memcpy(&sender_addr.ipv4, tmp_ptr, pln); + tmp_ptr += pln; - sender_mac_addr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memcpy (sender_mac_addr.mac, arp_packet->sender_mac, - sizeof (sender_mac_addr.mac)); grub_net_link_layer_add_address (card, &sender_addr, &sender_mac_addr, 1); + /* The target hardware address. */ + tmp_ptr += hln; + + /* The target protocol address. */ + target_addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + grub_memcpy(&target_addr.ipv4, tmp_ptr, pln); + + if (sender_addr.ipv4 == pending_req) + have_pending = 1; + FOR_NET_NETWORK_LEVEL_INTERFACES (inf) { /* Verify vlantag id */ @@ -148,11 +183,11 @@ grub_net_arp_receive (struct grub_net_buff *nb, struct grub_net_card *card, /* Am I the protocol address target? */ if (grub_net_addr_cmp (&inf->address, &target_addr) == 0 - && arp_packet->op == grub_cpu_to_be16_compile_time (ARP_REQUEST)) + && arp_header->op == grub_cpu_to_be16_compile_time (ARP_REQUEST)) { grub_net_link_level_address_t target; struct grub_net_buff nb_reply; - struct arppkt *arp_reply; + struct arphdr *arp_reply; grub_uint8_t arp_data[128]; grub_err_t err; @@ -161,25 +196,39 @@ grub_net_arp_receive (struct grub_net_buff *nb, struct grub_net_card *card, grub_netbuff_clear (&nb_reply); grub_netbuff_reserve (&nb_reply, 128); - err = grub_netbuff_push (&nb_reply, sizeof (*arp_packet)); + err = grub_netbuff_push (&nb_reply, arp_packet_len); if (err) return err; - arp_reply = (struct arppkt *) nb_reply.data; + arp_reply = (struct arphdr *) nb_reply.data; - arp_reply->hrd = grub_cpu_to_be16_compile_time (GRUB_NET_ARPHRD_ETHERNET); + arp_reply->hrd = grub_cpu_to_be16 (hw_type); arp_reply->pro = grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP); - arp_reply->pln = 4; - arp_reply->hln = 6; + arp_reply->pln = pln; + arp_reply->hln = hln; arp_reply->op = grub_cpu_to_be16_compile_time (ARP_REPLY); - arp_reply->sender_ip = arp_packet->recv_ip; - arp_reply->recv_ip = arp_packet->sender_ip; - arp_reply->hln = 6; - - target.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memcpy (target.mac, arp_packet->sender_mac, 6); - grub_memcpy (arp_reply->sender_mac, inf->hwaddress.mac, 6); - grub_memcpy (arp_reply->recv_mac, arp_packet->sender_mac, 6); + + tmp_ptr = nb_reply.data + sizeof (*arp_reply); + + /* The source hardware address. */ + grub_memcpy (tmp_ptr, inf->hwaddress.mac, hln); + tmp_ptr += hln; + + /* The source protocol address. */ + grub_memcpy (tmp_ptr, &target_addr.ipv4, pln); + tmp_ptr += pln; + + /* The target hardware address. */ + grub_memcpy (tmp_ptr, sender_mac_addr.mac, hln); + tmp_ptr += hln; + + /* The target protocol address */ + grub_memcpy (tmp_ptr, &sender_addr.ipv4, pln); + tmp_ptr += pln; + + target.type = hw_type; + target.len = hln; + grub_memcpy (target.mac, sender_mac_addr.mac, hln); /* Change operation to REPLY and send packet */ send_ethernet_packet (inf, &nb_reply, target, GRUB_NET_ETHERTYPE_ARP); diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index e28fb6a09f..08b6b2b5d6 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -233,7 +233,6 @@ grub_net_configure_by_dhcp_ack (const char *name, int is_def, char **device, char **path) { grub_net_network_level_address_t addr; - grub_net_link_level_address_t hwaddr; struct grub_net_network_level_interface *inter; int mask = -1; char server_ip[sizeof ("xxx.xxx.xxx.xxx")]; @@ -250,12 +249,8 @@ grub_net_configure_by_dhcp_ack (const char *name, if (path) *path = 0; - grub_memcpy (hwaddr.mac, bp->mac_addr, - bp->hw_len < sizeof (hwaddr.mac) ? bp->hw_len - : sizeof (hwaddr.mac)); - hwaddr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - - inter = grub_net_add_addr (name, card, &addr, &hwaddr, flags); + grub_dprintf("dhcp", "configuring dhcp for %s\n", name); + inter = grub_net_add_addr (name, card, &addr, &card->default_address, flags); if (!inter) return 0; @@ -567,7 +562,9 @@ send_dhcp_packet (struct grub_net_network_level_interface *iface) grub_memset (pack, 0, sizeof (*pack)); pack->opcode = 1; pack->hw_type = 1; - pack->hw_len = 6; + pack->hw_len = iface->hwaddress.len > 16 ? 0 + : iface->hwaddress.len; + err = grub_get_datetime (&date); if (err || !grub_datetime2unixtime (&date, &t)) { @@ -580,7 +577,7 @@ send_dhcp_packet (struct grub_net_network_level_interface *iface) else pack->ident = iface->xid; - grub_memcpy (&pack->mac_addr, &iface->hwaddress.mac, 6); + grub_memcpy (&pack->mac_addr, &iface->hwaddress.mac, pack->hw_len); grub_netbuff_push (nb, sizeof (*udph)); diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c index 173fb63153..a673bea807 100644 --- a/grub-core/net/drivers/efi/efinet.c +++ b/grub-core/net/drivers/efi/efinet.c @@ -279,6 +279,9 @@ grub_efinet_findcards (void) /* This should not happen... Why? */ continue; + if (net->mode->hwaddr_size > GRUB_NET_MAX_LINK_ADDRESS_SIZE) + continue; + if (net->mode->state == GRUB_EFI_NETWORK_STOPPED && efi_call_1 (net->start, net) != GRUB_EFI_SUCCESS) continue; @@ -315,10 +318,11 @@ grub_efinet_findcards (void) card->name = grub_xasprintf ("efinet%d", i++); card->driver = &efidriver; card->flags = 0; - card->default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + card->default_address.type = net->mode->if_type; + card->default_address.len = net->mode->hwaddr_size; grub_memcpy (card->default_address.mac, net->mode->current_address, - sizeof (card->default_address.mac)); + net->mode->hwaddr_size); card->efi_net = net; card->efi_handle = *handle; diff --git a/grub-core/net/drivers/emu/emunet.c b/grub-core/net/drivers/emu/emunet.c index b194920861..5b6c5e16a6 100644 --- a/grub-core/net/drivers/emu/emunet.c +++ b/grub-core/net/drivers/emu/emunet.c @@ -46,6 +46,7 @@ static struct grub_net_card emucard = .mtu = 1500, .default_address = { .type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET, + . len = 6, {.mac = {0, 1, 2, 3, 4, 5}} }, .flags = 0 diff --git a/grub-core/net/drivers/i386/pc/pxe.c b/grub-core/net/drivers/i386/pc/pxe.c index 3f4152d036..9f8fb4b6d2 100644 --- a/grub-core/net/drivers/i386/pc/pxe.c +++ b/grub-core/net/drivers/i386/pc/pxe.c @@ -386,20 +386,21 @@ GRUB_MOD_INIT(pxe) grub_memset (ui, 0, sizeof (*ui)); grub_pxe_call (GRUB_PXENV_UNDI_GET_INFORMATION, ui, pxe_rm_entry); + grub_pxe_card.default_address.len = 6; grub_memcpy (grub_pxe_card.default_address.mac, ui->current_addr, - sizeof (grub_pxe_card.default_address.mac)); - for (i = 0; i < sizeof (grub_pxe_card.default_address.mac); i++) + grub_pxe_card.default_address.len); + for (i = 0; i < grub_pxe_card.default_address.len; i++) if (grub_pxe_card.default_address.mac[i] != 0) break; - if (i != sizeof (grub_pxe_card.default_address.mac)) + if (i != grub_pxe_card.default_address.len) { - for (i = 0; i < sizeof (grub_pxe_card.default_address.mac); i++) + for (i = 0; i < grub_pxe_card.default_address.len; i++) if (grub_pxe_card.default_address.mac[i] != 0xff) break; } - if (i == sizeof (grub_pxe_card.default_address.mac)) + if (i == grub_pxe_card.default_address.len) grub_memcpy (grub_pxe_card.default_address.mac, ui->permanent_addr, - sizeof (grub_pxe_card.default_address.mac)); + grub_pxe_card.default_address.len); grub_pxe_card.mtu = ui->mtu; grub_pxe_card.default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; diff --git a/grub-core/net/drivers/ieee1275/ofnet.c b/grub-core/net/drivers/ieee1275/ofnet.c index 3860b6f78d..bcb3f9ea02 100644 --- a/grub-core/net/drivers/ieee1275/ofnet.c +++ b/grub-core/net/drivers/ieee1275/ofnet.c @@ -160,6 +160,7 @@ grub_ieee1275_parse_bootpath (const char *devpath, char *bootpath, grub_uint16_t vlantag = 0; hw_addr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + hw_addr.len = 6; args = bootpath + grub_strlen (devpath) + 1; do @@ -503,6 +504,7 @@ search_net_devices (struct grub_ieee1275_devalias *alias) grub_memcpy (&lla.mac, pprop, 6); lla.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + lla.len = 6; card->default_address = lla; card->txbufsize = ALIGN_UP (card->mtu, 64) + 256; diff --git a/grub-core/net/drivers/uboot/ubootnet.c b/grub-core/net/drivers/uboot/ubootnet.c index 056052e40d..22ebcbf211 100644 --- a/grub-core/net/drivers/uboot/ubootnet.c +++ b/grub-core/net/drivers/uboot/ubootnet.c @@ -131,6 +131,7 @@ GRUB_MOD_INIT (ubootnet) grub_memcpy (&(card->default_address.mac), &devinfo->di_net.hwaddr, 6); card->default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + card->default_address.len = 6; card->txbufsize = ALIGN_UP (card->mtu, 64) + 256; card->txbuf = grub_zalloc (card->txbufsize); diff --git a/grub-core/net/ethernet.c b/grub-core/net/ethernet.c index 4d7ceed6f9..9aae83a5eb 100644 --- a/grub-core/net/ethernet.c +++ b/grub-core/net/ethernet.c @@ -29,13 +29,6 @@ #define LLCADDRMASK 0x7f -struct etherhdr -{ - grub_uint8_t dst[6]; - grub_uint8_t src[6]; - grub_uint16_t type; -} GRUB_PACKED; - struct llchdr { grub_uint8_t dsap; @@ -55,13 +48,15 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf, grub_net_link_level_address_t target_addr, grub_net_ethertype_t ethertype) { - struct etherhdr *eth; + grub_uint8_t *eth; grub_err_t err; - grub_uint8_t etherhdr_size; - grub_uint16_t vlantag_id = VLANTAG_IDENTIFIER; + grub_uint32_t vlantag = 0; + grub_uint8_t hw_addr_len = inf->card->default_address.len; + grub_uint8_t etherhdr_size = 2 * hw_addr_len + 2; - etherhdr_size = sizeof (*eth); - COMPILE_TIME_ASSERT (sizeof (*eth) + 4 < GRUB_NET_MAX_LINK_HEADER_SIZE); + /* Source and destination link addresses + ethertype + vlan tag */ + COMPILE_TIME_ASSERT ((GRUB_NET_MAX_LINK_ADDRESS_SIZE * 2 + 2 + 4) < + GRUB_NET_MAX_LINK_HEADER_SIZE); /* Increase ethernet header in case of vlantag */ if (inf->vlantag != 0) @@ -70,11 +65,22 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf, err = grub_netbuff_push (nb, etherhdr_size); if (err) return err; - eth = (struct etherhdr *) nb->data; - grub_memcpy (eth->dst, target_addr.mac, 6); - grub_memcpy (eth->src, inf->hwaddress.mac, 6); + eth = nb->data; + grub_memcpy (eth, target_addr.mac, hw_addr_len); + eth += hw_addr_len; + grub_memcpy (eth, inf->hwaddress.mac, hw_addr_len); + eth += hw_addr_len; + + /* Check if a vlan-tag is present. */ + if (vlantag != 0) + { + *((grub_uint32_t *)eth) = grub_cpu_to_be32 (vlantag); + eth += sizeof (vlantag); + } + + /* Write ethertype */ + *((grub_uint16_t*) eth) = grub_cpu_to_be16 (ethertype); - eth->type = grub_cpu_to_be16 (ethertype); if (!inf->card->opened) { err = GRUB_ERR_NONE; @@ -85,18 +91,6 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf, inf->card->opened = 1; } - /* Check and add a vlan-tag if needed. */ - if (inf->vlantag != 0) - { - /* Move eth type to the right */ - grub_memcpy ((char *) nb->data + etherhdr_size - 2, - (char *) nb->data + etherhdr_size - 6, 2); - - /* Add the tag in the middle */ - grub_memcpy ((char *) nb->data + etherhdr_size - 6, &vlantag_id, 2); - grub_memcpy ((char *) nb->data + etherhdr_size - 4, (char *) &(inf->vlantag), 2); - } - return inf->card->driver->send (inf->card, nb); } @@ -104,31 +98,40 @@ grub_err_t grub_net_recv_ethernet_packet (struct grub_net_buff *nb, struct grub_net_card *card) { - struct etherhdr *eth; + grub_uint8_t *eth; struct llchdr *llch; struct snaphdr *snaph; grub_net_ethertype_t type; grub_net_link_level_address_t hwaddress; grub_net_link_level_address_t src_hwaddress; grub_err_t err; - grub_uint8_t etherhdr_size = sizeof (*eth); + grub_uint8_t hw_addr_len = card->default_address.len; + grub_uint8_t etherhdr_size = 2 * hw_addr_len + 2; grub_uint16_t vlantag = 0; + eth = nb->data; - /* Check if a vlan-tag is present. If so, the ethernet header is 4 bytes */ - /* longer than the original one. The vlantag id is extracted and the header */ - /* is reseted to the original size. */ - if (grub_get_unaligned16 (nb->data + etherhdr_size - 2) == VLANTAG_IDENTIFIER) + hwaddress.type = card->default_address.type; + hwaddress.len = hw_addr_len; + grub_memcpy (hwaddress.mac, eth, hw_addr_len); + eth += hw_addr_len; + + src_hwaddress.type = card->default_address.type; + src_hwaddress.len = hw_addr_len; + grub_memcpy (src_hwaddress.mac, eth, hw_addr_len); + eth += hw_addr_len; + + type = grub_be_to_cpu16 (*(grub_uint16_t*)(eth)); + if (type == VLANTAG_IDENTIFIER) { - vlantag = grub_get_unaligned16 (nb->data + etherhdr_size); + /* Skip vlan tag */ + eth += 2; + vlantag = grub_be_to_cpu16 (*(grub_uint16_t*)(eth)); etherhdr_size += 4; - /* Move eth type to the original position */ - grub_memcpy((char *) nb->data + etherhdr_size - 6, - (char *) nb->data + etherhdr_size - 2, 2); + eth += 2; + type = grub_be_to_cpu16 (*(grub_uint16_t*)(eth)); } - eth = (struct etherhdr *) nb->data; - type = grub_be_to_cpu16 (eth->type); err = grub_netbuff_pull (nb, etherhdr_size); if (err) return err; @@ -148,11 +151,6 @@ grub_net_recv_ethernet_packet (struct grub_net_buff *nb, } } - hwaddress.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memcpy (hwaddress.mac, eth->dst, sizeof (hwaddress.mac)); - src_hwaddress.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memcpy (src_hwaddress.mac, eth->src, sizeof (src_hwaddress.mac)); - switch (type) { /* ARP packet. */ diff --git a/grub-core/net/icmp6.c b/grub-core/net/icmp6.c index 2cbd95dce2..56a3ec5c8e 100644 --- a/grub-core/net/icmp6.c +++ b/grub-core/net/icmp6.c @@ -231,8 +231,9 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, && ohdr->len == 1) { grub_net_link_level_address_t ll_address; - ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac)); + ll_address.type = card->default_address.type; + ll_address.len = card->default_address.len; + grub_memcpy (ll_address.mac, ohdr + 1, ll_address.len); grub_net_link_layer_add_address (card, source, &ll_address, 0); } } @@ -335,8 +336,9 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, && ohdr->len == 1) { grub_net_link_level_address_t ll_address; - ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac)); + ll_address.type = card->default_address.type; + ll_address.len = card->default_address.len; + grub_memcpy (ll_address.mac, ohdr + 1, ll_address.len); grub_net_link_layer_add_address (card, source, &ll_address, 0); } } @@ -384,8 +386,9 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, && ohdr->len == 1) { grub_net_link_level_address_t ll_address; - ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac)); + ll_address.type = card->default_address.type; + ll_address.len = card->default_address.len; + grub_memcpy (ll_address.mac, ohdr + 1, ll_address.len); grub_net_link_layer_add_address (card, source, &ll_address, 0); } if (ohdr->type == OPTION_PREFIX && ohdr->len == 4) diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index ea5edf8f1f..a5896f6dc2 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -276,8 +276,8 @@ handle_dgram (struct grub_net_buff *nb, if (inf->card == card && inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV && inf->hwaddress.type == GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET - && grub_memcmp (inf->hwaddress.mac, &bootp->mac_addr, - sizeof (inf->hwaddress.mac)) == 0) + && (grub_memcmp (inf->hwaddress.mac, &bootp->mac_addr, + bootp->hw_len) == 0 || bootp->hw_len == 0)) { grub_net_process_dhcp (nb, inf); grub_netbuff_free (nb); diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 22f2689aae..a46f82362e 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -133,8 +133,9 @@ grub_net_link_layer_resolve (struct grub_net_network_level_interface *inf, << 48) && proto_addr->ipv6[1] == (grub_be_to_cpu64_compile_time (1)))) { - hw_addr->type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memset (hw_addr->mac, -1, 6); + hw_addr->type = inf->card->default_address.type; + hw_addr->len = inf->card->default_address.len; + grub_memset (hw_addr->mac, -1, hw_addr->len); return GRUB_ERR_NONE; } @@ -142,6 +143,7 @@ grub_net_link_layer_resolve (struct grub_net_network_level_interface *inf, && ((grub_be_to_cpu64 (proto_addr->ipv6[0]) >> 56) == 0xff)) { hw_addr->type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + hw_addr->len = inf->card->default_address.len; hw_addr->mac[0] = 0x33; hw_addr->mac[1] = 0x33; hw_addr->mac[2] = ((grub_be_to_cpu64 (proto_addr->ipv6[1]) >> 24) & 0xff); @@ -762,23 +764,23 @@ grub_net_addr_to_str (const grub_net_network_level_address_t *target, char *buf) void grub_net_hwaddr_to_str (const grub_net_link_level_address_t *addr, char *str) { - str[0] = 0; - switch (addr->type) + char *ptr; + unsigned i; + int maxstr; + + if (addr->len > GRUB_NET_MAX_LINK_ADDRESS_SIZE) { - case GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET: - { - char *ptr; - unsigned i; - for (ptr = str, i = 0; i < ARRAY_SIZE (addr->mac); i++) - { - grub_snprintf (ptr, GRUB_NET_MAX_STR_HWADDR_LEN - (ptr - str), - "%02x:", addr->mac[i] & 0xff); - ptr += (sizeof ("XX:") - 1); - } - return; - } + str[0] = 0; + grub_printf (_("Unsupported hw address type %d len %d\n"), + addr->type, addr->len); + return; + } + maxstr = addr->len * grub_strlen ("XX:"); + for (ptr = str, i = 0; i < addr->len; i++) + { + ptr += grub_snprintf (ptr, maxstr - (ptr - str), + "%02x:", addr->mac[i] & 0xff); } - grub_printf (_("Unsupported hw address type %d\n"), addr->type); } int @@ -789,13 +791,17 @@ grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a, return -1; if (a->type > b->type) return +1; - switch (a->type) + if (a->len < b->len) + return -1; + if (a->len > b->len) + return +1; + if (a->len > GRUB_NET_MAX_LINK_ADDRESS_SIZE) { - case GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET: - return grub_memcmp (a->mac, b->mac, sizeof (a->mac)); + grub_printf (_("Unsupported hw address type %d len %d\n"), + a->type, a->len); + return + 1; } - grub_printf (_("Unsupported hw address type %d\n"), a->type); - return 1; + return grub_memcmp (a->mac, b->mac, a->len); } int diff --git a/include/grub/net.h b/include/grub/net.h index 8a05ec4fe7..af0404db7e 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -29,7 +29,8 @@ enum { - GRUB_NET_MAX_LINK_HEADER_SIZE = 64, + GRUB_NET_MAX_LINK_HEADER_SIZE = 96, + GRUB_NET_MAX_LINK_ADDRESS_SIZE = 32, GRUB_NET_UDP_HEADER_SIZE = 8, GRUB_NET_TCP_HEADER_SIZE = 20, GRUB_NET_OUR_IPV4_HEADER_SIZE = 20, @@ -42,15 +43,17 @@ enum typedef enum grub_link_level_protocol_id { - GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET + /* IANA ARP constant to define hardware type. */ + GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET = 1, } grub_link_level_protocol_id_t; typedef struct grub_net_link_level_address { grub_link_level_protocol_id_t type; + grub_uint8_t len; union { - grub_uint8_t mac[6]; + grub_uint8_t mac[GRUB_NET_MAX_LINK_ADDRESS_SIZE]; }; } grub_net_link_level_address_t; @@ -566,11 +569,13 @@ grub_net_addr_cmp (const grub_net_network_level_address_t *a, #define GRUB_NET_MAX_STR_ADDR_LEN sizeof ("XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX") /* - Currently suppoerted adresses: - ethernet: XX:XX:XX:XX:XX:XX + Up to 32 byte hardware address supported, see GRUB_NET_MAX_LINK_ADDRESS_SIZE */ - -#define GRUB_NET_MAX_STR_HWADDR_LEN (sizeof ("XX:XX:XX:XX:XX:XX")) +#define GRUB_NET_MAX_STR_HWADDR_LEN (sizeof (\ + "XX:XX:XX:XX:XX:XX:XX:XX:"\ + "XX:XX:XX:XX:XX:XX:XX:XX:"\ + "XX:XX:XX:XX:XX:XX:XX:XX:"\ + "XX:XX:XX:XX:XX:XX:XX:XX")) void grub_net_addr_to_str (const grub_net_network_level_address_t *target, From 862ad2513e941702027d8d3471cb1e2122c9c3ee Mon Sep 17 00:00:00 2001 From: Aaron Miller Date: Fri, 29 Jul 2016 17:41:38 +0800 Subject: [PATCH 071/367] net: read bracketed ipv6 addrs and port numbers Allow specifying port numbers for http and tftp paths, and allow ipv6 addresses to be recognized with brackets around them, which is required to specify a port number Signed-off-by: Aaron Miller [pjones: various bug fixes] Signed-off-by: Peter Jones --- grub-core/net/http.c | 25 ++++++++++--- grub-core/net/net.c | 87 +++++++++++++++++++++++++++++++++++++++++--- grub-core/net/tftp.c | 8 +++- include/grub/net.h | 1 + 4 files changed, 109 insertions(+), 12 deletions(-) diff --git a/grub-core/net/http.c b/grub-core/net/http.c index b616cf40b1..12a2632ea5 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -289,7 +289,9 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), nb2 = grub_netbuff_alloc (data->chunk_rem); if (!nb2) return grub_errno; - grub_netbuff_put (nb2, data->chunk_rem); + err = grub_netbuff_put (nb2, data->chunk_rem); + if (err) + return grub_errno; grub_memcpy (nb2->data, nb->data, data->chunk_rem); if (file->device->net->packs.count >= 20) { @@ -312,12 +314,14 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) int i; struct grub_net_buff *nb; grub_err_t err; + char* server = file->device->net->server; + int port = file->device->net->port; nb = grub_netbuff_alloc (GRUB_NET_TCP_RESERVE_SIZE + sizeof ("GET ") - 1 + grub_strlen (data->filename) + sizeof (" HTTP/1.1\r\nHost: ") - 1 - + grub_strlen (file->device->net->server) + + grub_strlen (server) + sizeof (":XXXXXXXXXX") + sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n") - 1 + sizeof ("Range: bytes=XXXXXXXXXXXXXXXXXXXX" @@ -356,7 +360,7 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) sizeof (" HTTP/1.1\r\nHost: ") - 1); ptr = nb->tail; - err = grub_netbuff_put (nb, grub_strlen (file->device->net->server)); + err = grub_netbuff_put (nb, grub_strlen (server)); if (err) { grub_netbuff_free (nb); @@ -365,6 +369,15 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) grub_memcpy (ptr, file->device->net->server, grub_strlen (file->device->net->server)); + if (port) + { + ptr = nb->tail; + grub_snprintf ((char *) ptr, + sizeof (":XXXXXXXXXX"), + ":%d", + port); + } + ptr = nb->tail; err = grub_netbuff_put (nb, sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n") @@ -390,8 +403,10 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) grub_netbuff_put (nb, 2); grub_memcpy (ptr, "\r\n", 2); - data->sock = grub_net_tcp_open (file->device->net->server, - HTTP_PORT, http_receive, + grub_dprintf ("http", "opening path %s on host %s TCP port %d\n", + data->filename, server, port ? port : HTTP_PORT); + data->sock = grub_net_tcp_open (server, + port ? port : HTTP_PORT, http_receive, http_err, NULL, file); if (!data->sock) diff --git a/grub-core/net/net.c b/grub-core/net/net.c index a46f82362e..0ce5e675ed 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -444,6 +444,13 @@ parse_ip6 (const char *val, grub_uint64_t *ip, const char **rest) grub_uint16_t newip[8]; const char *ptr = val; int word, quaddot = -1; + int bracketed = 0; + + if (ptr[0] == '[') + { + bracketed = 1; + ptr++; + } if (ptr[0] == ':' && ptr[1] != ':') return 0; @@ -482,6 +489,8 @@ parse_ip6 (const char *val, grub_uint64_t *ip, const char **rest) grub_memset (&newip[quaddot], 0, (7 - word) * sizeof (newip[0])); } grub_memcpy (ip, newip, 16); + if (bracketed && *ptr == ']') + ptr++; if (rest) *rest = ptr; return 1; @@ -1343,8 +1352,10 @@ grub_net_open_real (const char *name) { grub_net_app_level_t proto; const char *protname, *server; + char *host; grub_size_t protnamelen; int try; + int port = 0; if (grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) == 0) { @@ -1382,6 +1393,72 @@ grub_net_open_real (const char *name) return NULL; } + char* port_start; + /* ipv6 or port specified? */ + if ((port_start = grub_strchr (server, ':'))) + { + char* ipv6_begin; + if((ipv6_begin = grub_strchr (server, '['))) + { + char* ipv6_end = grub_strchr (server, ']'); + if(!ipv6_end) + { + grub_error (GRUB_ERR_NET_BAD_ADDRESS, + N_("mismatched [ in address")); + return NULL; + } + /* port number after bracketed ipv6 addr */ + if(ipv6_end[1] == ':') + { + port = grub_strtoul (ipv6_end + 2, NULL, 10); + if(port > 65535) + { + grub_error (GRUB_ERR_NET_BAD_ADDRESS, + N_("bad port number")); + return NULL; + } + } + host = grub_strndup (ipv6_begin, (ipv6_end - ipv6_begin) + 1); + } + else + { + if (grub_strchr (port_start + 1, ':')) + { + int iplen = grub_strlen (server); + /* bracket bare ipv6 addrs */ + host = grub_malloc (iplen + 3); + if(!host) + { + return NULL; + } + host[0] = '['; + grub_memcpy (host + 1, server, iplen); + host[iplen + 1] = ']'; + host[iplen + 2] = '\0'; + } + else + { + /* hostname:port or ipv4:port */ + port = grub_strtol (port_start + 1, NULL, 10); + if(port > 65535) + { + grub_error (GRUB_ERR_NET_BAD_ADDRESS, + N_("bad port number")); + return NULL; + } + host = grub_strndup (server, port_start - server); + } + } + } + else + { + host = grub_strdup (server); + } + if (!host) + { + return NULL; + } + for (try = 0; try < 2; try++) { FOR_NET_APP_LEVEL (proto) @@ -1391,14 +1468,13 @@ grub_net_open_real (const char *name) { grub_net_t ret = grub_zalloc (sizeof (*ret)); if (!ret) - return NULL; - ret->protocol = proto; - ret->server = grub_strdup (server); - if (!ret->server) { - grub_free (ret); + grub_free (host); return NULL; } + ret->protocol = proto; + ret->port = port; + ret->server = host; ret->fs = &grub_net_fs; return ret; } @@ -1473,6 +1549,7 @@ grub_net_open_real (const char *name) grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' not found"), name); + grub_free (host); return NULL; } diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index 4ab2f5c735..d54b13f09f 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -295,6 +295,7 @@ tftp_open (struct grub_file *file, const char *filename) grub_err_t err; grub_uint8_t *nbd; grub_net_network_level_address_t addr; + int port = file->device->net->port; data = grub_zalloc (sizeof (*data)); if (!data) @@ -362,14 +363,17 @@ tftp_open (struct grub_file *file, const char *filename) err = grub_net_resolve_address (file->device->net->server, &addr); if (err) { - grub_dprintf("tftp", "Address resolution failed: %d\n", err); + grub_dprintf ("tftp", "Address resolution failed: %d\n", err); + grub_dprintf ("tftp", "file_size is %llu, block_size is %llu\n", + (unsigned long long)data->file_size, + (unsigned long long)data->block_size); grub_free (data); return err; } grub_dprintf("tftp", "opening connection\n"); data->sock = grub_net_udp_open (addr, - TFTP_SERVER_PORT, tftp_receive, + port ? port : TFTP_SERVER_PORT, tftp_receive, file); if (!data->sock) { diff --git a/include/grub/net.h b/include/grub/net.h index af0404db7e..d55d505a03 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -273,6 +273,7 @@ typedef struct grub_net { char *server; char *name; + int port; grub_net_app_level_t protocol; grub_net_packets_t packs; grub_off_t offset; From d54a76e5cf49f898801bef5d769ed1a10a4953d5 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Wed, 10 Jul 2019 15:42:36 +0200 Subject: [PATCH 072/367] bootp: New net_bootp6 command Implement new net_bootp6 command for IPv6 network auto configuration via the DHCPv6 protocol (RFC3315). Signed-off-by: Michael Chang Signed-off-by: Ken Lin [pjones: Put back our code to add a local route] Signed-off-by: Peter Jones --- grub-core/net/bootp.c | 1059 +++++++++++++++++++++++----- grub-core/net/drivers/efi/efinet.c | 20 +- grub-core/net/ip.c | 39 + include/grub/efi/api.h | 2 +- include/grub/net.h | 91 ++- 5 files changed, 1002 insertions(+), 209 deletions(-) diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index 08b6b2b5d6..fe93b80f1c 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -24,6 +24,98 @@ #include #include #include +#include +#include + +static int +dissect_url (const char *url, char **proto, char **host, char **path) +{ + const char *p, *ps; + grub_size_t l; + + *proto = *host = *path = NULL; + ps = p = url; + + while ((p = grub_strchr (p, ':'))) + { + if (grub_strlen (p) < sizeof ("://") - 1) + break; + if (grub_memcmp (p, "://", sizeof ("://") - 1) == 0) + { + l = p - ps; + *proto = grub_malloc (l + 1); + if (!*proto) + { + grub_print_error (); + return 0; + } + + grub_memcpy (*proto, ps, l); + (*proto)[l] = '\0'; + p += sizeof ("://") - 1; + break; + } + ++p; + } + + if (!*proto) + { + grub_dprintf ("bootp", "url: %s is not valid, protocol not found\n", url); + return 0; + } + + ps = p; + p = grub_strchr (p, '/'); + + if (!p) + { + grub_dprintf ("bootp", "url: %s is not valid, host/path not found\n", url); + grub_free (*proto); + *proto = NULL; + return 0; + } + + l = p - ps; + + if (l > 2 && ps[0] == '[' && ps[l - 1] == ']') + { + *host = grub_malloc (l - 1); + if (!*host) + { + grub_print_error (); + grub_free (*proto); + *proto = NULL; + return 0; + } + grub_memcpy (*host, ps + 1, l - 2); + (*host)[l - 2] = 0; + } + else + { + *host = grub_malloc (l + 1); + if (!*host) + { + grub_print_error (); + grub_free (*proto); + *proto = NULL; + return 0; + } + grub_memcpy (*host, ps, l); + (*host)[l] = 0; + } + + *path = grub_strdup (p); + if (!*path) + { + grub_print_error (); + grub_free (*host); + grub_free (*proto); + *host = NULL; + *proto = NULL; + return 0; + } + return 1; +} struct grub_dhcp_discover_options { @@ -604,6 +696,584 @@ send_dhcp_packet (struct grub_net_network_level_interface *iface) return err; } +/* The default netbuff size for sending DHCPv6 packets which should be + large enough to hold the information */ +#define GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE 512 + +struct grub_dhcp6_options +{ + grub_uint8_t *client_duid; + grub_uint16_t client_duid_len; + grub_uint8_t *server_duid; + grub_uint16_t server_duid_len; + grub_uint32_t iaid; + grub_uint32_t t1; + grub_uint32_t t2; + grub_net_network_level_address_t *ia_addr; + grub_uint32_t preferred_lifetime; + grub_uint32_t valid_lifetime; + grub_net_network_level_address_t *dns_server_addrs; + grub_uint16_t num_dns_server; + char *boot_file_proto; + char *boot_file_server_ip; + char *boot_file_path; +}; + +typedef struct grub_dhcp6_options *grub_dhcp6_options_t; + +struct grub_dhcp6_session +{ + struct grub_dhcp6_session *next; + struct grub_dhcp6_session **prev; + grub_uint32_t iaid; + grub_uint32_t transaction_id:24; + grub_uint64_t start_time; + struct grub_net_dhcp6_option_duid_ll duid; + struct grub_net_network_level_interface *iface; + + /* The associated dhcpv6 options */ + grub_dhcp6_options_t adv; + grub_dhcp6_options_t reply; +}; + +typedef struct grub_dhcp6_session *grub_dhcp6_session_t; + +typedef void (*dhcp6_option_hook_fn) (const struct grub_net_dhcp6_option *opt, void *data); + +static void +foreach_dhcp6_option (const struct grub_net_dhcp6_option *opt, grub_size_t size, + dhcp6_option_hook_fn hook, void *hook_data); + +static void +parse_dhcp6_iaaddr (const struct grub_net_dhcp6_option *opt, void *data) +{ + grub_dhcp6_options_t dhcp6 = (grub_dhcp6_options_t )data; + + grub_uint16_t code = grub_be_to_cpu16 (opt->code); + grub_uint16_t len = grub_be_to_cpu16 (opt->len); + + if (code == GRUB_NET_DHCP6_OPTION_IAADDR) + { + const struct grub_net_dhcp6_option_iaaddr *iaaddr; + iaaddr = (const struct grub_net_dhcp6_option_iaaddr *)opt->data; + + if (len < sizeof (*iaaddr)) + { + grub_dprintf ("bootp", "DHCPv6: code %u with insufficient length %u\n", code, len); + return; + } + if (!dhcp6->ia_addr) + { + dhcp6->ia_addr = grub_malloc (sizeof(*dhcp6->ia_addr)); + dhcp6->ia_addr->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + dhcp6->ia_addr->ipv6[0] = grub_get_unaligned64 (iaaddr->addr); + dhcp6->ia_addr->ipv6[1] = grub_get_unaligned64 (iaaddr->addr + 8); + dhcp6->preferred_lifetime = grub_be_to_cpu32 (iaaddr->preferred_lifetime); + dhcp6->valid_lifetime = grub_be_to_cpu32 (iaaddr->valid_lifetime); + } + } +} + +static void +parse_dhcp6_option (const struct grub_net_dhcp6_option *opt, void *data) +{ + grub_dhcp6_options_t dhcp6 = (grub_dhcp6_options_t)data; + grub_uint16_t code = grub_be_to_cpu16 (opt->code); + grub_uint16_t len = grub_be_to_cpu16 (opt->len); + + switch (code) + { + case GRUB_NET_DHCP6_OPTION_CLIENTID: + + if (dhcp6->client_duid || !len) + { + grub_dprintf ("bootp", "Skipped DHCPv6 CLIENTID with length %u\n", len); + break; + } + dhcp6->client_duid = grub_malloc (len); + grub_memcpy (dhcp6->client_duid, opt->data, len); + dhcp6->client_duid_len = len; + break; + + case GRUB_NET_DHCP6_OPTION_SERVERID: + + if (dhcp6->server_duid || !len) + { + grub_dprintf ("bootp", "Skipped DHCPv6 SERVERID with length %u\n", len); + break; + } + dhcp6->server_duid = grub_malloc (len); + grub_memcpy (dhcp6->server_duid, opt->data, len); + dhcp6->server_duid_len = len; + break; + + case GRUB_NET_DHCP6_OPTION_IA_NA: + { + const struct grub_net_dhcp6_option_iana *ia_na; + grub_uint16_t data_len; + + if (dhcp6->iaid || len < sizeof (*ia_na)) + { + grub_dprintf ("bootp", "Skipped DHCPv6 IA_NA with length %u\n", len); + break; + } + ia_na = (const struct grub_net_dhcp6_option_iana *)opt->data; + dhcp6->iaid = grub_be_to_cpu32 (ia_na->iaid); + dhcp6->t1 = grub_be_to_cpu32 (ia_na->t1); + dhcp6->t2 = grub_be_to_cpu32 (ia_na->t2); + + data_len = len - sizeof (*ia_na); + if (data_len) + foreach_dhcp6_option ((const struct grub_net_dhcp6_option *)ia_na->data, data_len, parse_dhcp6_iaaddr, dhcp6); + } + break; + + case GRUB_NET_DHCP6_OPTION_DNS_SERVERS: + { + const grub_uint8_t *po; + grub_uint16_t ln; + grub_net_network_level_address_t *la; + + if (!len || len & 0xf) + { + grub_dprintf ("bootp", "Skip invalid length DHCPv6 DNS_SERVERS \n"); + break; + } + dhcp6->num_dns_server = ln = len >> 4; + dhcp6->dns_server_addrs = la = grub_zalloc (ln * sizeof (*la)); + + for (po = opt->data; ln > 0; po += 0x10, la++, ln--) + { + la->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + la->ipv6[0] = grub_get_unaligned64 (po); + la->ipv6[1] = grub_get_unaligned64 (po + 8); + la->option = DNS_OPTION_PREFER_IPV6; + } + } + break; + + case GRUB_NET_DHCP6_OPTION_BOOTFILE_URL: + dissect_url ((const char *)opt->data, + &dhcp6->boot_file_proto, + &dhcp6->boot_file_server_ip, + &dhcp6->boot_file_path); + break; + + default: + break; + } +} + +static void +foreach_dhcp6_option (const struct grub_net_dhcp6_option *opt, grub_size_t size, dhcp6_option_hook_fn hook, void *hook_data) +{ + while (size) + { + grub_uint16_t code, len; + + if (size < sizeof (*opt)) + { + grub_dprintf ("bootp", "DHCPv6: Options stopped with remaining size %" PRIxGRUB_SIZE "\n", size); + break; + } + size -= sizeof (*opt); + len = grub_be_to_cpu16 (opt->len); + code = grub_be_to_cpu16 (opt->code); + if (size < len) + { + grub_dprintf ("bootp", "DHCPv6: Options stopped at out of bound length %u for option %u\n", len, code); + break; + } + if (!len) + { + grub_dprintf ("bootp", "DHCPv6: Options stopped at zero length option %u\n", code); + break; + } + else + { + if (hook) + hook (opt, hook_data); + size -= len; + opt = (const struct grub_net_dhcp6_option *)((grub_uint8_t *)opt + len + sizeof (*opt)); + } + } +} + +static grub_dhcp6_options_t +grub_dhcp6_options_get (const struct grub_net_dhcp6_packet *v6h, + grub_size_t size) +{ + grub_dhcp6_options_t options; + + if (size < sizeof (*v6h)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("DHCPv6 packet size too small")); + return NULL; + } + + options = grub_zalloc (sizeof(*options)); + if (!options) + return NULL; + + foreach_dhcp6_option ((const struct grub_net_dhcp6_option *)v6h->dhcp_options, + size - sizeof (*v6h), parse_dhcp6_option, options); + + return options; +} + +static void +grub_dhcp6_options_free (grub_dhcp6_options_t options) +{ + if (options->client_duid) + grub_free (options->client_duid); + if (options->server_duid) + grub_free (options->server_duid); + if (options->ia_addr) + grub_free (options->ia_addr); + if (options->dns_server_addrs) + grub_free (options->dns_server_addrs); + if (options->boot_file_proto) + grub_free (options->boot_file_proto); + if (options->boot_file_server_ip) + grub_free (options->boot_file_server_ip); + if (options->boot_file_path) + grub_free (options->boot_file_path); + + grub_free (options); +} + +static grub_dhcp6_session_t grub_dhcp6_sessions; +#define FOR_DHCP6_SESSIONS_SAFE(var, next) FOR_LIST_ELEMENTS_SAFE (var, next, grub_dhcp6_sessions) +#define FOR_DHCP6_SESSIONS(var) FOR_LIST_ELEMENTS (var, grub_dhcp6_sessions) + +static void +grub_net_configure_by_dhcp6_info (const char *name, + struct grub_net_card *card, + grub_dhcp6_options_t dhcp6, + int is_def, + int flags, + struct grub_net_network_level_interface **ret_inf) +{ + grub_net_network_level_netaddress_t netaddr; + struct grub_net_network_level_interface *inf; + + if (dhcp6->ia_addr) + { + inf = grub_net_add_addr (name, card, dhcp6->ia_addr, &card->default_address, flags); + + netaddr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + netaddr.ipv6.base[0] = dhcp6->ia_addr->ipv6[0]; + netaddr.ipv6.base[1] = 0; + netaddr.ipv6.masksize = 64; + grub_net_add_route (name, netaddr, inf); + + if (ret_inf) + *ret_inf = inf; + } + + if (dhcp6->dns_server_addrs) + { + grub_uint16_t i; + + for (i = 0; i < dhcp6->num_dns_server; ++i) + grub_net_add_dns_server (dhcp6->dns_server_addrs + i); + } + + if (dhcp6->boot_file_path) + grub_env_set_net_property (name, "boot_file", dhcp6->boot_file_path, + grub_strlen (dhcp6->boot_file_path)); + + if (is_def && dhcp6->boot_file_server_ip) + { + grub_net_default_server = grub_strdup (dhcp6->boot_file_server_ip); + grub_env_set ("net_default_interface", name); + grub_env_export ("net_default_interface"); + } +} + +static void +grub_dhcp6_session_add (struct grub_net_network_level_interface *iface, + grub_uint32_t iaid) +{ + grub_dhcp6_session_t se; + struct grub_datetime date; + grub_err_t err; + grub_int32_t t = 0; + + se = grub_malloc (sizeof (*se)); + + err = grub_get_datetime (&date); + if (err || !grub_datetime2unixtime (&date, &t)) + { + grub_errno = GRUB_ERR_NONE; + t = 0; + } + + se->iface = iface; + se->iaid = iaid; + se->transaction_id = t; + se->start_time = grub_get_time_ms (); + se->duid.type = grub_cpu_to_be16_compile_time (3) ; + se->duid.hw_type = grub_cpu_to_be16_compile_time (1); + grub_memcpy (&se->duid.hwaddr, &iface->hwaddress.mac, sizeof (se->duid.hwaddr)); + se->adv = NULL; + se->reply = NULL; + grub_list_push (GRUB_AS_LIST_P (&grub_dhcp6_sessions), GRUB_AS_LIST (se)); +} + +static void +grub_dhcp6_session_remove (grub_dhcp6_session_t se) +{ + grub_list_remove (GRUB_AS_LIST (se)); + if (se->adv) + grub_dhcp6_options_free (se->adv); + if (se->reply) + grub_dhcp6_options_free (se->reply); + grub_free (se); +} + +static void +grub_dhcp6_session_remove_all (void) +{ + grub_dhcp6_session_t se, next; + + FOR_DHCP6_SESSIONS_SAFE (se, next) + { + grub_dhcp6_session_remove (se); + } + grub_dhcp6_sessions = NULL; +} + +static grub_err_t +grub_dhcp6_session_configure_network (grub_dhcp6_session_t se) +{ + char *name; + + name = grub_xasprintf ("%s:dhcp6", se->iface->card->name); + if (!name) + return grub_errno; + + grub_net_configure_by_dhcp6_info (name, se->iface->card, se->reply, 1, 0, 0); + grub_free (name); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_dhcp6_session_send_request (grub_dhcp6_session_t se) +{ + struct grub_net_buff *nb; + struct grub_net_dhcp6_option *opt; + struct grub_net_dhcp6_packet *v6h; + struct grub_net_dhcp6_option_iana *ia_na; + struct grub_net_dhcp6_option_iaaddr *iaaddr; + struct udphdr *udph; + grub_net_network_level_address_t multicast; + grub_net_link_level_address_t ll_multicast; + grub_uint64_t elapsed; + struct grub_net_network_level_interface *inf = se->iface; + grub_dhcp6_options_t dhcp6 = se->adv; + grub_err_t err = GRUB_ERR_NONE; + + multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + multicast.ipv6[0] = grub_cpu_to_be64_compile_time (0xff02ULL << 48); + multicast.ipv6[1] = grub_cpu_to_be64_compile_time (0x10002ULL); + + err = grub_net_link_layer_resolve (inf, &multicast, &ll_multicast); + if (err) + return err; + + nb = grub_netbuff_alloc (GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE); + + if (!nb) + return grub_errno; + + err = grub_netbuff_reserve (nb, GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + err = grub_netbuff_push (nb, dhcp6->client_duid_len + sizeof (*opt)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_CLIENTID); + opt->len = grub_cpu_to_be16 (dhcp6->client_duid_len); + grub_memcpy (opt->data, dhcp6->client_duid , dhcp6->client_duid_len); + + err = grub_netbuff_push (nb, dhcp6->server_duid_len + sizeof (*opt)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_SERVERID); + opt->len = grub_cpu_to_be16 (dhcp6->server_duid_len); + grub_memcpy (opt->data, dhcp6->server_duid , dhcp6->server_duid_len); + + err = grub_netbuff_push (nb, sizeof (*ia_na) + sizeof (*opt)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + if (dhcp6->ia_addr) + { + err = grub_netbuff_push (nb, sizeof(*iaaddr) + sizeof (*opt)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + } + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA); + opt->len = grub_cpu_to_be16 (sizeof (*ia_na)); + if (dhcp6->ia_addr) + opt->len += grub_cpu_to_be16 (sizeof(*iaaddr) + sizeof (*opt)); + + ia_na = (struct grub_net_dhcp6_option_iana *)opt->data; + ia_na->iaid = grub_cpu_to_be32 (dhcp6->iaid); + + ia_na->t1 = grub_cpu_to_be32 (dhcp6->t1); + ia_na->t2 = grub_cpu_to_be32 (dhcp6->t2); + + if (dhcp6->ia_addr) + { + opt = (struct grub_net_dhcp6_option *)ia_na->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IAADDR); + opt->len = grub_cpu_to_be16 (sizeof (*iaaddr)); + iaaddr = (struct grub_net_dhcp6_option_iaaddr *)opt->data; + grub_set_unaligned64 (iaaddr->addr, dhcp6->ia_addr->ipv6[0]); + grub_set_unaligned64 (iaaddr->addr + 8, dhcp6->ia_addr->ipv6[1]); + + iaaddr->preferred_lifetime = grub_cpu_to_be32 (dhcp6->preferred_lifetime); + iaaddr->valid_lifetime = grub_cpu_to_be32 (dhcp6->valid_lifetime); + } + + err = grub_netbuff_push (nb, sizeof (*opt) + 2 * sizeof (grub_uint16_t)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + opt = (struct grub_net_dhcp6_option*) nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ORO); + opt->len = grub_cpu_to_be16_compile_time (2 * sizeof (grub_uint16_t)); + grub_set_unaligned16 (opt->data, grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_BOOTFILE_URL)); + grub_set_unaligned16 (opt->data + 2, grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_DNS_SERVERS)); + + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (grub_uint16_t)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + opt = (struct grub_net_dhcp6_option*) nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ELAPSED_TIME); + opt->len = grub_cpu_to_be16_compile_time (sizeof (grub_uint16_t)); + + /* the time is expressed in hundredths of a second */ + elapsed = grub_divmod64 (grub_get_time_ms () - se->start_time, 10, 0); + + if (elapsed > 0xffff) + elapsed = 0xffff; + + grub_set_unaligned16 (opt->data, grub_cpu_to_be16 ((grub_uint16_t)elapsed)); + + err = grub_netbuff_push (nb, sizeof (*v6h)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + v6h = (struct grub_net_dhcp6_packet *) nb->data; + v6h->message_type = GRUB_NET_DHCP6_REQUEST; + v6h->transaction_id = se->transaction_id; + + err = grub_netbuff_push (nb, sizeof (*udph)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + udph = (struct udphdr *) nb->data; + udph->src = grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT); + udph->dst = grub_cpu_to_be16_compile_time (DHCP6_SERVER_PORT); + udph->chksum = 0; + udph->len = grub_cpu_to_be16 (nb->tail - nb->data); + + udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP, + &inf->address, + &multicast); + err = grub_net_send_ip_packet (inf, &multicast, &ll_multicast, nb, + GRUB_NET_IP_UDP); + + grub_netbuff_free (nb); + + return err; +} + +struct grub_net_network_level_interface * +grub_net_configure_by_dhcpv6_reply (const char *name, + struct grub_net_card *card, + grub_net_interface_flags_t flags, + const struct grub_net_dhcp6_packet *v6h, + grub_size_t size, + int is_def, + char **device, char **path) +{ + struct grub_net_network_level_interface *inf; + grub_dhcp6_options_t dhcp6; + int mask = -1; + + dhcp6 = grub_dhcp6_options_get (v6h, size); + if (!dhcp6) + { + grub_print_error (); + return NULL; + } + + grub_net_configure_by_dhcp6_info (name, card, dhcp6, is_def, flags, &inf); + + if (device && dhcp6->boot_file_proto && dhcp6->boot_file_server_ip) + { + *device = grub_xasprintf ("%s,%s", dhcp6->boot_file_proto, dhcp6->boot_file_server_ip); + grub_print_error (); + } + if (path && dhcp6->boot_file_path) + { + *path = grub_strdup (dhcp6->boot_file_path); + grub_print_error (); + if (*path) + { + char *slash; + slash = grub_strrchr (*path, '/'); + if (slash) + *slash = 0; + else + **path = 0; + } + } + + grub_dhcp6_options_free (dhcp6); + + if (inf) + grub_net_add_ipv6_local (inf, mask); + + return inf; +} + /* * This is called directly from net/ip.c:handle_dgram(), because those * BOOTP/DHCP packets are a bit special due to their improper @@ -672,6 +1342,77 @@ grub_net_process_dhcp (struct grub_net_buff *nb, } } +grub_err_t +grub_net_process_dhcp6 (struct grub_net_buff *nb, + struct grub_net_card *card __attribute__ ((unused))) +{ + const struct grub_net_dhcp6_packet *v6h; + grub_dhcp6_session_t se; + grub_size_t size; + grub_dhcp6_options_t options; + + v6h = (const struct grub_net_dhcp6_packet *) nb->data; + size = nb->tail - nb->data; + + options = grub_dhcp6_options_get (v6h, size); + if (!options) + return grub_errno; + + if (!options->client_duid || !options->server_duid || !options->ia_addr) + { + grub_dhcp6_options_free (options); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Bad DHCPv6 Packet"); + } + + FOR_DHCP6_SESSIONS (se) + { + if (se->transaction_id == v6h->transaction_id && + grub_memcmp (options->client_duid, &se->duid, sizeof (se->duid)) == 0 && + se->iaid == options->iaid) + break; + } + + if (!se) + { + grub_dprintf ("bootp", "DHCPv6 session not found\n"); + grub_dhcp6_options_free (options); + return GRUB_ERR_NONE; + } + + if (v6h->message_type == GRUB_NET_DHCP6_ADVERTISE) + { + if (se->adv) + { + grub_dprintf ("bootp", "Skipped DHCPv6 Advertised .. \n"); + grub_dhcp6_options_free (options); + return GRUB_ERR_NONE; + } + + se->adv = options; + return grub_dhcp6_session_send_request (se); + } + else if (v6h->message_type == GRUB_NET_DHCP6_REPLY) + { + if (!se->adv) + { + grub_dprintf ("bootp", "Skipped DHCPv6 Reply .. \n"); + grub_dhcp6_options_free (options); + return GRUB_ERR_NONE; + } + + se->reply = options; + grub_dhcp6_session_configure_network (se); + grub_dhcp6_session_remove (se); + return GRUB_ERR_NONE; + } + else + { + grub_dhcp6_options_free (options); + } + + return GRUB_ERR_NONE; +} + static grub_err_t grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)), int argc, char **args) @@ -897,180 +1638,174 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), return err; } -static grub_command_t cmd_getdhcp, cmd_bootp, cmd_dhcp; - -struct grub_net_network_level_interface * -grub_net_configure_by_dhcpv6_ack (const char *name, - struct grub_net_card *card, - grub_net_interface_flags_t flags - __attribute__((__unused__)), - const grub_net_link_level_address_t *hwaddr, - const struct grub_net_dhcpv6_packet *packet, - int is_def, char **device, char **path) +static grub_err_t +grub_cmd_bootp6 (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) { - struct grub_net_network_level_interface *inter = NULL; - struct grub_net_network_level_address addr; - int mask = -1; - - if (!device || !path) - return NULL; - - *device = 0; - *path = 0; - - grub_dprintf ("net", "mac address is %02x:%02x:%02x:%02x:%02x:%02x\n", - hwaddr->mac[0], hwaddr->mac[1], hwaddr->mac[2], - hwaddr->mac[3], hwaddr->mac[4], hwaddr->mac[5]); - - if (is_def) - grub_net_default_server = 0; - - if (is_def && !grub_net_default_server && packet) - { - const grub_uint8_t *options = packet->dhcp_options; - unsigned int option_max = 1024 - OFFSET_OF (dhcp_options, packet); - unsigned int i; - - for (i = 0; i < option_max - sizeof (grub_net_dhcpv6_option_t); ) - { - grub_uint16_t num, len; - grub_net_dhcpv6_option_t *opt = - (grub_net_dhcpv6_option_t *)(options + i); - - num = grub_be_to_cpu16(opt->option_num); - len = grub_be_to_cpu16(opt->option_len); + struct grub_net_card *card; + grub_uint32_t iaid = 0; + int interval; + grub_err_t err; + grub_dhcp6_session_t se; - grub_dprintf ("net", "got dhcpv6 option %d len %d\n", num, len); + err = GRUB_ERR_NONE; - if (len == 0) - break; + FOR_NET_CARDS (card) + { + struct grub_net_network_level_interface *iface; - if (len + i > 1024) - break; + if (argc > 0 && grub_strcmp (card->name, args[0]) != 0) + continue; - if (num == GRUB_NET_DHCP6_BOOTFILE_URL) - { - char *scheme, *userinfo, *host, *file; - char *tmp; - int hostlen; - int port; - int rc = extract_url_info ((const char *)opt->option_data, - (grub_size_t)len, - &scheme, &userinfo, &host, &port, - &file); - if (rc < 0) - continue; - - /* right now this only handles tftp. */ - if (grub_strcmp("tftp", scheme)) - { - grub_free (scheme); - grub_free (userinfo); - grub_free (host); - grub_free (file); - continue; - } - grub_free (userinfo); - - hostlen = grub_strlen (host); - if (hostlen > 2 && host[0] == '[' && host[hostlen-1] == ']') - { - tmp = host+1; - host[hostlen-1] = '\0'; - } - else - tmp = host; - - *device = grub_xasprintf ("%s,%s", scheme, tmp); - grub_free (scheme); - grub_free (host); - - if (file && *file) - { - tmp = grub_strrchr (file, '/'); - if (tmp) - *(tmp+1) = '\0'; - else - file[0] = '\0'; - } - else if (!file) - file = grub_strdup (""); - - if (file[0] == '/') - { - *path = grub_strdup (file+1); - grub_free (file); - } - else - *path = file; - } - else if (num == GRUB_NET_DHCP6_IA_NA) - { - const grub_net_dhcpv6_option_t *ia_na_opt; - const grub_net_dhcpv6_opt_ia_na_t *ia_na = - (const grub_net_dhcpv6_opt_ia_na_t *)opt; - unsigned int left = len - OFFSET_OF (options, ia_na); - unsigned int j; - - if ((grub_uint8_t *)ia_na + left > - (grub_uint8_t *)options + option_max) - left -= ((grub_uint8_t *)ia_na + left) - - ((grub_uint8_t *)options + option_max); - - if (len < OFFSET_OF (option_data, opt) - + sizeof (grub_net_dhcpv6_option_t)) - { - grub_dprintf ("net", - "found dhcpv6 ia_na option with no address\n"); - continue; - } - - for (j = 0; left > sizeof (grub_net_dhcpv6_option_t); ) - { - ia_na_opt = (const grub_net_dhcpv6_option_t *) - (ia_na->options + j); - grub_uint16_t ia_na_opt_num, ia_na_opt_len; - - ia_na_opt_num = grub_be_to_cpu16 (ia_na_opt->option_num); - ia_na_opt_len = grub_be_to_cpu16 (ia_na_opt->option_len); - if (ia_na_opt_len == 0) - break; - if (j + ia_na_opt_len > left) - break; - if (ia_na_opt_num == GRUB_NET_DHCP6_IA_ADDRESS) - { - const grub_net_dhcpv6_opt_ia_address_t *ia_addr; - - ia_addr = (const grub_net_dhcpv6_opt_ia_address_t *) - ia_na_opt; - addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; - grub_memcpy(addr.ipv6, ia_addr->ipv6_address, - sizeof (ia_addr->ipv6_address)); - inter = grub_net_add_addr (name, card, &addr, hwaddr, 0); - } - - j += ia_na_opt_len; - left -= ia_na_opt_len; - } - } + iface = grub_net_ipv6_get_link_local (card, &card->default_address); + if (!iface) + { + grub_dhcp6_session_remove_all (); + return grub_errno; + } - i += len + 4; - } + grub_dhcp6_session_add (iface, iaid++); + } - grub_print_error (); + for (interval = 200; interval < 10000; interval *= 2) + { + int done = 1; + + FOR_DHCP6_SESSIONS (se) + { + struct grub_net_buff *nb; + struct grub_net_dhcp6_option *opt; + struct grub_net_dhcp6_packet *v6h; + struct grub_net_dhcp6_option_duid_ll *duid; + struct grub_net_dhcp6_option_iana *ia_na; + grub_net_network_level_address_t multicast; + grub_net_link_level_address_t ll_multicast; + struct udphdr *udph; + + multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + multicast.ipv6[0] = grub_cpu_to_be64_compile_time (0xff02ULL << 48); + multicast.ipv6[1] = grub_cpu_to_be64_compile_time (0x10002ULL); + + err = grub_net_link_layer_resolve (se->iface, + &multicast, &ll_multicast); + if (err) + { + grub_dhcp6_session_remove_all (); + return err; + } + + nb = grub_netbuff_alloc (GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE); + + if (!nb) + { + grub_dhcp6_session_remove_all (); + return grub_errno; + } + + err = grub_netbuff_reserve (nb, GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE); + if (err) + { + grub_dhcp6_session_remove_all (); + grub_netbuff_free (nb); + return err; + } + + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (grub_uint16_t)); + if (err) + { + grub_dhcp6_session_remove_all (); + grub_netbuff_free (nb); + return err; + } + + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ELAPSED_TIME); + opt->len = grub_cpu_to_be16_compile_time (sizeof (grub_uint16_t)); + grub_set_unaligned16 (opt->data, 0); + + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (*duid)); + if (err) + { + grub_dhcp6_session_remove_all (); + grub_netbuff_free (nb); + return err; + } + + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_CLIENTID); + opt->len = grub_cpu_to_be16 (sizeof (*duid)); + + duid = (struct grub_net_dhcp6_option_duid_ll *) opt->data; + grub_memcpy (duid, &se->duid, sizeof (*duid)); + + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (*ia_na)); + if (err) + { + grub_dhcp6_session_remove_all (); + grub_netbuff_free (nb); + return err; + } + + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA); + opt->len = grub_cpu_to_be16 (sizeof (*ia_na)); + ia_na = (struct grub_net_dhcp6_option_iana *)opt->data; + ia_na->iaid = grub_cpu_to_be32 (se->iaid); + ia_na->t1 = 0; + ia_na->t2 = 0; + + err = grub_netbuff_push (nb, sizeof (*v6h)); + if (err) + { + grub_dhcp6_session_remove_all (); + grub_netbuff_free (nb); + return err; + } + + v6h = (struct grub_net_dhcp6_packet *)nb->data; + v6h->message_type = GRUB_NET_DHCP6_SOLICIT; + v6h->transaction_id = se->transaction_id; + + grub_netbuff_push (nb, sizeof (*udph)); + + udph = (struct udphdr *) nb->data; + udph->src = grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT); + udph->dst = grub_cpu_to_be16_compile_time (DHCP6_SERVER_PORT); + udph->chksum = 0; + udph->len = grub_cpu_to_be16 (nb->tail - nb->data); + + udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP, + &se->iface->address, &multicast); + + err = grub_net_send_ip_packet (se->iface, &multicast, + &ll_multicast, nb, GRUB_NET_IP_UDP); + done = 0; + grub_netbuff_free (nb); + + if (err) + { + grub_dhcp6_session_remove_all (); + return err; + } + } + if (!done) + grub_net_poll_cards (interval, 0); } - if (is_def) + FOR_DHCP6_SESSIONS (se) { - grub_env_set ("net_default_interface", name); - grub_env_export ("net_default_interface"); + grub_error_push (); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, + N_("couldn't autoconfigure %s"), + se->iface->card->name); } - if (inter) - grub_net_add_ipv6_local (inter, mask); - return inter; + grub_dhcp6_session_remove_all (); + + return err; } +static grub_command_t cmd_getdhcp, cmd_bootp, cmd_dhcp, cmd_bootp6; void grub_bootp_init (void) @@ -1084,11 +1819,15 @@ grub_bootp_init (void) cmd_getdhcp = grub_register_command ("net_get_dhcp_option", grub_cmd_dhcpopt, N_("VAR INTERFACE NUMBER DESCRIPTION"), N_("retrieve DHCP option and save it into VAR. If VAR is - then print the value.")); + cmd_bootp6 = grub_register_command ("net_bootp6", grub_cmd_bootp6, + N_("[CARD]"), + N_("perform a DHCPv6 autoconfiguration")); } void grub_bootp_fini (void) { + grub_unregister_command (cmd_bootp6); grub_unregister_command (cmd_getdhcp); grub_unregister_command (cmd_bootp); grub_unregister_command (cmd_dhcp); diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c index a673bea807..8e25680db0 100644 --- a/grub-core/net/drivers/efi/efinet.c +++ b/grub-core/net/drivers/efi/efinet.c @@ -393,9 +393,6 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, pxe_mode = pxe->mode; if (pxe_mode->using_ipv6) { - grub_net_link_level_address_t hwaddr; - struct grub_net_network_level_interface *intf; - grub_dprintf ("efinet", "using ipv6 and dhcpv6\n"); grub_dprintf ("efinet", "dhcp_ack_received: %s%s\n", pxe_mode->dhcp_ack_received ? "yes" : "no", @@ -403,15 +400,14 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, if (!pxe_mode->dhcp_ack_received) continue; - hwaddr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memcpy (hwaddr.mac, - card->efi_net->mode->current_address, - sizeof (hwaddr.mac)); - - intf = grub_net_configure_by_dhcpv6_ack (card->name, card, 0, &hwaddr, - (const struct grub_net_dhcpv6_packet *)&pxe_mode->dhcp_ack.dhcpv6, - 1, device, path); - if (intf && device && path) + grub_net_configure_by_dhcpv6_reply (card->name, card, 0, + (struct grub_net_dhcp6_packet *) + &pxe_mode->dhcp_ack, + sizeof (pxe_mode->dhcp_ack), + 1, device, path); + if (grub_errno) + grub_print_error (); + if (device && path) grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path); } else diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index a5896f6dc2..ce6bdc75c6 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -239,6 +239,45 @@ handle_dgram (struct grub_net_buff *nb, { struct udphdr *udph; udph = (struct udphdr *) nb->data; + + if (proto == GRUB_NET_IP_UDP && udph->dst == grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT)) + { + if (udph->chksum) + { + grub_uint16_t chk, expected; + chk = udph->chksum; + udph->chksum = 0; + expected = grub_net_ip_transport_checksum (nb, + GRUB_NET_IP_UDP, + source, + dest); + if (expected != chk) + { + grub_dprintf ("net", "Invalid UDP checksum. " + "Expected %x, got %x\n", + grub_be_to_cpu16 (expected), + grub_be_to_cpu16 (chk)); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + udph->chksum = chk; + } + + err = grub_netbuff_pull (nb, sizeof (*udph)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + err = grub_net_process_dhcp6 (nb, card); + if (err) + grub_print_error (); + + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + if (proto == GRUB_NET_IP_UDP && grub_be_to_cpu16 (udph->dst) == 68) { const struct grub_net_bootp_packet *bootp; diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index 9962880147..7614b58dca 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -1532,7 +1532,7 @@ typedef struct grub_efi_pxe_ip_filter { grub_efi_uint8_t filters; grub_efi_uint8_t ip_count; - grub_efi_uint8_t reserved; + grub_efi_uint16_t reserved; grub_efi_ip_address_t ip_list[GRUB_EFI_PXE_MAX_IPCNT]; } grub_efi_pxe_ip_filter_t; diff --git a/include/grub/net.h b/include/grub/net.h index d55d505a03..543251f727 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -451,50 +451,65 @@ struct grub_net_bootp_packet grub_uint8_t vendor[0]; } GRUB_PACKED; -enum - { - GRUB_NET_DHCP6_IA_NA = 3, - GRUB_NET_DHCP6_IA_ADDRESS = 5, - GRUB_NET_DHCP6_BOOTFILE_URL = 59, - }; - -struct grub_net_dhcpv6_option +struct grub_net_dhcp6_packet { - grub_uint16_t option_num; - grub_uint16_t option_len; - grub_uint8_t option_data[]; + grub_uint32_t message_type:8; + grub_uint32_t transaction_id:24; + grub_uint8_t dhcp_options[0]; } GRUB_PACKED; -typedef struct grub_net_dhcpv6_option grub_net_dhcpv6_option_t; -struct grub_net_dhcpv6_opt_ia_na -{ - grub_uint16_t option_num; - grub_uint16_t option_len; +struct grub_net_dhcp6_option { + grub_uint16_t code; + grub_uint16_t len; + grub_uint8_t data[0]; +} GRUB_PACKED; + +struct grub_net_dhcp6_option_iana { grub_uint32_t iaid; grub_uint32_t t1; grub_uint32_t t2; - grub_uint8_t options[]; + grub_uint8_t data[0]; } GRUB_PACKED; -typedef struct grub_net_dhcpv6_opt_ia_na grub_net_dhcpv6_opt_ia_na_t; -struct grub_net_dhcpv6_opt_ia_address -{ - grub_uint16_t option_num; - grub_uint16_t option_len; - grub_uint64_t ipv6_address[2]; +struct grub_net_dhcp6_option_iaaddr { + grub_uint8_t addr[16]; grub_uint32_t preferred_lifetime; grub_uint32_t valid_lifetime; - grub_uint8_t options[]; + grub_uint8_t data[0]; } GRUB_PACKED; -typedef struct grub_net_dhcpv6_opt_ia_address grub_net_dhcpv6_opt_ia_address_t; -struct grub_net_dhcpv6_packet +struct grub_net_dhcp6_option_duid_ll { - grub_uint32_t message_type:8; - grub_uint32_t transaction_id:24; - grub_uint8_t dhcp_options[1024]; + grub_uint16_t type; + grub_uint16_t hw_type; + grub_uint8_t hwaddr[6]; } GRUB_PACKED; -typedef struct grub_net_dhcpv6_packet grub_net_dhcpv6_packet_t; + +enum + { + GRUB_NET_DHCP6_SOLICIT = 1, + GRUB_NET_DHCP6_ADVERTISE = 2, + GRUB_NET_DHCP6_REQUEST = 3, + GRUB_NET_DHCP6_REPLY = 7 + }; + +enum + { + DHCP6_CLIENT_PORT = 546, + DHCP6_SERVER_PORT = 547 + }; + +enum + { + GRUB_NET_DHCP6_OPTION_CLIENTID = 1, + GRUB_NET_DHCP6_OPTION_SERVERID = 2, + GRUB_NET_DHCP6_OPTION_IA_NA = 3, + GRUB_NET_DHCP6_OPTION_IAADDR = 5, + GRUB_NET_DHCP6_OPTION_ORO = 6, + GRUB_NET_DHCP6_OPTION_ELAPSED_TIME = 8, + GRUB_NET_DHCP6_OPTION_DNS_SERVERS = 23, + GRUB_NET_DHCP6_OPTION_BOOTFILE_URL = 59 + }; #define GRUB_NET_BOOTP_RFC1048_MAGIC_0 0x63 #define GRUB_NET_BOOTP_RFC1048_MAGIC_1 0x82 @@ -532,12 +547,12 @@ grub_net_configure_by_dhcp_ack (const char *name, int is_def, char **device, char **path); struct grub_net_network_level_interface * -grub_net_configure_by_dhcpv6_ack (const char *name, - struct grub_net_card *card, - grub_net_interface_flags_t flags, - const grub_net_link_level_address_t *hwaddr, - const struct grub_net_dhcpv6_packet *packet, - int is_def, char **device, char **path); +grub_net_configure_by_dhcpv6_reply (const char *name, + struct grub_net_card *card, + grub_net_interface_flags_t flags, + const struct grub_net_dhcp6_packet *v6, + grub_size_t size, + int is_def, char **device, char **path); int grub_ipv6_get_masksize(grub_uint16_t *mask); @@ -554,6 +569,10 @@ void grub_net_process_dhcp (struct grub_net_buff *nb, struct grub_net_network_level_interface *iface); +grub_err_t +grub_net_process_dhcp6 (struct grub_net_buff *nb, + struct grub_net_card *card); + int grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a, const grub_net_link_level_address_t *b); From 8b9ce49b2c2591725d74ac2c0e012f8f4f68ec7d Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Wed, 15 Apr 2015 14:48:30 +0800 Subject: [PATCH 073/367] efinet: UEFI IPv6 PXE support When grub2 image is booted from UEFI IPv6 PXE, the DHCPv6 Reply packet is cached in firmware buffer which can be obtained by PXE Base Code protocol. The network interface can be setup through the parameters in that obtained packet. Signed-off-by: Michael Chang Signed-off-by: Ken Lin --- grub-core/net/drivers/efi/efinet.c | 2 + include/grub/efi/api.h | 71 ++++++++++++++++++------------ 2 files changed, 46 insertions(+), 27 deletions(-) diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c index 8e25680db0..014e5bf980 100644 --- a/grub-core/net/drivers/efi/efinet.c +++ b/grub-core/net/drivers/efi/efinet.c @@ -409,6 +409,8 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, grub_print_error (); if (device && path) grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path); + if (grub_errno) + grub_print_error (); } else { diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index 7614b58dca..91ab528e4d 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -1524,31 +1524,6 @@ typedef union grub_efi_pxe_dhcpv6_packet_t dhcpv6; } grub_efi_pxe_packet_t; -#define GRUB_EFI_PXE_MAX_IPCNT 8 -#define GRUB_EFI_PXE_MAX_ARP_ENTRIES 8 -#define GRUB_EFI_PXE_MAX_ROUTE_ENTRIES 8 - -typedef struct grub_efi_pxe_ip_filter -{ - grub_efi_uint8_t filters; - grub_efi_uint8_t ip_count; - grub_efi_uint16_t reserved; - grub_efi_ip_address_t ip_list[GRUB_EFI_PXE_MAX_IPCNT]; -} grub_efi_pxe_ip_filter_t; - -typedef struct grub_efi_pxe_arp_entry -{ - grub_efi_ip_address_t ip_addr; - grub_efi_mac_address_t mac_addr; -} grub_efi_pxe_arp_entry_t; - -typedef struct grub_efi_pxe_route_entry -{ - grub_efi_ip_address_t ip_addr; - grub_efi_ip_address_t subnet_mask; - grub_efi_ip_address_t gateway_addr; -} grub_efi_pxe_route_entry_t; - typedef struct grub_efi_pxe_icmp_error { grub_efi_uint8_t type; @@ -1574,6 +1549,48 @@ typedef struct grub_efi_pxe_tftp_error grub_efi_char8_t error_string[127]; } grub_efi_pxe_tftp_error_t; +typedef struct { + grub_uint8_t addr[4]; +} grub_efi_pxe_ipv4_address_t; + +typedef struct { + grub_uint8_t addr[16]; +} grub_efi_pxe_ipv6_address_t; + +typedef struct { + grub_uint8_t addr[32]; +} grub_efi_pxe_mac_address_t; + +typedef union { + grub_uint32_t addr[4]; + grub_efi_pxe_ipv4_address_t v4; + grub_efi_pxe_ipv6_address_t v6; +} grub_efi_pxe_ip_address_t; + +#define GRUB_EFI_PXE_BASE_CODE_MAX_IPCNT 8 +typedef struct grub_efi_pxe_ip_filter +{ + grub_efi_uint8_t filters; + grub_efi_uint8_t ip_count; + grub_efi_uint16_t reserved; + grub_efi_ip_address_t ip_list[GRUB_EFI_PXE_BASE_CODE_MAX_IPCNT]; +} grub_efi_pxe_ip_filter_t; + +typedef struct { + grub_efi_pxe_ip_address_t ip_addr; + grub_efi_pxe_mac_address_t mac_addr; +} grub_efi_pxe_arp_entry_t; + +typedef struct { + grub_efi_pxe_ip_address_t ip_addr; + grub_efi_pxe_ip_address_t subnet_mask; + grub_efi_pxe_ip_address_t gw_addr; +} grub_efi_pxe_route_entry_t; + + +#define GRUB_EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES 8 +#define GRUB_EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES 8 + typedef struct grub_efi_pxe_mode { grub_efi_boolean_t started; @@ -1605,9 +1622,9 @@ typedef struct grub_efi_pxe_mode grub_efi_pxe_packet_t pxe_bis_reply; grub_efi_pxe_ip_filter_t ip_filter; grub_efi_uint32_t arp_cache_entries; - grub_efi_pxe_arp_entry_t arp_cache[GRUB_EFI_PXE_MAX_ARP_ENTRIES]; + grub_efi_pxe_arp_entry_t arp_cache[GRUB_EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES]; grub_efi_uint32_t route_table_entries; - grub_efi_pxe_route_entry_t route_table[GRUB_EFI_PXE_MAX_ROUTE_ENTRIES]; + grub_efi_pxe_route_entry_t route_table[GRUB_EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES]; grub_efi_pxe_icmp_error_t icmp_error; grub_efi_pxe_tftp_error_t tftp_error; } grub_efi_pxe_mode_t; From 262bff531cae6ece5936e8b1d4a7dbb7cdb743a0 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Tue, 5 May 2015 14:19:24 +0800 Subject: [PATCH 074/367] grub.texi: Add net_bootp6 doument Update grub documentation for net_bootp6 command. Signed-off-by: Michael Chang Signed-off-by: Ken Lin --- docs/grub.texi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/grub.texi b/docs/grub.texi index 0615d0ed97..04ed6ac1f0 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -5487,6 +5487,7 @@ This command is only available on AArch64 systems. * net_add_dns:: Add a DNS server * net_add_route:: Add routing entry * net_bootp:: Perform a bootp/DHCP autoconfiguration +* net_bootp6:: Perform a DHCPv6 autoconfiguration * net_del_addr:: Remove IP address from interface * net_del_dns:: Remove a DNS server * net_del_route:: Remove a route entry @@ -5611,6 +5612,22 @@ Sets environment variable @samp{net_}@var{}@samp{_boot_file} @end deffn +@node net_bootp6 +@subsection net_bootp6 + +@deffn Command net_bootp6 [@var{card}] +Perform configuration of @var{card} using DHCPv6 protocol. If no card name is +specified, try to configure all existing cards. If configuration was +successful, interface with name @var{card}@samp{:dhcp6} and configured address +is added to @var{card}. + +@table @samp +@item 1 (Domain Name Server) +Adds all servers from option value to the list of servers used during name +resolution. +@end table + +@end deffn @node net_get_dhcp_option @subsection net_get_dhcp_option From fbdbe19997fa4dc53ebf1c36ea4a3f6d459a6b47 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Wed, 10 Jul 2019 23:58:28 +0200 Subject: [PATCH 075/367] bootp: Add processing DHCPACK packet from HTTP Boot The vendor class identifier with the string "HTTPClient" is used to denote the packet as responding to HTTP boot request. In DHCP4 config, the filename for HTTP boot is the URL of the boot file while for PXE boot it is the path to the boot file. As a consequence, the next-server becomes obseleted because the HTTP URL already contains the server address for the boot file. For DHCP6 config, there's no difference definition in existing config as dhcp6.bootfile-url can be used to specify URL for both HTTP and PXE boot file. This patch adds processing for "HTTPClient" vendor class identifier in DHCPACK packet by treating it as HTTP format, not as the PXE format. Signed-off-by: Michael Chang Signed-off-by: Ken Lin --- grub-core/net/bootp.c | 55 +++++++++++++++++++++++++++++++++++++++++++ include/grub/net.h | 1 + 2 files changed, 56 insertions(+) diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index fe93b80f1c..8fb8918ae7 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -500,6 +501,60 @@ grub_net_configure_by_dhcp_ack (const char *name, if (opt && opt_len) grub_env_set_net_property (name, "rootpath", (const char *) opt, opt_len); + opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER, &opt_len); + if (opt && opt_len) + { + grub_env_set_net_property (name, "vendor_class_identifier", (const char *) opt, opt_len); + if (opt && grub_strcmp (opt, "HTTPClient") == 0) + { + char *proto, *ip, *pa; + + if (!dissect_url (bp->boot_file, &proto, &ip, &pa)) + return inter; + + grub_env_set_net_property (name, "boot_file", pa, grub_strlen (pa)); + if (is_def) + { + grub_net_default_server = grub_strdup (ip); + grub_env_set ("net_default_interface", name); + grub_env_export ("net_default_interface"); + } + if (device && !*device) + { + *device = grub_xasprintf ("%s,%s", proto, ip); + grub_print_error (); + } + if (path) + { + *path = grub_strdup (pa); + grub_print_error (); + if (*path) + { + char *slash; + slash = grub_strrchr (*path, '/'); + if (slash) + *slash = 0; + else + **path = 0; + } + } + grub_net_add_ipv4_local (inter, mask); + inter->dhcp_ack = grub_malloc (size); + if (inter->dhcp_ack) + { + grub_memcpy (inter->dhcp_ack, bp, size); + inter->dhcp_acklen = size; + } + else + grub_errno = GRUB_ERR_NONE; + + grub_free (proto); + grub_free (ip); + grub_free (pa); + return inter; + } + } + opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_EXTENSIONS_PATH, &opt_len); if (opt && opt_len) grub_env_set_net_property (name, "extensionspath", (const char *) opt, opt_len); diff --git a/include/grub/net.h b/include/grub/net.h index 543251f727..42af7de250 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -531,6 +531,7 @@ enum GRUB_NET_DHCP_MESSAGE_TYPE = 53, GRUB_NET_DHCP_SERVER_IDENTIFIER = 54, GRUB_NET_DHCP_PARAMETER_REQUEST_LIST = 55, + GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER = 60, GRUB_NET_BOOTP_CLIENT_ID = 61, GRUB_NET_DHCP_TFTP_SERVER_NAME = 66, GRUB_NET_DHCP_BOOTFILE_NAME = 67, From 838bc461d9c7c42423a2475d1d37031471aa05bb Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Sun, 10 Jul 2016 23:46:31 +0800 Subject: [PATCH 076/367] efinet: Setting network from UEFI device path The PXE Base Code protocol used to obtain cached PXE DHCPACK packet is no longer provided for HTTP Boot. Instead, we have to get the HTTP boot information from the device path nodes defined in following UEFI Specification sections. 9.3.5.12 IPv4 Device Path 9.3.5.13 IPv6 Device Path 9.3.5.23 Uniform Resource Identifiers (URI) Device Path This patch basically does: include/grub/efi/api.h: Add new structure of Uniform Resource Identifiers (URI) Device Path grub-core/net/drivers/efi/efinet.c: Check if PXE Base Code is available, if not it will try to obtain the netboot information from the device path where the image booted from. The DHCPACK packet is recoverd from the information in device patch and feed into the same DHCP packet processing functions to ensure the network interface is setting up the same way it used to be. Signed-off-by: Michael Chang Signed-off-by: Ken Lin --- grub-core/net/drivers/efi/efinet.c | 284 +++++++++++++++++++++++++++-- include/grub/efi/api.h | 11 ++ 2 files changed, 280 insertions(+), 15 deletions(-) diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c index 014e5bf980..8171ecaa5e 100644 --- a/grub-core/net/drivers/efi/efinet.c +++ b/grub-core/net/drivers/efi/efinet.c @@ -26,6 +26,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -331,6 +332,227 @@ grub_efinet_findcards (void) grub_free (handles); } +static struct grub_net_buff * +grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *use_ipv6) +{ + grub_efi_uint16_t uri_len; + grub_efi_device_path_t *ldp, *ddp; + grub_efi_uri_device_path_t *uri_dp; + struct grub_net_buff *nb; + grub_err_t err; + + ddp = grub_efi_duplicate_device_path (dp); + if (!ddp) + return NULL; + + ldp = grub_efi_find_last_device_path (ddp); + + if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + || GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE) + { + grub_free (ddp); + return NULL; + } + + uri_len = GRUB_EFI_DEVICE_PATH_LENGTH (ldp) > 4 ? GRUB_EFI_DEVICE_PATH_LENGTH (ldp) - 4 : 0; + + if (!uri_len) + { + grub_free (ddp); + return NULL; + } + + uri_dp = (grub_efi_uri_device_path_t *) ldp; + + ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + ldp->length = sizeof (*ldp); + + ldp = grub_efi_find_last_device_path (ddp); + + if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)) + { + grub_free (ddp); + return NULL; + } + + nb = grub_netbuff_alloc (512); + if (!nb) + { + grub_free (ddp); + return NULL; + } + + if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE) + { + grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) ldp; + struct grub_net_bootp_packet *bp; + grub_uint8_t *ptr; + + bp = (struct grub_net_bootp_packet *) nb->tail; + err = grub_netbuff_put (nb, sizeof (*bp) + 4); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + + if (sizeof(bp->boot_file) < uri_len) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + grub_memcpy (bp->boot_file, uri_dp->uri, uri_len); + grub_memcpy (&bp->your_ip, ipv4->local_ip_address, sizeof (bp->your_ip)); + grub_memcpy (&bp->server_ip, ipv4->remote_ip_address, sizeof (bp->server_ip)); + + bp->vendor[0] = GRUB_NET_BOOTP_RFC1048_MAGIC_0; + bp->vendor[1] = GRUB_NET_BOOTP_RFC1048_MAGIC_1; + bp->vendor[2] = GRUB_NET_BOOTP_RFC1048_MAGIC_2; + bp->vendor[3] = GRUB_NET_BOOTP_RFC1048_MAGIC_3; + + ptr = nb->tail; + err = grub_netbuff_put (nb, sizeof (ipv4->subnet_mask) + 2); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr++ = GRUB_NET_BOOTP_NETMASK; + *ptr++ = sizeof (ipv4->subnet_mask); + grub_memcpy (ptr, ipv4->subnet_mask, sizeof (ipv4->subnet_mask)); + + ptr = nb->tail; + err = grub_netbuff_put (nb, sizeof (ipv4->gateway_ip_address) + 2); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr++ = GRUB_NET_BOOTP_ROUTER; + *ptr++ = sizeof (ipv4->gateway_ip_address); + grub_memcpy (ptr, ipv4->gateway_ip_address, sizeof (ipv4->gateway_ip_address)); + + ptr = nb->tail; + err = grub_netbuff_put (nb, sizeof ("HTTPClient") + 1); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr++ = GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER; + *ptr++ = sizeof ("HTTPClient") - 1; + grub_memcpy (ptr, "HTTPClient", sizeof ("HTTPClient") - 1); + + ptr = nb->tail; + err = grub_netbuff_put (nb, 1); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr = GRUB_NET_BOOTP_END; + *use_ipv6 = 0; + + ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + ldp->length = sizeof (*ldp); + ldp = grub_efi_find_last_device_path (ddp); + + if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE) + { + grub_efi_mac_address_device_path_t *mac = (grub_efi_mac_address_device_path_t *) ldp; + bp->hw_type = mac->if_type; + bp->hw_len = sizeof (bp->mac_addr); + grub_memcpy (bp->mac_addr, mac->mac_address, bp->hw_len); + } + } + else + { + grub_efi_ipv6_device_path_t *ipv6 = (grub_efi_ipv6_device_path_t *) ldp; + + struct grub_net_dhcp6_packet *d6p; + struct grub_net_dhcp6_option *opt; + struct grub_net_dhcp6_option_iana *iana; + struct grub_net_dhcp6_option_iaaddr *iaaddr; + + d6p = (struct grub_net_dhcp6_packet *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*d6p)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + d6p->message_type = GRUB_NET_DHCP6_REPLY; + + opt = (struct grub_net_dhcp6_option *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*opt)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA); + opt->len = grub_cpu_to_be16_compile_time (sizeof(*iana) + sizeof(*opt) + sizeof(*iaaddr)); + + err = grub_netbuff_put (nb, sizeof(*iana)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + + opt = (struct grub_net_dhcp6_option *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*opt)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IAADDR); + opt->len = grub_cpu_to_be16_compile_time (sizeof (*iaaddr)); + + iaaddr = (struct grub_net_dhcp6_option_iaaddr *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*iaaddr)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + grub_memcpy (iaaddr->addr, ipv6->local_ip_address, sizeof(ipv6->local_ip_address)); + + opt = (struct grub_net_dhcp6_option *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*opt) + uri_len); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_BOOTFILE_URL); + opt->len = grub_cpu_to_be16 (uri_len); + grub_memcpy (opt->data, uri_dp->uri, uri_len); + + *use_ipv6 = 1; + } + + grub_free (ddp); + return nb; +} + static void grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, char **path) @@ -346,7 +568,11 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, { grub_efi_device_path_t *cdp; struct grub_efi_pxe *pxe; - struct grub_efi_pxe_mode *pxe_mode; + struct grub_efi_pxe_mode *pxe_mode = NULL; + grub_uint8_t *packet_buf; + grub_size_t packet_bufsz ; + int ipv6; + struct grub_net_buff *nb = NULL; if (card->driver != &efidriver) continue; @@ -370,11 +596,21 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, */ if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE - && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)) + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)) continue; dup_dp = grub_efi_duplicate_device_path (dp); if (!dup_dp) continue; + + if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE) + { + dup_ldp = grub_efi_find_last_device_path (dup_dp); + dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + dup_ldp->length = sizeof (*dup_ldp); + } + dup_ldp = grub_efi_find_last_device_path (dup_dp); dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; @@ -387,23 +623,37 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, pxe = grub_efi_open_protocol (hnd, &pxe_io_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (! pxe) - continue; + if (!pxe) + { + nb = grub_efinet_create_dhcp_ack_from_device_path (dp, &ipv6); + if (!nb) + { + grub_print_error (); + continue; + } + packet_buf = nb->head; + packet_bufsz = nb->tail - nb->head; + } + else + { + pxe_mode = pxe->mode; + packet_buf = (grub_uint8_t *) &pxe_mode->dhcp_ack; + packet_bufsz = sizeof (pxe_mode->dhcp_ack); + ipv6 = pxe_mode->using_ipv6; + } - pxe_mode = pxe->mode; - if (pxe_mode->using_ipv6) + if (ipv6) { grub_dprintf ("efinet", "using ipv6 and dhcpv6\n"); - grub_dprintf ("efinet", "dhcp_ack_received: %s%s\n", - pxe_mode->dhcp_ack_received ? "yes" : "no", - pxe_mode->dhcp_ack_received ? "" : " cannot continue"); - if (!pxe_mode->dhcp_ack_received) - continue; + if (pxe_mode) + grub_dprintf ("efinet", "dhcp_ack_received: %s%s\n", + pxe_mode->dhcp_ack_received ? "yes" : "no", + pxe_mode->dhcp_ack_received ? "" : " cannot continue"); grub_net_configure_by_dhcpv6_reply (card->name, card, 0, (struct grub_net_dhcp6_packet *) - &pxe_mode->dhcp_ack, - sizeof (pxe_mode->dhcp_ack), + packet_buf, + packet_bufsz, 1, device, path); if (grub_errno) grub_print_error (); @@ -417,11 +667,15 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, grub_dprintf ("efinet", "using ipv4 and dhcp\n"); grub_net_configure_by_dhcp_ack (card->name, card, 0, (struct grub_net_bootp_packet *) - &pxe_mode->dhcp_ack, - sizeof (pxe_mode->dhcp_ack), + packet_buf, + packet_bufsz, 1, device, path); grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path); } + + if (nb) + grub_netbuff_free (nb); + return; } } diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index 91ab528e4d..4a51667adb 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -864,6 +864,8 @@ struct grub_efi_ipv4_device_path grub_efi_uint16_t remote_port; grub_efi_uint16_t protocol; grub_efi_uint8_t static_ip_address; + grub_efi_ipv4_address_t gateway_ip_address; + grub_efi_ipv4_address_t subnet_mask; } GRUB_PACKED; typedef struct grub_efi_ipv4_device_path grub_efi_ipv4_device_path_t; @@ -918,6 +920,15 @@ struct grub_efi_sata_device_path } GRUB_PACKED; typedef struct grub_efi_sata_device_path grub_efi_sata_device_path_t; +#define GRUB_EFI_URI_DEVICE_PATH_SUBTYPE 24 + +struct grub_efi_uri_device_path +{ + grub_efi_device_path_t header; + grub_efi_uint8_t uri[0]; +} GRUB_PACKED; +typedef struct grub_efi_uri_device_path grub_efi_uri_device_path_t; + #define GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE 10 /* Media Device Path. */ From 179fd1d0485551dedf030f67c4fe232400cf32f5 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Thu, 14 Jul 2016 17:48:45 +0800 Subject: [PATCH 077/367] efinet: Setting DNS server from UEFI protocol In the URI device path node, any name rahter than address can be used for looking up the resources so that DNS service become needed to get answer of the name's address. Unfortunately the DNS is not defined in any of the device path nodes so that we use the EFI_IP4_CONFIG2_PROTOCOL and EFI_IP6_CONFIG_PROTOCOL to obtain it. These two protcols are defined the sections of UEFI specification. 27.5 EFI IPv4 Configuration II Protocol 27.7 EFI IPv6 Configuration Protocol include/grub/efi/api.h: Add new structure and protocol UUID of EFI_IP4_CONFIG2_PROTOCOL and EFI_IP6_CONFIG_PROTOCOL. grub-core/net/drivers/efi/efinet.c: Use the EFI_IP4_CONFIG2_PROTOCOL and EFI_IP6_CONFIG_PROTOCOL to obtain the list of DNS server address for IPv4 and IPv6 respectively. The address of DNS servers is structured into DHCPACK packet and feed into the same DHCP packet processing functions to ensure the network interface is setting up the same way it used to be. Signed-off-by: Michael Chang Signed-off-by: Ken Lin --- grub-core/net/drivers/efi/efinet.c | 163 +++++++++++++++++++++++++++++ include/grub/efi/api.h | 75 +++++++++++++ 2 files changed, 238 insertions(+) diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c index 8171ecaa5e..715a6168d7 100644 --- a/grub-core/net/drivers/efi/efinet.c +++ b/grub-core/net/drivers/efi/efinet.c @@ -33,6 +33,8 @@ GRUB_MOD_LICENSE ("GPLv3+"); /* GUID. */ static grub_efi_guid_t net_io_guid = GRUB_EFI_SIMPLE_NETWORK_GUID; static grub_efi_guid_t pxe_io_guid = GRUB_EFI_PXE_GUID; +static grub_efi_guid_t ip4_config_guid = GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID; +static grub_efi_guid_t ip6_config_guid = GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID; static grub_err_t send_card_buffer (struct grub_net_card *dev, @@ -332,6 +334,125 @@ grub_efinet_findcards (void) grub_free (handles); } +static grub_efi_handle_t +grub_efi_locate_device_path (grub_efi_guid_t *protocol, grub_efi_device_path_t *device_path, + grub_efi_device_path_t **r_device_path) +{ + grub_efi_handle_t handle; + grub_efi_status_t status; + + status = efi_call_3 (grub_efi_system_table->boot_services->locate_device_path, + protocol, &device_path, &handle); + + if (status != GRUB_EFI_SUCCESS) + return 0; + + if (r_device_path) + *r_device_path = device_path; + + return handle; +} + +static grub_efi_ipv4_address_t * +grub_dns_server_ip4_address (grub_efi_device_path_t *dp, grub_efi_uintn_t *num_dns) +{ + grub_efi_handle_t hnd; + grub_efi_status_t status; + grub_efi_ip4_config2_protocol_t *conf; + grub_efi_ipv4_address_t *addrs; + grub_efi_uintn_t data_size = 1 * sizeof (grub_efi_ipv4_address_t); + + hnd = grub_efi_locate_device_path (&ip4_config_guid, dp, NULL); + + if (!hnd) + return 0; + + conf = grub_efi_open_protocol (hnd, &ip4_config_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (!conf) + return 0; + + addrs = grub_malloc (data_size); + if (!addrs) + return 0; + + status = efi_call_4 (conf->get_data, conf, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER, + &data_size, addrs); + + if (status == GRUB_EFI_BUFFER_TOO_SMALL) + { + grub_free (addrs); + addrs = grub_malloc (data_size); + if (!addrs) + return 0; + + status = efi_call_4 (conf->get_data, conf, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER, + &data_size, addrs); + } + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (addrs); + return 0; + } + + *num_dns = data_size / sizeof (grub_efi_ipv4_address_t); + return addrs; +} + +static grub_efi_ipv6_address_t * +grub_dns_server_ip6_address (grub_efi_device_path_t *dp, grub_efi_uintn_t *num_dns) +{ + grub_efi_handle_t hnd; + grub_efi_status_t status; + grub_efi_ip6_config_protocol_t *conf; + grub_efi_ipv6_address_t *addrs; + grub_efi_uintn_t data_size = 1 * sizeof (grub_efi_ipv6_address_t); + + hnd = grub_efi_locate_device_path (&ip6_config_guid, dp, NULL); + + if (!hnd) + return 0; + + conf = grub_efi_open_protocol (hnd, &ip6_config_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (!conf) + return 0; + + addrs = grub_malloc (data_size); + if (!addrs) + return 0; + + status = efi_call_4 (conf->get_data, conf, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER, + &data_size, addrs); + + if (status == GRUB_EFI_BUFFER_TOO_SMALL) + { + grub_free (addrs); + addrs = grub_malloc (data_size); + if (!addrs) + return 0; + + status = efi_call_4 (conf->get_data, conf, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER, + &data_size, addrs); + } + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (addrs); + return 0; + } + + *num_dns = data_size / sizeof (grub_efi_ipv6_address_t); + return addrs; +} + static struct grub_net_buff * grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *use_ipv6) { @@ -390,6 +511,8 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) ldp; struct grub_net_bootp_packet *bp; grub_uint8_t *ptr; + grub_efi_ipv4_address_t *dns; + grub_efi_uintn_t num_dns; bp = (struct grub_net_bootp_packet *) nb->tail; err = grub_netbuff_put (nb, sizeof (*bp) + 4); @@ -451,6 +574,25 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u *ptr++ = sizeof ("HTTPClient") - 1; grub_memcpy (ptr, "HTTPClient", sizeof ("HTTPClient") - 1); + dns = grub_dns_server_ip4_address (dp, &num_dns); + if (dns) + { + grub_efi_uintn_t size_dns = sizeof (*dns) * num_dns; + + ptr = nb->tail; + err = grub_netbuff_put (nb, size_dns + 2); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr++ = GRUB_NET_BOOTP_DNS; + *ptr++ = size_dns; + grub_memcpy (ptr, dns, size_dns); + grub_free (dns); + } + ptr = nb->tail; err = grub_netbuff_put (nb, 1); if (err) @@ -483,6 +625,8 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u struct grub_net_dhcp6_option *opt; struct grub_net_dhcp6_option_iana *iana; struct grub_net_dhcp6_option_iaaddr *iaaddr; + grub_efi_ipv6_address_t *dns; + grub_efi_uintn_t num_dns; d6p = (struct grub_net_dhcp6_packet *)nb->tail; err = grub_netbuff_put (nb, sizeof(*d6p)); @@ -546,6 +690,25 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u opt->len = grub_cpu_to_be16 (uri_len); grub_memcpy (opt->data, uri_dp->uri, uri_len); + dns = grub_dns_server_ip6_address (dp, &num_dns); + if (dns) + { + grub_efi_uintn_t size_dns = sizeof (*dns) * num_dns; + + opt = (struct grub_net_dhcp6_option *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*opt) + size_dns); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_DNS_SERVERS); + opt->len = grub_cpu_to_be16 (size_dns); + grub_memcpy (opt->data, dns, size_dns); + grub_free (dns); + } + *use_ipv6 = 1; } diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index 4a51667adb..0b490195ad 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -352,6 +352,15 @@ #define GRUB_EFI_RNG_PROTOCOL_GUID \ { 0x3152bca5, 0xeade, 0x433d, \ { 0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44 } \ + +#define GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID \ + { 0x5b446ed1, 0xe30b, 0x4faa, \ + { 0x87, 0x1a, 0x36, 0x54, 0xec, 0xa3, 0x60, 0x80 } \ + } + +#define GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID \ + { 0x937fe521, 0x95ae, 0x4d1a, \ + { 0x89, 0x29, 0x48, 0xbc, 0xd9, 0x0a, 0xd3, 0x1a } \ } struct grub_efi_sal_system_table @@ -1883,6 +1892,72 @@ struct grub_efi_rng_protocol }; typedef struct grub_efi_rng_protocol grub_efi_rng_protocol_t; +enum grub_efi_ip4_config2_data_type { + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_POLICY, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_GATEWAY, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MAXIMUM +}; +typedef enum grub_efi_ip4_config2_data_type grub_efi_ip4_config2_data_type_t; + +struct grub_efi_ip4_config2_protocol +{ + grub_efi_status_t (*set_data) (struct grub_efi_ip4_config2_protocol *this, + grub_efi_ip4_config2_data_type_t data_type, + grub_efi_uintn_t data_size, + void *data); + + grub_efi_status_t (*get_data) (struct grub_efi_ip4_config2_protocol *this, + grub_efi_ip4_config2_data_type_t data_type, + grub_efi_uintn_t *data_size, + void *data); + + grub_efi_status_t (*register_data_notify) (struct grub_efi_ip4_config2_protocol *this, + grub_efi_ip4_config2_data_type_t data_type, + grub_efi_event_t event); + + grub_efi_status_t (*unregister_datanotify) (struct grub_efi_ip4_config2_protocol *this, + grub_efi_ip4_config2_data_type_t data_type, + grub_efi_event_t event); +}; +typedef struct grub_efi_ip4_config2_protocol grub_efi_ip4_config2_protocol_t; + +enum grub_efi_ip6_config_data_type { + GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_ALT_INTERFACEID, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_POLICY, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_DUP_ADDR_DETECT_TRANSMITS, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_GATEWAY, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_MAXIMUM +}; +typedef enum grub_efi_ip6_config_data_type grub_efi_ip6_config_data_type_t; + +struct grub_efi_ip6_config_protocol +{ + grub_efi_status_t (*set_data) (struct grub_efi_ip6_config_protocol *this, + grub_efi_ip6_config_data_type_t data_type, + grub_efi_uintn_t data_size, + void *data); + + grub_efi_status_t (*get_data) (struct grub_efi_ip6_config_protocol *this, + grub_efi_ip6_config_data_type_t data_type, + grub_efi_uintn_t *data_size, + void *data); + + grub_efi_status_t (*register_data_notify) (struct grub_efi_ip6_config_protocol *this, + grub_efi_ip6_config_data_type_t data_type, + grub_efi_event_t event); + + grub_efi_status_t (*unregister_datanotify) (struct grub_efi_ip6_config_protocol *this, + grub_efi_ip6_config_data_type_t data_type, + grub_efi_event_t event); +}; +typedef struct grub_efi_ip6_config_protocol grub_efi_ip6_config_protocol_t; + #if (GRUB_TARGET_SIZEOF_VOID_P == 4) || defined (__ia64__) \ || defined (__aarch64__) || defined (__MINGW64__) || defined (__CYGWIN__) \ || defined(__riscv) From 30fd1f9f2eeefbe5238ad1c150fd18db604ea337 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Wed, 22 Feb 2017 14:27:50 +0800 Subject: [PATCH 078/367] Support UEFI networking protocols References: fate#320130, bsc#1015589, bsc#1076132 Patch-Mainline: no V1: * Add preliminary support of UEFI networking protocols * Support UEFI HTTPS Boot V2: * Workaround http data access in firmware * Fix DNS device path parsing for efinet device * Relaxed UEFI Protocol requirement * Support Intel OPA (Omni-Path Architecture) PXE Boot V3: * Fix bufio in calculating address of next_buf * Check HTTP respond code * Use HEAD request method to test before GET * Finish HTTP transaction in one go * Fix bsc#1076132 Signed-off-by: Michael Chang [pjones: make efi_netfs not duplicate symbols from efinet] Signed-off-by: Peter Jones --- grub-core/Makefile.core.def | 12 + grub-core/io/bufio.c | 2 +- grub-core/kern/efi/efi.c | 96 +- grub-core/net/drivers/efi/efinet.c | 27 + grub-core/net/efi/dhcp.c | 397 ++++++++ grub-core/net/efi/efi_netfs.c | 57 ++ grub-core/net/efi/http.c | 419 ++++++++ grub-core/net/efi/ip4_config.c | 398 ++++++++ grub-core/net/efi/ip6_config.c | 422 ++++++++ grub-core/net/efi/net.c | 1428 ++++++++++++++++++++++++++++ grub-core/net/efi/pxe.c | 424 +++++++++ grub-core/net/net.c | 74 ++ include/grub/efi/api.h | 180 +++- include/grub/efi/dhcp.h | 343 +++++++ include/grub/efi/http.h | 215 +++++ include/grub/net/efi.h | 144 +++ util/grub-mknetdir.c | 23 +- 17 files changed, 4620 insertions(+), 41 deletions(-) create mode 100644 grub-core/net/efi/dhcp.c create mode 100644 grub-core/net/efi/efi_netfs.c create mode 100644 grub-core/net/efi/http.c create mode 100644 grub-core/net/efi/ip4_config.c create mode 100644 grub-core/net/efi/ip6_config.c create mode 100644 grub-core/net/efi/net.c create mode 100644 grub-core/net/efi/pxe.c create mode 100644 include/grub/efi/dhcp.h create mode 100644 include/grub/efi/http.h create mode 100644 include/grub/net/efi.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 52ec0fafcd..12797336c9 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2300,6 +2300,12 @@ module = { common = hook/datehook.c; }; +module = { + name = efi_netfs; + common = net/efi/efi_netfs.c; + enable = efi; +}; + module = { name = net; common = net/net.c; @@ -2313,6 +2319,12 @@ module = { common = net/ethernet.c; common = net/arp.c; common = net/netbuff.c; + efi = net/efi/net.c; + efi = net/efi/http.c; + efi = net/efi/pxe.c; + efi = net/efi/ip4_config.c; + efi = net/efi/ip6_config.c; + efi = net/efi/dhcp.c; }; module = { diff --git a/grub-core/io/bufio.c b/grub-core/io/bufio.c index a458c3aca7..1637731535 100644 --- a/grub-core/io/bufio.c +++ b/grub-core/io/bufio.c @@ -139,7 +139,7 @@ grub_bufio_read (grub_file_t file, char *buf, grub_size_t len) return res; /* Need to read some more. */ - next_buf = (file->offset + res + len - 1) & ~((grub_off_t) bufio->block_size - 1); + next_buf = (grub_divmod64 (file->offset + res + len - 1, bufio->block_size, NULL)) * bufio->block_size; /* Now read between file->offset + res and bufio->buffer_at. */ if (file->offset + res < next_buf) { diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index d6a2fb5778..2a446f5031 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -755,7 +755,7 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp) { grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) dp; - grub_printf ("/IPv4(%u.%u.%u.%u,%u.%u.%u.%u,%u,%u,%x,%x)", + grub_printf ("/IPv4(%u.%u.%u.%u,%u.%u.%u.%u,%u,%u,%x,%x", (unsigned) ipv4->local_ip_address[0], (unsigned) ipv4->local_ip_address[1], (unsigned) ipv4->local_ip_address[2], @@ -768,33 +768,60 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp) (unsigned) ipv4->remote_port, (unsigned) ipv4->protocol, (unsigned) ipv4->static_ip_address); + if (len == sizeof (*ipv4)) + { + grub_printf (",%u.%u.%u.%u,%u.%u.%u.%u", + (unsigned) ipv4->gateway_ip_address[0], + (unsigned) ipv4->gateway_ip_address[1], + (unsigned) ipv4->gateway_ip_address[2], + (unsigned) ipv4->gateway_ip_address[3], + (unsigned) ipv4->subnet_mask[0], + (unsigned) ipv4->subnet_mask[1], + (unsigned) ipv4->subnet_mask[2], + (unsigned) ipv4->subnet_mask[3]); + } + grub_printf (")"); } break; case GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE: { grub_efi_ipv6_device_path_t *ipv6 = (grub_efi_ipv6_device_path_t *) dp; - grub_printf ("/IPv6(%x:%x:%x:%x:%x:%x:%x:%x,%x:%x:%x:%x:%x:%x:%x:%x,%u,%u,%x,%x)", - (unsigned) ipv6->local_ip_address[0], - (unsigned) ipv6->local_ip_address[1], - (unsigned) ipv6->local_ip_address[2], - (unsigned) ipv6->local_ip_address[3], - (unsigned) ipv6->local_ip_address[4], - (unsigned) ipv6->local_ip_address[5], - (unsigned) ipv6->local_ip_address[6], - (unsigned) ipv6->local_ip_address[7], - (unsigned) ipv6->remote_ip_address[0], - (unsigned) ipv6->remote_ip_address[1], - (unsigned) ipv6->remote_ip_address[2], - (unsigned) ipv6->remote_ip_address[3], - (unsigned) ipv6->remote_ip_address[4], - (unsigned) ipv6->remote_ip_address[5], - (unsigned) ipv6->remote_ip_address[6], - (unsigned) ipv6->remote_ip_address[7], + grub_printf ("/IPv6(%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x,%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x,%u,%u,%x,%x", + (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[0]), + (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[1]), + (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[2]), + (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[3]), + (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[4]), + (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[5]), + (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[6]), + (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[7]), + (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[0]), + (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[1]), + (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[2]), + (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[3]), + (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[4]), + (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[5]), + (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[6]), + (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[7]), (unsigned) ipv6->local_port, (unsigned) ipv6->remote_port, (unsigned) ipv6->protocol, (unsigned) ipv6->static_ip_address); + if (len == sizeof (*ipv6)) + { + grub_printf (",%u,%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + (unsigned) ipv6->prefix_length, + (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[0]), + (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[1]), + (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[2]), + (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[3]), + (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[4]), + (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[5]), + (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[6]), + (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[7])); + } + grub_printf (")"); } break; case GRUB_EFI_INFINIBAND_DEVICE_PATH_SUBTYPE: @@ -834,6 +861,39 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp) dump_vendor_path ("Messaging", (grub_efi_vendor_device_path_t *) dp); break; + case GRUB_EFI_URI_DEVICE_PATH_SUBTYPE: + { + grub_efi_uri_device_path_t *uri + = (grub_efi_uri_device_path_t *) dp; + grub_printf ("/URI(%s)", uri->uri); + } + break; + case GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE: + { + grub_efi_dns_device_path_t *dns + = (grub_efi_dns_device_path_t *) dp; + if (dns->is_ipv6) + { + grub_printf ("/DNS(%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x)", + (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[0]) >> 16), + (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[0])), + (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[1]) >> 16), + (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[1])), + (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[2]) >> 16), + (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[2])), + (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[3]) >> 16), + (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[3]))); + } + else + { + grub_printf ("/DNS(%d.%d.%d.%d)", + dns->dns_server_ip[0].v4.addr[0], + dns->dns_server_ip[0].v4.addr[1], + dns->dns_server_ip[0].v4.addr[2], + dns->dns_server_ip[0].v4.addr[3]); + } + } + break; default: grub_printf ("/UnknownMessaging(%x)", (unsigned) subtype); break; diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c index 715a6168d7..e11d759f19 100644 --- a/grub-core/net/drivers/efi/efinet.c +++ b/grub-core/net/drivers/efi/efinet.c @@ -27,6 +27,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -491,6 +492,17 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u ldp = grub_efi_find_last_device_path (ddp); + /* Skip the DNS Device */ + if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE) + { + ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + ldp->length = sizeof (*ldp); + + ldp = grub_efi_find_last_device_path (ddp); + } + if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)) @@ -760,6 +772,7 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)) continue; dup_dp = grub_efi_duplicate_device_path (dp); @@ -774,6 +787,15 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, dup_ldp->length = sizeof (*dup_ldp); } + dup_ldp = grub_efi_find_last_device_path (dup_dp); + if (GRUB_EFI_DEVICE_PATH_SUBTYPE (dup_ldp) == GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE) + { + dup_ldp = grub_efi_find_last_device_path (dup_dp); + dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + dup_ldp->length = sizeof (*dup_ldp); + } + dup_ldp = grub_efi_find_last_device_path (dup_dp); dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; @@ -845,6 +867,9 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, GRUB_MOD_INIT(efinet) { + if (grub_efi_net_config) + return; + grub_efinet_findcards (); grub_efi_net_config = grub_efi_net_config_real; } @@ -856,5 +881,7 @@ GRUB_MOD_FINI(efinet) FOR_NET_CARDS_SAFE (card, next) if (card->driver == &efidriver) grub_net_card_unregister (card); + + grub_efi_net_config = NULL; } diff --git a/grub-core/net/efi/dhcp.c b/grub-core/net/efi/dhcp.c new file mode 100644 index 0000000000..dbef63d8c0 --- /dev/null +++ b/grub-core/net/efi/dhcp.c @@ -0,0 +1,397 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifdef GRUB_EFI_NET_DEBUG +static void +dhcp4_mode_print (grub_efi_dhcp4_mode_data_t *mode) +{ + switch (mode->state) + { + case GRUB_EFI_DHCP4_STOPPED: + grub_printf ("STATE: STOPPED\n"); + break; + case GRUB_EFI_DHCP4_INIT: + grub_printf ("STATE: INIT\n"); + break; + case GRUB_EFI_DHCP4_SELECTING: + grub_printf ("STATE: SELECTING\n"); + break; + case GRUB_EFI_DHCP4_REQUESTING: + grub_printf ("STATE: REQUESTING\n"); + break; + case GRUB_EFI_DHCP4_BOUND: + grub_printf ("STATE: BOUND\n"); + break; + case GRUB_EFI_DHCP4_RENEWING: + grub_printf ("STATE: RENEWING\n"); + break; + case GRUB_EFI_DHCP4_REBINDING: + grub_printf ("STATE: REBINDING\n"); + break; + case GRUB_EFI_DHCP4_INIT_REBOOT: + grub_printf ("STATE: INIT_REBOOT\n"); + break; + case GRUB_EFI_DHCP4_REBOOTING: + grub_printf ("STATE: REBOOTING\n"); + break; + default: + grub_printf ("STATE: UNKNOWN\n"); + break; + } + + grub_printf ("CLIENT_ADDRESS: %u.%u.%u.%u\n", + mode->client_address[0], + mode->client_address[1], + mode->client_address[2], + mode->client_address[3]); + grub_printf ("SERVER_ADDRESS: %u.%u.%u.%u\n", + mode->server_address[0], + mode->server_address[1], + mode->server_address[2], + mode->server_address[3]); + grub_printf ("SUBNET_MASK: %u.%u.%u.%u\n", + mode->subnet_mask[0], + mode->subnet_mask[1], + mode->subnet_mask[2], + mode->subnet_mask[3]); + grub_printf ("ROUTER_ADDRESS: %u.%u.%u.%u\n", + mode->router_address[0], + mode->router_address[1], + mode->router_address[2], + mode->router_address[3]); +} +#endif + +static grub_efi_ipv4_address_t * +grub_efi_dhcp4_parse_dns (grub_efi_dhcp4_protocol_t *dhcp4, grub_efi_dhcp4_packet_t *reply_packet) +{ + grub_efi_dhcp4_packet_option_t **option_list; + grub_efi_status_t status; + grub_efi_uint32_t option_count = 0; + grub_efi_uint32_t i; + + status = efi_call_4 (dhcp4->parse, dhcp4, reply_packet, &option_count, NULL); + + if (status != GRUB_EFI_BUFFER_TOO_SMALL) + return NULL; + + option_list = grub_malloc (option_count * sizeof(*option_list)); + if (!option_list) + return NULL; + + status = efi_call_4 (dhcp4->parse, dhcp4, reply_packet, &option_count, option_list); + if (status != GRUB_EFI_SUCCESS) + { + grub_free (option_list); + return NULL; + } + + for (i = 0; i < option_count; ++i) + { + if (option_list[i]->op_code == 6) + { + grub_efi_ipv4_address_t *dns_address; + + if (((option_list[i]->length & 0x3) != 0) || (option_list[i]->length == 0)) + continue; + + /* We only contact primary dns */ + dns_address = grub_malloc (sizeof (*dns_address)); + if (!dns_address) + { + grub_free (option_list); + return NULL; + } + grub_memcpy (dns_address, option_list[i]->data, sizeof (dns_address)); + grub_free (option_list); + return dns_address; + } + } + + grub_free (option_list); + return NULL; +} + +#if 0 +/* Somehow this doesn't work ... */ +static grub_err_t +grub_cmd_efi_bootp (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + struct grub_efi_net_device *dev; + for (dev = net_devices; dev; dev = dev->next) + { + grub_efi_pxe_t *pxe = dev->ip4_pxe; + grub_efi_pxe_mode_t *mode = pxe->mode; + grub_efi_status_t status; + + if (!mode->started) + { + status = efi_call_2 (pxe->start, pxe, 0); + + if (status != GRUB_EFI_SUCCESS) + grub_printf ("Couldn't start PXE\n"); + } + + status = efi_call_2 (pxe->dhcp, pxe, 0); + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("dhcp4 configure failed, %d\n", (int)status); + continue; + } + + dev->prefer_ip6 = 0; + } + + return GRUB_ERR_NONE; +} +#endif + +static grub_err_t +grub_cmd_efi_bootp (struct grub_command *cmd __attribute__ ((unused)), + int argc, + char **args) +{ + struct grub_efi_net_device *netdev; + + for (netdev = net_devices; netdev; netdev = netdev->next) + { + grub_efi_status_t status; + grub_efi_dhcp4_mode_data_t mode; + grub_efi_dhcp4_config_data_t config; + grub_efi_dhcp4_packet_option_t *options; + grub_efi_ipv4_address_t *dns_address; + grub_efi_net_ip_manual_address_t net_ip; + grub_efi_net_ip_address_t ip_addr; + grub_efi_net_interface_t *inf = NULL; + + if (argc > 0 && grub_strcmp (netdev->card_name, args[0]) != 0) + continue; + + grub_memset (&config, 0, sizeof(config)); + + config.option_count = 1; + options = grub_malloc (sizeof(*options) + 2); + /* Parameter request list */ + options->op_code = 55; + options->length = 3; + /* subnet mask */ + options->data[0] = 1; + /* router */ + options->data[1] = 3; + /* DNS */ + options->data[2] = 6; + config.option_list = &options; + + /* FIXME: What if the dhcp has bounded */ + status = efi_call_2 (netdev->dhcp4->configure, netdev->dhcp4, &config); + grub_free (options); + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("dhcp4 configure failed, %d\n", (int)status); + continue; + } + + status = efi_call_2 (netdev->dhcp4->start, netdev->dhcp4, NULL); + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("dhcp4 start failed, %d\n", (int)status); + continue; + } + + status = efi_call_2 (netdev->dhcp4->get_mode_data, netdev->dhcp4, &mode); + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("dhcp4 get mode failed, %d\n", (int)status); + continue; + } + +#ifdef GRUB_EFI_NET_DEBUG + dhcp4_mode_print (&mode); +#endif + + for (inf = netdev->net_interfaces; inf; inf = inf->next) + if (inf->prefer_ip6 == 0) + break; + + grub_memcpy (net_ip.ip4.address, mode.client_address, sizeof (net_ip.ip4.address)); + grub_memcpy (net_ip.ip4.subnet_mask, mode.subnet_mask, sizeof (net_ip.ip4.subnet_mask)); + + if (!inf) + { + char *name = grub_xasprintf ("%s:dhcp", netdev->card_name); + + net_ip.is_ip6 = 0; + inf = grub_efi_net_create_interface (netdev, + name, + &net_ip, + 1); + grub_free (name); + } + else + { + efi_net_interface_set_address (inf, &net_ip, 1); + } + + grub_memcpy (ip_addr.ip4, mode.router_address, sizeof (ip_addr.ip4)); + efi_net_interface_set_gateway (inf, &ip_addr); + + dns_address = grub_efi_dhcp4_parse_dns (netdev->dhcp4, mode.reply_packet); + if (dns_address) + efi_net_interface_set_dns (inf, (grub_efi_net_ip_address_t *)&dns_address); + + } + + return GRUB_ERR_NONE; +} + + +static grub_err_t +grub_cmd_efi_bootp6 (struct grub_command *cmd __attribute__ ((unused)), + int argc, + char **args) +{ + struct grub_efi_net_device *dev; + grub_efi_uint32_t ia_id; + + for (dev = net_devices, ia_id = 0; dev; dev = dev->next, ia_id++) + { + grub_efi_dhcp6_config_data_t config; + grub_efi_dhcp6_packet_option_t *option_list[1]; + grub_efi_dhcp6_packet_option_t *opt; + grub_efi_status_t status; + grub_efi_dhcp6_mode_data_t mode; + grub_efi_dhcp6_retransmission_t retrans; + grub_efi_net_ip_manual_address_t net_ip; + grub_efi_boot_services_t *b = grub_efi_system_table->boot_services; + grub_efi_net_interface_t *inf = NULL; + + if (argc > 0 && grub_strcmp (dev->card_name, args[0]) != 0) + continue; + + opt = grub_malloc (sizeof(*opt) + 2 * sizeof (grub_efi_uint16_t)); + +#define GRUB_EFI_DHCP6_OPT_ORO 6 + + opt->op_code = grub_cpu_to_be16_compile_time (GRUB_EFI_DHCP6_OPT_ORO); + opt->op_len = grub_cpu_to_be16_compile_time (2 * sizeof (grub_efi_uint16_t)); + +#define GRUB_EFI_DHCP6_OPT_BOOT_FILE_URL 59 +#define GRUB_EFI_DHCP6_OPT_DNS_SERVERS 23 + + grub_set_unaligned16 (opt->data, grub_cpu_to_be16_compile_time(GRUB_EFI_DHCP6_OPT_BOOT_FILE_URL)); + grub_set_unaligned16 (opt->data + 1 * sizeof (grub_efi_uint16_t), + grub_cpu_to_be16_compile_time(GRUB_EFI_DHCP6_OPT_DNS_SERVERS)); + + option_list[0] = opt; + retrans.irt = 4; + retrans.mrc = 4; + retrans.mrt = 32; + retrans.mrd = 60; + + config.dhcp6_callback = NULL; + config.callback_context = NULL; + config.option_count = 1; + config.option_list = option_list; + config.ia_descriptor.ia_id = ia_id; + config.ia_descriptor.type = GRUB_EFI_DHCP6_IA_TYPE_NA; + config.ia_info_event = NULL; + config.reconfigure_accept = 0; + config.rapid_commit = 0; + config.solicit_retransmission = &retrans; + + status = efi_call_2 (dev->dhcp6->configure, dev->dhcp6, &config); + grub_free (opt); + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("dhcp6 configure failed, %d\n", (int)status); + continue; + } + status = efi_call_1 (dev->dhcp6->start, dev->dhcp6); + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("dhcp6 start failed, %d\n", (int)status); + continue; + } + + status = efi_call_3 (dev->dhcp6->get_mode_data, dev->dhcp6, &mode, NULL); + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("dhcp4 get mode failed, %d\n", (int)status); + continue; + } + + for (inf = dev->net_interfaces; inf; inf = inf->next) + if (inf->prefer_ip6 == 1) + break; + + grub_memcpy (net_ip.ip6.address, mode.ia->ia_address[0].ip_address, sizeof (net_ip.ip6.address)); + net_ip.ip6.prefix_length = 64; + net_ip.ip6.is_anycast = 0; + net_ip.is_ip6 = 1; + + if (!inf) + { + char *name = grub_xasprintf ("%s:dhcp", dev->card_name); + + inf = grub_efi_net_create_interface (dev, + name, + &net_ip, + 1); + grub_free (name); + } + else + { + efi_net_interface_set_address (inf, &net_ip, 1); + } + + { + grub_efi_uint32_t count = 0; + grub_efi_dhcp6_packet_option_t **options = NULL; + grub_efi_uint32_t i; + + status = efi_call_4 (dev->dhcp6->parse, dev->dhcp6, mode.ia->reply_packet, &count, NULL); + + if (status == GRUB_EFI_BUFFER_TOO_SMALL && count) + { + options = grub_malloc (count * sizeof(*options)); + status = efi_call_4 (dev->dhcp6->parse, dev->dhcp6, mode.ia->reply_packet, &count, options); + } + + if (status != GRUB_EFI_SUCCESS) + { + if (options) + grub_free (options); + continue; + } + + for (i = 0; i < count; ++i) + { + if (options[i]->op_code == grub_cpu_to_be16_compile_time(GRUB_EFI_DHCP6_OPT_DNS_SERVERS)) + { + grub_efi_net_ip_address_t dns; + grub_memcpy (dns.ip6, options[i]->data, sizeof(net_ip.ip6)); + efi_net_interface_set_dns (inf, &dns); + break; + } + } + + if (options) + grub_free (options); + } + + efi_call_1 (b->free_pool, mode.client_id); + efi_call_1 (b->free_pool, mode.ia); + } + + return GRUB_ERR_NONE; +} + +grub_command_func_t grub_efi_net_bootp = grub_cmd_efi_bootp; +grub_command_func_t grub_efi_net_bootp6 = grub_cmd_efi_bootp6; diff --git a/grub-core/net/efi/efi_netfs.c b/grub-core/net/efi/efi_netfs.c new file mode 100644 index 0000000000..ef371d885e --- /dev/null +++ b/grub-core/net/efi/efi_netfs.c @@ -0,0 +1,57 @@ +#include +#include +#define EFI_NET_CMD_PREFIX "net_efi" +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_command_t cmd_efi_lsroutes; +static grub_command_t cmd_efi_lscards; +static grub_command_t cmd_efi_lsaddrs; +static grub_command_t cmd_efi_addaddr; +static grub_command_t cmd_efi_bootp; +static grub_command_t cmd_efi_bootp6; + +static int initialized; + +GRUB_MOD_INIT(efi_netfs) +{ + if (grub_net_open) + return; + + if (grub_efi_net_fs_init ()) + { + cmd_efi_lsroutes = grub_register_command ("net_efi_ls_routes", grub_efi_net_list_routes, + "", N_("list network routes")); + cmd_efi_lscards = grub_register_command ("net_efi_ls_cards", grub_efi_net_list_cards, + "", N_("list network cards")); + cmd_efi_lsaddrs = grub_register_command ("net_efi_ls_addr", grub_efi_net_list_addrs, + "", N_("list network addresses")); + cmd_efi_addaddr = grub_register_command ("net_efi_add_addr", grub_efi_net_add_addr, + N_("SHORTNAME CARD ADDRESS [HWADDRESS]"), + N_("Add a network address.")); + cmd_efi_bootp = grub_register_command ("net_efi_bootp", grub_efi_net_bootp, + N_("[CARD]"), + N_("perform a bootp autoconfiguration")); + cmd_efi_bootp6 = grub_register_command ("net_efi_bootp6", grub_efi_net_bootp6, + N_("[CARD]"), + N_("perform a bootp autoconfiguration")); + initialized = 1; + } +} + +GRUB_MOD_FINI(efi_netfs) +{ + if (initialized) + { + grub_unregister_command (cmd_efi_lsroutes); + grub_unregister_command (cmd_efi_lscards); + grub_unregister_command (cmd_efi_lsaddrs); + grub_unregister_command (cmd_efi_addaddr); + grub_unregister_command (cmd_efi_bootp); + grub_unregister_command (cmd_efi_bootp6); + grub_efi_net_fs_fini (); + initialized = 0; + return; + } +} diff --git a/grub-core/net/efi/http.c b/grub-core/net/efi/http.c new file mode 100644 index 0000000000..3f61fd2fa5 --- /dev/null +++ b/grub-core/net/efi/http.c @@ -0,0 +1,419 @@ + +#include +#include +#include +#include +#include + +static void +http_configure (struct grub_efi_net_device *dev, int prefer_ip6) +{ + grub_efi_http_config_data_t http_config; + grub_efi_httpv4_access_point_t httpv4_node; + grub_efi_httpv6_access_point_t httpv6_node; + grub_efi_status_t status; + + grub_efi_http_t *http = dev->http; + + grub_memset (&http_config, 0, sizeof(http_config)); + http_config.http_version = GRUB_EFI_HTTPVERSION11; + http_config.timeout_millisec = 5000; + + if (prefer_ip6) + { + grub_efi_uintn_t sz; + grub_efi_ip6_config_manual_address_t manual_address; + + http_config.local_address_is_ipv6 = 1; + sz = sizeof (manual_address); + status = efi_call_4 (dev->ip6_config->get_data, dev->ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS, + &sz, &manual_address); + + if (status == GRUB_EFI_NOT_FOUND) + { + grub_printf ("The MANUAL ADDRESS is not found\n"); + } + + /* FIXME: The manual interface would return BUFFER TOO SMALL !!! */ + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("??? %d\n",(int) status); + return; + } + + grub_memcpy (httpv6_node.local_address, manual_address.address, sizeof (httpv6_node.local_address)); + httpv6_node.local_port = 0; + http_config.access_point.ipv6_node = &httpv6_node; + } + else + { + http_config.local_address_is_ipv6 = 0; + grub_memset (&httpv4_node, 0, sizeof(httpv4_node)); + httpv4_node.use_default_address = 1; + + /* Use random port here */ + /* See TcpBind() in edk2/NetworkPkg/TcpDxe/TcpDispatcher.c */ + httpv4_node.local_port = 0; + http_config.access_point.ipv4_node = &httpv4_node; + } + + status = efi_call_2 (http->configure, http, &http_config); + + if (status == GRUB_EFI_ALREADY_STARTED) + { + /* XXX: This hangs HTTPS boot */ +#if 0 + if (efi_call_2 (http->configure, http, NULL) != GRUB_EFI_SUCCESS) + { + grub_error (GRUB_ERR_IO, N_("couldn't reset http instance")); + grub_print_error (); + return; + } + status = efi_call_2 (http->configure, http, &http_config); +#endif + return; + } + + if (status != GRUB_EFI_SUCCESS) + { + grub_error (GRUB_ERR_IO, N_("couldn't configure http protocol, reason: %d"), (int)status); + grub_print_error (); + return ; + } +} + +static grub_efi_boolean_t request_callback_done; +static grub_efi_boolean_t response_callback_done; + +static void +grub_efi_http_request_callback (grub_efi_event_t event __attribute__ ((unused)), + void *context __attribute__ ((unused))) +{ + request_callback_done = 1; +} + +static void +grub_efi_http_response_callback (grub_efi_event_t event __attribute__ ((unused)), + void *context __attribute__ ((unused))) +{ + response_callback_done = 1; +} + +static grub_err_t +efihttp_request (grub_efi_http_t *http, char *server, char *name, int use_https, int headeronly, grub_off_t *file_size) +{ + grub_efi_http_request_data_t request_data; + grub_efi_http_message_t request_message; + grub_efi_http_token_t request_token; + grub_efi_http_response_data_t response_data; + grub_efi_http_message_t response_message; + grub_efi_http_token_t response_token; + grub_efi_http_header_t request_headers[3]; + + grub_efi_status_t status; + grub_efi_boot_services_t *b = grub_efi_system_table->boot_services; + char *url = NULL; + + request_headers[0].field_name = (grub_efi_char8_t *)"Host"; + request_headers[0].field_value = (grub_efi_char8_t *)server; + request_headers[1].field_name = (grub_efi_char8_t *)"Accept"; + request_headers[1].field_value = (grub_efi_char8_t *)"*/*"; + request_headers[2].field_name = (grub_efi_char8_t *)"User-Agent"; + request_headers[2].field_value = (grub_efi_char8_t *)"UefiHttpBoot/1.0"; + + { + grub_efi_ipv6_address_t address; + const char *rest; + grub_efi_char16_t *ucs2_url; + grub_size_t url_len, ucs2_url_len; + const char *protocol = (use_https == 1) ? "https" : "http"; + + if (grub_efi_string_to_ip6_address (server, &address, &rest) && *rest == 0) + url = grub_xasprintf ("%s://[%s]%s", protocol, server, name); + else + url = grub_xasprintf ("%s://%s%s", protocol, server, name); + + if (!url) + { + return grub_errno; + } + + url_len = grub_strlen (url); + ucs2_url_len = url_len * GRUB_MAX_UTF16_PER_UTF8; + ucs2_url = grub_malloc ((ucs2_url_len + 1) * sizeof (ucs2_url[0])); + + if (!ucs2_url) + { + grub_free (url); + return grub_errno; + } + + ucs2_url_len = grub_utf8_to_utf16 (ucs2_url, ucs2_url_len, (grub_uint8_t *)url, url_len, NULL); /* convert string format from ascii to usc2 */ + ucs2_url[ucs2_url_len] = 0; + grub_free (url); + request_data.url = ucs2_url; + } + + request_data.method = (headeronly > 0) ? GRUB_EFI_HTTPMETHODHEAD : GRUB_EFI_HTTPMETHODGET; + + request_message.data.request = &request_data; + request_message.header_count = 3; + request_message.headers = request_headers; + request_message.body_length = 0; + request_message.body = NULL; + + /* request token */ + request_token.event = NULL; + request_token.status = GRUB_EFI_NOT_READY; + request_token.message = &request_message; + + request_callback_done = 0; + status = efi_call_5 (b->create_event, + GRUB_EFI_EVT_NOTIFY_SIGNAL, + GRUB_EFI_TPL_CALLBACK, + grub_efi_http_request_callback, + NULL, + &request_token.event); + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (request_data.url); + return grub_error (GRUB_ERR_IO, "Fail to create an event! status=0x%x\n", status); + } + + status = efi_call_2 (http->request, http, &request_token); + + if (status != GRUB_EFI_SUCCESS) + { + efi_call_1 (b->close_event, request_token.event); + grub_free (request_data.url); + return grub_error (GRUB_ERR_IO, "Fail to send a request! status=0x%x\n", status); + } + /* TODO: Add Timeout */ + while (!request_callback_done) + efi_call_1(http->poll, http); + + response_data.status_code = GRUB_EFI_HTTP_STATUS_UNSUPPORTED_STATUS; + response_message.data.response = &response_data; + /* herader_count will be updated by the HTTP driver on response */ + response_message.header_count = 0; + /* headers will be populated by the driver on response */ + response_message.headers = NULL; + /* use zero BodyLength to only receive the response headers */ + response_message.body_length = 0; + response_message.body = NULL; + response_token.event = NULL; + + status = efi_call_5 (b->create_event, + GRUB_EFI_EVT_NOTIFY_SIGNAL, + GRUB_EFI_TPL_CALLBACK, + grub_efi_http_response_callback, + NULL, + &response_token.event); + + if (status != GRUB_EFI_SUCCESS) + { + efi_call_1 (b->close_event, request_token.event); + grub_free (request_data.url); + return grub_error (GRUB_ERR_IO, "Fail to create an event! status=0x%x\n", status); + } + + response_token.status = GRUB_EFI_SUCCESS; + response_token.message = &response_message; + + /* wait for HTTP response */ + response_callback_done = 0; + status = efi_call_2 (http->response, http, &response_token); + + if (status != GRUB_EFI_SUCCESS) + { + efi_call_1 (b->close_event, response_token.event); + efi_call_1 (b->close_event, request_token.event); + grub_free (request_data.url); + return grub_error (GRUB_ERR_IO, "Fail to receive a response! status=%d\n", (int)status); + } + + /* TODO: Add Timeout */ + while (!response_callback_done) + efi_call_1 (http->poll, http); + + if (response_message.data.response->status_code != GRUB_EFI_HTTP_STATUS_200_OK) + { + grub_efi_http_status_code_t status_code = response_message.data.response->status_code; + + if (response_message.headers) + efi_call_1 (b->free_pool, response_message.headers); + efi_call_1 (b->close_event, response_token.event); + efi_call_1 (b->close_event, request_token.event); + grub_free (request_data.url); + if (status_code == GRUB_EFI_HTTP_STATUS_404_NOT_FOUND) + { + return grub_error (GRUB_ERR_FILE_NOT_FOUND, _("file `%s' not found"), name); + } + else + { + return grub_error (GRUB_ERR_NET_UNKNOWN_ERROR, + _("unsupported uefi http status code 0x%x"), status_code); + } + } + + if (file_size) + { + int i; + /* parse the length of the file from the ContentLength header */ + for (*file_size = 0, i = 0; i < (int)response_message.header_count; ++i) + { + if (!grub_strcmp((const char*)response_message.headers[i].field_name, "Content-Length")) + { + *file_size = grub_strtoul((const char*)response_message.headers[i].field_value, 0, 10); + break; + } + } + } + + if (response_message.headers) + efi_call_1 (b->free_pool, response_message.headers); + efi_call_1 (b->close_event, response_token.event); + efi_call_1 (b->close_event, request_token.event); + grub_free (request_data.url); + + return GRUB_ERR_NONE; +} + +static grub_ssize_t +efihttp_read (struct grub_efi_net_device *dev, + char *buf, + grub_size_t len) +{ + grub_efi_http_message_t response_message; + grub_efi_http_token_t response_token; + + grub_efi_status_t status; + grub_size_t sum = 0; + grub_efi_boot_services_t *b = grub_efi_system_table->boot_services; + grub_efi_http_t *http = dev->http; + + if (!len) + { + grub_error (GRUB_ERR_BUG, "Invalid arguments to EFI HTTP Read"); + return -1; + } + + efi_call_5 (b->create_event, + GRUB_EFI_EVT_NOTIFY_SIGNAL, + GRUB_EFI_TPL_CALLBACK, + grub_efi_http_response_callback, + NULL, + &response_token.event); + + while (len) + { + response_message.data.response = NULL; + response_message.header_count = 0; + response_message.headers = NULL; + response_message.body_length = len; + response_message.body = buf; + + response_token.message = &response_message; + response_token.status = GRUB_EFI_NOT_READY; + + response_callback_done = 0; + + status = efi_call_2 (http->response, http, &response_token); + if (status != GRUB_EFI_SUCCESS) + { + efi_call_1 (b->close_event, response_token.event); + grub_error (GRUB_ERR_IO, "Error! status=%d\n", (int)status); + return -1; + } + + while (!response_callback_done) + efi_call_1(http->poll, http); + + sum += response_message.body_length; + buf += response_message.body_length; + len -= response_message.body_length; + } + + efi_call_1 (b->close_event, response_token.event); + + return sum; +} + +static grub_err_t +grub_efihttp_open (struct grub_efi_net_device *dev, + int prefer_ip6 __attribute__ ((unused)), + grub_file_t file, + const char *filename __attribute__ ((unused)), + int type) +{ + grub_err_t err; + grub_off_t size; + char *buf; + + err = efihttp_request (dev->http, file->device->net->server, file->device->net->name, type, 1, 0); + if (err != GRUB_ERR_NONE) + return err; + + err = efihttp_request (dev->http, file->device->net->server, file->device->net->name, type, 0, &size); + if (err != GRUB_ERR_NONE) + return err; + + buf = grub_malloc (size); + efihttp_read (dev, buf, size); + + file->size = size; + file->data = buf; + file->not_easily_seekable = 0; + file->device->net->offset = 0; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_efihttp_close (struct grub_efi_net_device *dev __attribute__ ((unused)), + int prefer_ip6 __attribute__ ((unused)), + grub_file_t file) +{ + if (file->data) + grub_free (file->data); + + file->data = 0; + file->offset = 0; + file->size = 0; + file->device->net->offset = 0; + return GRUB_ERR_NONE; +} + +static grub_ssize_t +grub_efihttp_read (struct grub_efi_net_device *dev __attribute__((unused)), + int prefer_ip6 __attribute__((unused)), + grub_file_t file, + char *buf, + grub_size_t len) +{ + grub_size_t r = len; + + if (!file->data || !buf || !len) + return 0; + + if ((file->device->net->offset + len) > file->size) + r = file->size - file->device->net->offset; + + if (r) + { + grub_memcpy (buf, (char *)file->data + file->device->net->offset, r); + file->device->net->offset += r; + } + + return r; +} + +struct grub_efi_net_io io_http = + { + .configure = http_configure, + .open = grub_efihttp_open, + .read = grub_efihttp_read, + .close = grub_efihttp_close + }; diff --git a/grub-core/net/efi/ip4_config.c b/grub-core/net/efi/ip4_config.c new file mode 100644 index 0000000000..b711a5d945 --- /dev/null +++ b/grub-core/net/efi/ip4_config.c @@ -0,0 +1,398 @@ + +#include +#include +#include +#include +#include + +char * +grub_efi_hw_address_to_string (grub_efi_uint32_t hw_address_size, grub_efi_mac_address_t hw_address) +{ + char *hw_addr, *p; + int sz, s; + int i; + + sz = (int)hw_address_size * (sizeof ("XX:") - 1) + 1; + + hw_addr = grub_malloc (sz); + if (!hw_addr) + return NULL; + + p = hw_addr; + s = sz; + for (i = 0; i < (int)hw_address_size; i++) + { + grub_snprintf (p, sz, "%02x:", hw_address[i]); + p += sizeof ("XX:") - 1; + s -= sizeof ("XX:") - 1; + } + + hw_addr[sz - 2] = '\0'; + return hw_addr; +} + +char * +grub_efi_ip4_address_to_string (grub_efi_ipv4_address_t *address) +{ + char *addr; + + addr = grub_malloc (sizeof ("XXX.XXX.XXX.XXX")); + if (!addr) + return NULL; + + /* FIXME: Use grub_xasprintf ? */ + grub_snprintf (addr, + sizeof ("XXX.XXX.XXX.XXX"), + "%u.%u.%u.%u", + (*address)[0], + (*address)[1], + (*address)[2], + (*address)[3]); + + return addr; +} + +int +grub_efi_string_to_ip4_address (const char *val, grub_efi_ipv4_address_t *address, const char **rest) +{ + grub_uint32_t newip = 0; + int i; + const char *ptr = val; + + for (i = 0; i < 4; i++) + { + unsigned long t; + t = grub_strtoul (ptr, (char **) &ptr, 0); + if (grub_errno) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (*ptr != '.' && i == 0) + { + /* XXX: t is in host byte order */ + newip = t; + break; + } + if (t & ~0xff) + return 0; + newip <<= 8; + newip |= t; + if (i != 3 && *ptr != '.') + return 0; + ptr++; + } + + newip = grub_cpu_to_be32 (newip); + + grub_memcpy (address, &newip, sizeof(*address)); + + if (rest) + *rest = (ptr - 1); + return 1; +} + +static grub_efi_ip4_config2_interface_info_t * +efi_ip4_config_interface_info (grub_efi_ip4_config2_protocol_t *ip4_config) +{ + grub_efi_uintn_t sz; + grub_efi_status_t status; + grub_efi_ip4_config2_interface_info_t *interface_info; + + sz = sizeof (*interface_info) + sizeof (*interface_info->route_table); + interface_info = grub_malloc (sz); + if (!interface_info) + return NULL; + + status = efi_call_4 (ip4_config->get_data, ip4_config, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO, + &sz, interface_info); + + if (status == GRUB_EFI_BUFFER_TOO_SMALL) + { + grub_free (interface_info); + interface_info = grub_malloc (sz); + status = efi_call_4 (ip4_config->get_data, ip4_config, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO, + &sz, interface_info); + } + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (interface_info); + return NULL; + } + + return interface_info; +} + +static grub_efi_ip4_config2_manual_address_t * +efi_ip4_config_manual_address (grub_efi_ip4_config2_protocol_t *ip4_config) +{ + grub_efi_uintn_t sz; + grub_efi_status_t status; + grub_efi_ip4_config2_manual_address_t *manual_address; + + sz = sizeof (*manual_address); + manual_address = grub_malloc (sz); + if (!manual_address) + return NULL; + + status = efi_call_4 (ip4_config->get_data, ip4_config, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, + &sz, manual_address); + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (manual_address); + return NULL; + } + + return manual_address; +} + +char * +grub_efi_ip4_interface_name (struct grub_efi_net_device *dev) +{ + grub_efi_ip4_config2_interface_info_t *interface_info; + char *name; + + interface_info = efi_ip4_config_interface_info (dev->ip4_config); + + if (!interface_info) + return NULL; + + name = grub_malloc (GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE + * GRUB_MAX_UTF8_PER_UTF16 + 1); + *grub_utf16_to_utf8 ((grub_uint8_t *)name, interface_info->name, + GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE) = 0; + grub_free (interface_info); + return name; +} + +static char * +grub_efi_ip4_interface_hw_address (struct grub_efi_net_device *dev) +{ + grub_efi_ip4_config2_interface_info_t *interface_info; + char *hw_addr; + + interface_info = efi_ip4_config_interface_info (dev->ip4_config); + + if (!interface_info) + return NULL; + + hw_addr = grub_efi_hw_address_to_string (interface_info->hw_address_size, interface_info->hw_address); + grub_free (interface_info); + + return hw_addr; +} + +static char * +grub_efi_ip4_interface_address (struct grub_efi_net_device *dev) +{ + grub_efi_ip4_config2_manual_address_t *manual_address; + char *addr; + + manual_address = efi_ip4_config_manual_address (dev->ip4_config); + + if (!manual_address) + return NULL; + + addr = grub_efi_ip4_address_to_string (&manual_address->address); + grub_free (manual_address); + return addr; +} + + +static int +address_mask_size (grub_efi_ipv4_address_t *address) +{ + grub_uint8_t i; + grub_uint32_t u32_addr = grub_be_to_cpu32 (grub_get_unaligned32 (address)); + + if (u32_addr == 0) + return 0; + + for (i = 0; i < 32 ; ++i) + { + if (u32_addr == ((0xffffffff >> i) << i)) + return (32 - i); + } + + return -1; +} + +static char ** +grub_efi_ip4_interface_route_table (struct grub_efi_net_device *dev) +{ + grub_efi_ip4_config2_interface_info_t *interface_info; + char **ret; + int i, id; + + interface_info = efi_ip4_config_interface_info (dev->ip4_config); + if (!interface_info) + return NULL; + + ret = grub_malloc (sizeof (*ret) * (interface_info->route_table_size + 1)); + + if (!ret) + { + grub_free (interface_info); + return NULL; + } + + id = 0; + for (i = 0; i < (int)interface_info->route_table_size; i++) + { + char *subnet, *gateway, *mask; + grub_uint32_t u32_subnet, u32_gateway; + int mask_size; + grub_efi_ip4_route_table_t *route_table = interface_info->route_table + i; + grub_efi_net_interface_t *inf; + char *interface_name = NULL; + + for (inf = dev->net_interfaces; inf; inf = inf->next) + if (!inf->prefer_ip6) + interface_name = inf->name; + + u32_gateway = grub_get_unaligned32 (&route_table->gateway_address); + gateway = grub_efi_ip4_address_to_string (&route_table->gateway_address); + u32_subnet = grub_get_unaligned32 (&route_table->subnet_address); + subnet = grub_efi_ip4_address_to_string (&route_table->subnet_address); + mask_size = address_mask_size (&route_table->subnet_mask); + mask = grub_efi_ip4_address_to_string (&route_table->subnet_mask); + if (u32_subnet && !u32_gateway && interface_name) + ret[id++] = grub_xasprintf ("%s:local %s/%d %s", dev->card_name, subnet, mask_size, interface_name); + else if (u32_subnet && u32_gateway) + ret[id++] = grub_xasprintf ("%s:gw %s/%d gw %s", dev->card_name, subnet, mask_size, gateway); + else if (!u32_subnet && u32_gateway) + ret[id++] = grub_xasprintf ("%s:default %s/%d gw %s", dev->card_name, subnet, mask_size, gateway); + grub_free (subnet); + grub_free (gateway); + grub_free (mask); + } + + ret[id] = NULL; + grub_free (interface_info); + return ret; +} + +static grub_efi_net_interface_t * +grub_efi_ip4_interface_match (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *ip_address) +{ + grub_efi_ip4_config2_interface_info_t *interface_info; + grub_efi_net_interface_t *inf; + int i; + grub_efi_ipv4_address_t *address = &ip_address->ip4; + + interface_info = efi_ip4_config_interface_info (dev->ip4_config); + if (!interface_info) + return NULL; + + for (i = 0; i < (int)interface_info->route_table_size; i++) + { + grub_efi_ip4_route_table_t *route_table = interface_info->route_table + i; + grub_uint32_t u32_address, u32_mask, u32_subnet; + + u32_address = grub_get_unaligned32 (address); + u32_subnet = grub_get_unaligned32 (route_table->subnet_address); + u32_mask = grub_get_unaligned32 (route_table->subnet_mask); + + /* SKIP Default GATEWAY */ + if (!u32_subnet && !u32_mask) + continue; + + if ((u32_address & u32_mask) == u32_subnet) + { + for (inf = dev->net_interfaces; inf; inf = inf->next) + if (!inf->prefer_ip6) + { + grub_free (interface_info); + return inf; + } + } + } + + grub_free (interface_info); + return NULL; +} + +static int +grub_efi_ip4_interface_set_manual_address (struct grub_efi_net_device *dev, + grub_efi_net_ip_manual_address_t *net_ip, + int with_subnet) +{ + grub_efi_status_t status; + grub_efi_ip4_config2_manual_address_t *address = &net_ip->ip4; + + if (!with_subnet) + { + grub_efi_ip4_config2_manual_address_t *manual_address = + efi_ip4_config_manual_address (dev->ip4_config); + + if (manual_address) + { + grub_memcpy (address->subnet_mask, manual_address->subnet_mask, sizeof(address->subnet_mask)); + grub_free (manual_address); + } + else + { + /* XXX: */ + address->subnet_mask[0] = 0xff; + address->subnet_mask[1] = 0xff; + address->subnet_mask[2] = 0xff; + address->subnet_mask[3] = 0; + } + } + + status = efi_call_4 (dev->ip4_config->set_data, dev->ip4_config, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, + sizeof(*address), address); + + if (status != GRUB_EFI_SUCCESS) + return 0; + + return 1; +} + +static int +grub_efi_ip4_interface_set_gateway (struct grub_efi_net_device *dev, + grub_efi_net_ip_address_t *address) +{ + grub_efi_status_t status; + + status = efi_call_4 (dev->ip4_config->set_data, dev->ip4_config, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_GATEWAY, + sizeof (address->ip4), &address->ip4); + + if (status != GRUB_EFI_SUCCESS) + return 0; + return 1; +} + +/* FIXME: Multiple DNS */ +static int +grub_efi_ip4_interface_set_dns (struct grub_efi_net_device *dev, + grub_efi_net_ip_address_t *address) +{ + grub_efi_status_t status; + + status = efi_call_4 (dev->ip4_config->set_data, dev->ip4_config, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER, + sizeof (address->ip4), &address->ip4); + + if (status != GRUB_EFI_SUCCESS) + return 0; + return 1; +} + +grub_efi_net_ip_config_t *efi_net_ip4_config = &(grub_efi_net_ip_config_t) + { + .get_hw_address = grub_efi_ip4_interface_hw_address, + .get_address = grub_efi_ip4_interface_address, + .get_route_table = grub_efi_ip4_interface_route_table, + .best_interface = grub_efi_ip4_interface_match, + .set_address = grub_efi_ip4_interface_set_manual_address, + .set_gateway = grub_efi_ip4_interface_set_gateway, + .set_dns = grub_efi_ip4_interface_set_dns + }; diff --git a/grub-core/net/efi/ip6_config.c b/grub-core/net/efi/ip6_config.c new file mode 100644 index 0000000000..017c4d05bc --- /dev/null +++ b/grub-core/net/efi/ip6_config.c @@ -0,0 +1,422 @@ +#include +#include +#include +#include +#include + +char * +grub_efi_ip6_address_to_string (grub_efi_pxe_ipv6_address_t *address) +{ + char *str = grub_malloc (sizeof ("XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX")); + char *p; + int i; + int squash; + + if (!str) + return NULL; + + p = str; + squash = 0; + for (i = 0; i < 8; ++i) + { + grub_uint16_t addr; + + if (i == 7) + squash = 2; + + addr = grub_get_unaligned16 (address->addr + i * 2); + + if (grub_be_to_cpu16 (addr)) + { + char buf[sizeof ("XXXX")]; + if (i > 0) + *p++ = ':'; + grub_snprintf (buf, sizeof (buf), "%x", grub_be_to_cpu16 (addr)); + grub_strcpy (p, buf); + p += grub_strlen (buf); + + if (squash == 1) + squash = 2; + } + else + { + if (squash == 0) + { + *p++ = ':'; + squash = 1; + } + else if (squash == 2) + { + *p++ = ':'; + *p++ = '0'; + } + } + } + *p = '\0'; + return str; +} + +int +grub_efi_string_to_ip6_address (const char *val, grub_efi_ipv6_address_t *address, const char **rest) +{ + grub_uint16_t newip[8]; + const char *ptr = val; + int word, quaddot = -1; + int bracketed = 0; + + if (ptr[0] == '[') { + bracketed = 1; + ptr++; + } + + if (ptr[0] == ':' && ptr[1] != ':') + return 0; + if (ptr[0] == ':') + ptr++; + + for (word = 0; word < 8; word++) + { + unsigned long t; + if (*ptr == ':') + { + quaddot = word; + word--; + ptr++; + continue; + } + t = grub_strtoul (ptr, (char **) &ptr, 16); + if (grub_errno) + { + grub_errno = GRUB_ERR_NONE; + break; + } + if (t & ~0xffff) + return 0; + newip[word] = grub_cpu_to_be16 (t); + if (*ptr != ':') + break; + ptr++; + } + if (quaddot == -1 && word < 7) + return 0; + if (quaddot != -1) + { + grub_memmove (&newip[quaddot + 7 - word], &newip[quaddot], + (word - quaddot + 1) * sizeof (newip[0])); + grub_memset (&newip[quaddot], 0, (7 - word) * sizeof (newip[0])); + } + grub_memcpy (address, newip, 16); + if (bracketed && *ptr == ']') { + ptr++; + } + if (rest) + *rest = ptr; + return 1; +} + +static grub_efi_ip6_config_interface_info_t * +efi_ip6_config_interface_info (grub_efi_ip6_config_protocol_t *ip6_config) +{ + grub_efi_uintn_t sz; + grub_efi_status_t status; + grub_efi_ip6_config_interface_info_t *interface_info; + + sz = sizeof (*interface_info) + sizeof (*interface_info->route_table); + interface_info = grub_malloc (sz); + + status = efi_call_4 (ip6_config->get_data, ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO, + &sz, interface_info); + + if (status == GRUB_EFI_BUFFER_TOO_SMALL) + { + grub_free (interface_info); + interface_info = grub_malloc (sz); + status = efi_call_4 (ip6_config->get_data, ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO, + &sz, interface_info); + } + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (interface_info); + return NULL; + } + + return interface_info; +} + +static grub_efi_ip6_config_manual_address_t * +efi_ip6_config_manual_address (grub_efi_ip6_config_protocol_t *ip6_config) +{ + grub_efi_uintn_t sz; + grub_efi_status_t status; + grub_efi_ip6_config_manual_address_t *manual_address; + + sz = sizeof (*manual_address); + manual_address = grub_malloc (sz); + if (!manual_address) + return NULL; + + status = efi_call_4 (ip6_config->get_data, ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS, + &sz, manual_address); + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (manual_address); + return NULL; + } + + return manual_address; +} + +char * +grub_efi_ip6_interface_name (struct grub_efi_net_device *dev) +{ + grub_efi_ip6_config_interface_info_t *interface_info; + char *name; + + interface_info = efi_ip6_config_interface_info (dev->ip6_config); + + if (!interface_info) + return NULL; + + name = grub_malloc (GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE + * GRUB_MAX_UTF8_PER_UTF16 + 1); + *grub_utf16_to_utf8 ((grub_uint8_t *)name, interface_info->name, + GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE) = 0; + grub_free (interface_info); + return name; +} + +static char * +grub_efi_ip6_interface_hw_address (struct grub_efi_net_device *dev) +{ + grub_efi_ip6_config_interface_info_t *interface_info; + char *hw_addr; + + interface_info = efi_ip6_config_interface_info (dev->ip6_config); + + if (!interface_info) + return NULL; + + hw_addr = grub_efi_hw_address_to_string (interface_info->hw_address_size, interface_info->hw_address); + grub_free (interface_info); + + return hw_addr; +} + +static char * +grub_efi_ip6_interface_address (struct grub_efi_net_device *dev) +{ + grub_efi_ip6_config_manual_address_t *manual_address; + char *addr; + + manual_address = efi_ip6_config_manual_address (dev->ip6_config); + + if (!manual_address) + return NULL; + + addr = grub_efi_ip6_address_to_string ((grub_efi_pxe_ipv6_address_t *)&manual_address->address); + grub_free (manual_address); + return addr; +} + +static char ** +grub_efi_ip6_interface_route_table (struct grub_efi_net_device *dev) +{ + grub_efi_ip6_config_interface_info_t *interface_info; + char **ret; + int i, id; + + interface_info = efi_ip6_config_interface_info (dev->ip6_config); + if (!interface_info) + return NULL; + + ret = grub_malloc (sizeof (*ret) * (interface_info->route_count + 1)); + + if (!ret) + { + grub_free (interface_info); + return NULL; + } + + id = 0; + for (i = 0; i < (int)interface_info->route_count ; i++) + { + char *gateway, *destination; + grub_uint64_t u64_gateway[2]; + grub_uint64_t u64_destination[2]; + grub_efi_ip6_route_table_t *route_table = interface_info->route_table + i; + grub_efi_net_interface_t *inf; + char *interface_name = NULL; + + gateway = grub_efi_ip6_address_to_string (&route_table->gateway); + destination = grub_efi_ip6_address_to_string (&route_table->destination); + + u64_gateway[0] = grub_get_unaligned64 (route_table->gateway.addr); + u64_gateway[1] = grub_get_unaligned64 (route_table->gateway.addr + 8); + u64_destination[0] = grub_get_unaligned64 (route_table->destination.addr); + u64_destination[1] = grub_get_unaligned64 (route_table->destination.addr + 8); + + for (inf = dev->net_interfaces; inf; inf = inf->next) + if (inf->prefer_ip6) + interface_name = inf->name; + + if ((!u64_gateway[0] && !u64_gateway[1]) + && (u64_destination[0] || u64_destination[1])) + { + if (interface_name) + { + if ((grub_be_to_cpu64 (u64_destination[0]) == 0xfe80000000000000ULL) + && (!u64_destination[1]) + && (route_table->prefix_length == 64)) + ret[id++] = grub_xasprintf ("%s:link %s/%d %s", dev->card_name, destination, route_table->prefix_length, interface_name); + else + ret[id++] = grub_xasprintf ("%s:local %s/%d %s", dev->card_name, destination, route_table->prefix_length, interface_name); + } + } + else if ((u64_gateway[0] || u64_gateway[1]) + && (u64_destination[0] || u64_destination[1])) + ret[id++] = grub_xasprintf ("%s:gw %s/%d gw %s", dev->card_name, destination, route_table->prefix_length, gateway); + else if ((u64_gateway[0] || u64_gateway[1]) + && (!u64_destination[0] && !u64_destination[1])) + ret[id++] = grub_xasprintf ("%s:default %s/%d gw %s", dev->card_name, destination, route_table->prefix_length, gateway); + + grub_free (gateway); + grub_free (destination); + } + + ret[id] = NULL; + grub_free (interface_info); + return ret; +} + +static grub_efi_net_interface_t * +grub_efi_ip6_interface_match (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *ip_address) +{ + grub_efi_ip6_config_interface_info_t *interface_info; + grub_efi_net_interface_t *inf; + int i; + grub_efi_ipv6_address_t *address = &ip_address->ip6; + + interface_info = efi_ip6_config_interface_info (dev->ip6_config); + if (!interface_info) + return NULL; + + for (i = 0; i < (int)interface_info->route_count ; i++) + { + grub_uint64_t u64_addr[2]; + grub_uint64_t u64_subnet[2]; + grub_uint64_t u64_mask[2]; + + grub_efi_ip6_route_table_t *route_table = interface_info->route_table + i; + + /* SKIP Default GATEWAY */ + if (route_table->prefix_length == 0) + continue; + + u64_addr[0] = grub_get_unaligned64 (address); + u64_addr[1] = grub_get_unaligned64 (address + 4); + u64_subnet[0] = grub_get_unaligned64 (route_table->destination.addr); + u64_subnet[1] = grub_get_unaligned64 (route_table->destination.addr + 8); + u64_mask[0] = (route_table->prefix_length <= 64) ? + 0xffffffffffffffffULL << (64 - route_table->prefix_length) : + 0xffffffffffffffffULL; + u64_mask[1] = (route_table->prefix_length <= 64) ? + 0 : + 0xffffffffffffffffULL << (128 - route_table->prefix_length); + + if (((u64_addr[0] & u64_mask[0]) == u64_subnet[0]) + && ((u64_addr[1] & u64_mask[1]) == u64_subnet[1])) + { + for (inf = dev->net_interfaces; inf; inf = inf->next) + if (inf->prefer_ip6) + { + grub_free (interface_info); + return inf; + } + } + } + + grub_free (interface_info); + return NULL; +} + +static int +grub_efi_ip6_interface_set_manual_address (struct grub_efi_net_device *dev, + grub_efi_net_ip_manual_address_t *net_ip, + int with_subnet) +{ + grub_efi_status_t status; + grub_efi_ip6_config_manual_address_t *address = &net_ip->ip6; + + if (!with_subnet) + { + grub_efi_ip6_config_manual_address_t *manual_address = + efi_ip6_config_manual_address (dev->ip6_config); + + if (manual_address) + { + address->prefix_length = manual_address->prefix_length; + grub_free (manual_address); + } + else + { + /* XXX: */ + address->prefix_length = 64; + } + } + + status = efi_call_4 (dev->ip6_config->set_data, dev->ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS, + sizeof(*address), address); + + if (status != GRUB_EFI_SUCCESS) + return 0; + + return 1; +} + +static int +grub_efi_ip6_interface_set_gateway (struct grub_efi_net_device *dev, + grub_efi_net_ip_address_t *address) +{ + grub_efi_status_t status; + + status = efi_call_4 (dev->ip6_config->set_data, dev->ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_GATEWAY, + sizeof (address->ip6), &address->ip6); + + if (status != GRUB_EFI_SUCCESS) + return 0; + return 1; +} + +static int +grub_efi_ip6_interface_set_dns (struct grub_efi_net_device *dev, + grub_efi_net_ip_address_t *address) +{ + + grub_efi_status_t status; + + status = efi_call_4 (dev->ip6_config->set_data, dev->ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER, + sizeof (address->ip6), &address->ip6); + + if (status != GRUB_EFI_SUCCESS) + return 0; + return 1; +} + +grub_efi_net_ip_config_t *efi_net_ip6_config = &(grub_efi_net_ip_config_t) + { + .get_hw_address = grub_efi_ip6_interface_hw_address, + .get_address = grub_efi_ip6_interface_address, + .get_route_table = grub_efi_ip6_interface_route_table, + .best_interface = grub_efi_ip6_interface_match, + .set_address = grub_efi_ip6_interface_set_manual_address, + .set_gateway = grub_efi_ip6_interface_set_gateway, + .set_dns = grub_efi_ip6_interface_set_dns + }; diff --git a/grub-core/net/efi/net.c b/grub-core/net/efi/net.c new file mode 100644 index 0000000000..86bce6535d --- /dev/null +++ b/grub-core/net/efi/net.c @@ -0,0 +1,1428 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define GRUB_EFI_IP6_PREFIX_LENGTH 64 + +static grub_efi_guid_t ip4_config_guid = GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID; +static grub_efi_guid_t ip6_config_guid = GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID; +static grub_efi_guid_t http_service_binding_guid = GRUB_EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID; +static grub_efi_guid_t http_guid = GRUB_EFI_HTTP_PROTOCOL_GUID; +static grub_efi_guid_t pxe_io_guid = GRUB_EFI_PXE_GUID; +static grub_efi_guid_t dhcp4_service_binding_guid = GRUB_EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID; +static grub_efi_guid_t dhcp4_guid = GRUB_EFI_DHCP4_PROTOCOL_GUID; +static grub_efi_guid_t dhcp6_service_binding_guid = GRUB_EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID; +static grub_efi_guid_t dhcp6_guid = GRUB_EFI_DHCP6_PROTOCOL_GUID; + +struct grub_efi_net_device *net_devices; + +static char *default_server; +static grub_efi_net_interface_t *net_interface; +static grub_efi_net_interface_t *net_default_interface; + +#define efi_net_interface_configure(inf) inf->io->configure (inf->dev, inf->prefer_ip6) +#define efi_net_interface_open(inf, file, name) inf->io->open (inf->dev, inf->prefer_ip6, file, name, inf->io_type) +#define efi_net_interface_read(inf, file, buf, sz) inf->io->read (inf->dev, inf->prefer_ip6, file, buf, sz) +#define efi_net_interface_close(inf, file) inf->io->close (inf->dev, inf->prefer_ip6, file) +#define efi_net_interface(m,...) efi_net_interface_ ## m (net_interface, ## __VA_ARGS__) + +static grub_efi_handle_t +grub_efi_locate_device_path (grub_efi_guid_t *protocol, grub_efi_device_path_t *device_path, + grub_efi_device_path_t **r_device_path) +{ + grub_efi_handle_t handle; + grub_efi_status_t status; + + status = efi_call_3 (grub_efi_system_table->boot_services->locate_device_path, + protocol, &device_path, &handle); + + if (status != GRUB_EFI_SUCCESS) + return 0; + + if (r_device_path) + *r_device_path = device_path; + + return handle; +} + +static int +url_parse_fields (const char *url, char **proto, char **host, char **path) +{ + const char *p, *ps; + grub_size_t l; + + *proto = *host = *path = NULL; + ps = p = url; + + while ((p = grub_strchr (p, ':'))) + { + if (grub_strlen (p) < sizeof ("://") - 1) + break; + if (grub_memcmp (p, "://", sizeof ("://") - 1) == 0) + { + l = p - ps; + *proto = grub_malloc (l + 1); + if (!*proto) + { + grub_print_error (); + return 0; + } + + grub_memcpy (*proto, ps, l); + (*proto)[l] = '\0'; + p += sizeof ("://") - 1; + break; + } + ++p; + } + + if (!*proto) + { + grub_dprintf ("bootp", "url: %s is not valid, protocol not found\n", url); + return 0; + } + + ps = p; + p = grub_strchr (p, '/'); + + if (!p) + { + grub_dprintf ("bootp", "url: %s is not valid, host/path not found\n", url); + grub_free (*proto); + *proto = NULL; + return 0; + } + + l = p - ps; + + if (l > 2 && ps[0] == '[' && ps[l - 1] == ']') + { + *host = grub_malloc (l - 1); + if (!*host) + { + grub_print_error (); + grub_free (*proto); + *proto = NULL; + return 0; + } + grub_memcpy (*host, ps + 1, l - 2); + (*host)[l - 2] = 0; + } + else + { + *host = grub_malloc (l + 1); + if (!*host) + { + grub_print_error (); + grub_free (*proto); + *proto = NULL; + return 0; + } + grub_memcpy (*host, ps, l); + (*host)[l] = 0; + } + + *path = grub_strdup (p); + if (!*path) + { + grub_print_error (); + grub_free (*host); + grub_free (*proto); + *host = NULL; + *proto = NULL; + return 0; + } + return 1; +} + +static void +url_get_boot_location (const char *url, char **device, char **path, int is_default) +{ + char *protocol, *server, *file; + char *slash; + + if (!url_parse_fields (url, &protocol, &server, &file)) + return; + + if ((slash = grub_strrchr (file, '/'))) + *slash = 0; + else + *file = 0; + + *device = grub_xasprintf ("%s,%s", protocol, server); + *path = grub_strdup(file); + + if (is_default) + default_server = server; + else + grub_free (server); + + grub_free (protocol); + grub_free (file); +} + +static void +pxe_get_boot_location (const struct grub_net_bootp_packet *bp, + char **device, + char **path, + int is_default) +{ + char *server = grub_xasprintf ("%d.%d.%d.%d", + ((grub_uint8_t *) &bp->server_ip)[0], + ((grub_uint8_t *) &bp->server_ip)[1], + ((grub_uint8_t *) &bp->server_ip)[2], + ((grub_uint8_t *) &bp->server_ip)[3]); + + *device = grub_xasprintf ("tftp,%s", server); + + *path = grub_strndup (bp->boot_file, sizeof (bp->boot_file)); + + if (*path) + { + char *slash; + slash = grub_strrchr (*path, '/'); + if (slash) + *slash = 0; + else + **path = 0; + } + + if (is_default) + default_server = server; + else + grub_free (server); +} + +static void +pxe_get_boot_location_v6 (const struct grub_net_dhcp6_packet *dp, + grub_size_t dhcp_size, + char **device, + char **path) +{ + + struct grub_net_dhcp6_option *dhcp_opt; + grub_size_t dhcp_remain_size; + *device = *path = 0; + + if (dhcp_size < sizeof (*dp)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("DHCPv6 packet size too small")); + return; + } + + dhcp_remain_size = dhcp_size - sizeof (*dp); + dhcp_opt = (struct grub_net_dhcp6_option *)dp->dhcp_options; + + while (dhcp_remain_size) + { + grub_uint16_t code = grub_be_to_cpu16 (dhcp_opt->code); + grub_uint16_t len = grub_be_to_cpu16 (dhcp_opt->len); + grub_uint16_t option_size = sizeof (*dhcp_opt) + len; + + if (dhcp_remain_size < option_size || code == 0) + break; + + if (code == GRUB_NET_DHCP6_OPTION_BOOTFILE_URL) + { + char *url = grub_malloc (len + 1); + + grub_memcpy (url, dhcp_opt->data, len); + url[len] = 0; + + url_get_boot_location ((const char *)url, device, path, 1); + grub_free (url); + break; + } + + dhcp_remain_size -= option_size; + dhcp_opt = (struct grub_net_dhcp6_option *)((grub_uint8_t *)dhcp_opt + option_size); + } +} + +static grub_efi_net_interface_t * +grub_efi_net_config_from_device_path (grub_efi_device_path_t *dp, + struct grub_efi_net_device *netdev, + char **device, + char **path) +{ + grub_efi_net_interface_t *inf = NULL; + + while (1) + { + grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp); + grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); + grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp); + + if (type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE) + { + if (subtype == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE) + { + grub_efi_uri_device_path_t *uri_dp; + uri_dp = (grub_efi_uri_device_path_t *) dp; + /* Beware that uri_dp->uri may not be null terminated */ + url_get_boot_location ((const char *)uri_dp->uri, device, path, 1); + } + else if (subtype == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE) + { + grub_efi_net_ip_manual_address_t net_ip; + grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) dp; + + if (inf) + continue; + grub_memcpy (net_ip.ip4.address, ipv4->local_ip_address, sizeof (net_ip.ip4.address)); + grub_memcpy (net_ip.ip4.subnet_mask, ipv4->subnet_mask, sizeof (net_ip.ip4.subnet_mask)); + net_ip.is_ip6 = 0; + inf = grub_efi_net_create_interface (netdev, + netdev->card_name, + &net_ip, + 1); + } + else if (subtype == GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE) + { + grub_efi_net_ip_manual_address_t net_ip; + grub_efi_ipv6_device_path_t *ipv6 = (grub_efi_ipv6_device_path_t *) dp; + + if (inf) + continue; + grub_memcpy (net_ip.ip6.address, ipv6->local_ip_address, sizeof (net_ip.ip6.address)); + net_ip.ip6.prefix_length = GRUB_EFI_IP6_PREFIX_LENGTH; + net_ip.ip6.is_anycast = 0; + net_ip.is_ip6 = 1; + inf = grub_efi_net_create_interface (netdev, + netdev->card_name, + &net_ip, + 1); + } + } + + if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp)) + break; + dp = (grub_efi_device_path_t *) ((char *) dp + len); + } + + return inf; +} + +static grub_efi_net_interface_t * +grub_efi_net_config_from_handle (grub_efi_handle_t *hnd, + struct grub_efi_net_device *netdev, + char **device, + char **path) +{ + grub_efi_pxe_t *pxe = NULL; + + if (hnd == netdev->ip4_pxe_handle) + pxe = netdev->ip4_pxe; + else if (hnd == netdev->ip6_pxe_handle) + pxe = netdev->ip6_pxe; + + if (!pxe) + return (grub_efi_net_config_from_device_path ( + grub_efi_get_device_path (hnd), + netdev, + device, + path)); + + if (pxe->mode->using_ipv6) + { + grub_efi_net_ip_manual_address_t net_ip; + + pxe_get_boot_location_v6 ( + (const struct grub_net_dhcp6_packet *) &pxe->mode->dhcp_ack, + sizeof (pxe->mode->dhcp_ack), + device, + path); + + grub_memcpy (net_ip.ip6.address, pxe->mode->station_ip.v6, sizeof(net_ip.ip6.address)); + net_ip.ip6.prefix_length = GRUB_EFI_IP6_PREFIX_LENGTH; + net_ip.ip6.is_anycast = 0; + net_ip.is_ip6 = 1; + return (grub_efi_net_create_interface (netdev, + netdev->card_name, + &net_ip, + 1)); + } + else + { + grub_efi_net_ip_manual_address_t net_ip; + + pxe_get_boot_location ( + (const struct grub_net_bootp_packet *) &pxe->mode->dhcp_ack, + device, + path, + 1); + + grub_memcpy (net_ip.ip4.address, pxe->mode->station_ip.v4, sizeof (net_ip.ip4.address)); + grub_memcpy (net_ip.ip4.subnet_mask, pxe->mode->subnet_mask.v4, sizeof (net_ip.ip4.subnet_mask)); + net_ip.is_ip6 = 0; + return (grub_efi_net_create_interface (netdev, + netdev->card_name, + &net_ip, + 1)); + } +} + +static const char * +grub_efi_net_var_get_address (struct grub_env_var *var, + const char *val __attribute__ ((unused))) +{ + struct grub_efi_net_device *dev; + + for (dev = net_devices; dev; dev = dev->next) + { + grub_efi_net_interface_t *inf; + + for (inf = dev->net_interfaces; inf; inf = inf->next) + { + char *var_name; + + var_name = grub_xasprintf ("net_%s_ip", inf->name); + if (grub_strcmp (var_name, var->name) == 0) + return efi_net_interface_get_address (inf); + grub_free (var_name); + var_name = grub_xasprintf ("net_%s_mac", inf->name); + if (grub_strcmp (var_name, var->name) == 0) + return efi_net_interface_get_hw_address (inf); + grub_free (var_name); + } + } + + return NULL; +} + +static char * +grub_efi_net_var_set_interface (struct grub_env_var *var __attribute__ ((unused)), + const char *val) +{ + struct grub_efi_net_device *dev; + grub_efi_net_interface_t *inf; + + for (dev = net_devices; dev; dev = dev->next) + for (inf = dev->net_interfaces; inf; inf = inf->next) + if (grub_strcmp (inf->name, val) == 0) + { + net_default_interface = inf; + return grub_strdup (val); + } + + return NULL; +} + +static char * +grub_efi_net_var_set_server (struct grub_env_var *var __attribute__ ((unused)), + const char *val) +{ + grub_free (default_server); + default_server = grub_strdup (val); + return grub_strdup (val); +} + +static const char * +grub_efi_net_var_get_server (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + return default_server ? : ""; +} + +static const char * +grub_efi_net_var_get_ip (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + const char *intf = grub_env_get ("net_default_interface"); + const char *ret = NULL; + if (intf) + { + char *buf = grub_xasprintf ("net_%s_ip", intf); + if (buf) + ret = grub_env_get (buf); + grub_free (buf); + } + return ret; +} + +static const char * +grub_efi_net_var_get_mac (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + const char *intf = grub_env_get ("net_default_interface"); + const char *ret = NULL; + if (intf) + { + char *buf = grub_xasprintf ("net_%s_mac", intf); + if (buf) + ret = grub_env_get (buf); + grub_free (buf); + } + return ret; +} + +static void +grub_efi_net_export_interface_vars (void) +{ + struct grub_efi_net_device *dev; + + for (dev = net_devices; dev; dev = dev->next) + { + grub_efi_net_interface_t *inf; + + for (inf = dev->net_interfaces; inf; inf = inf->next) + { + char *var; + + var = grub_xasprintf ("net_%s_ip", inf->name); + grub_register_variable_hook (var, grub_efi_net_var_get_address, 0); + grub_env_export (var); + grub_free (var); + var = grub_xasprintf ("net_%s_mac", inf->name); + grub_register_variable_hook (var, grub_efi_net_var_get_address, 0); + grub_env_export (var); + grub_free (var); + } + } +} + +static void +grub_efi_net_unset_interface_vars (void) +{ + struct grub_efi_net_device *dev; + + for (dev = net_devices; dev; dev = dev->next) + { + grub_efi_net_interface_t *inf; + + for (inf = dev->net_interfaces; inf; inf = inf->next) + { + char *var; + + var = grub_xasprintf ("net_%s_ip", inf->name); + grub_register_variable_hook (var, 0, 0); + grub_env_unset (var); + grub_free (var); + var = grub_xasprintf ("net_%s_mac", inf->name); + grub_register_variable_hook (var, 0, 0); + grub_env_unset (var); + grub_free (var); + } + } +} + +grub_efi_net_interface_t * +grub_efi_net_create_interface (struct grub_efi_net_device *dev, + const char *interface_name, + grub_efi_net_ip_manual_address_t *net_ip, + int has_subnet) +{ + grub_efi_net_interface_t *inf; + + for (inf = dev->net_interfaces; inf; inf = inf->next) + { + if (inf->prefer_ip6 == net_ip->is_ip6) + break; + } + + if (!inf) + { + inf = grub_malloc (sizeof(*inf)); + inf->name = grub_strdup (interface_name); + inf->prefer_ip6 = net_ip->is_ip6; + inf->dev = dev; + inf->next = dev->net_interfaces; + inf->ip_config = (net_ip->is_ip6) ? efi_net_ip6_config : efi_net_ip4_config ; + dev->net_interfaces = inf; + } + else + { + grub_free (inf->name); + inf->name = grub_strdup (interface_name); + } + + if (!efi_net_interface_set_address (inf, net_ip, has_subnet)) + { + grub_error (GRUB_ERR_BUG, N_("Set Address Failed")); + return NULL; + } + + return inf; +} + +static void +grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, + char **path) +{ + grub_efi_handle_t config_hnd; + + struct grub_efi_net_device *netdev; + grub_efi_net_interface_t *inf; + + config_hnd = grub_efi_locate_device_path (&ip4_config_guid, grub_efi_get_device_path (hnd), NULL); + + if (!config_hnd) + return; + + for (netdev = net_devices; netdev; netdev = netdev->next) + if (netdev->handle == config_hnd) + break; + + if (!netdev) + return; + + if (!(inf = grub_efi_net_config_from_handle (hnd, netdev, device, path))) + return; + + grub_env_set ("net_default_interface", inf->name); + grub_efi_net_export_interface_vars (); +} + +static grub_err_t +grub_efi_netfs_dir (grub_device_t device, const char *path __attribute__ ((unused)), + grub_fs_dir_hook_t hook __attribute__ ((unused)), + void *hook_data __attribute__ ((unused))) +{ + if (!device->net) + return grub_error (GRUB_ERR_BUG, "invalid net device"); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_efi_netfs_open (struct grub_file *file_out __attribute__ ((unused)), + const char *name __attribute__ ((unused))) +{ + struct grub_file *file, *bufio; + + file = grub_malloc (sizeof (*file)); + if (!file) + return grub_errno; + + grub_memcpy (file, file_out, sizeof (struct grub_file)); + file->device->net->name = grub_strdup (name); + + if (!file->device->net->name) + { + grub_free (file); + return grub_errno; + } + + efi_net_interface(open, file, name); + grub_print_error (); + + bufio = grub_bufio_open (file, 32768); + if (!bufio) + { + grub_free (file->device->net->name); + grub_free (file); + return grub_errno; + } + grub_memcpy (file_out, bufio, sizeof (struct grub_file)); + grub_free (bufio); + + return GRUB_ERR_NONE; +} + +static grub_ssize_t +grub_efihttp_chunk_read (grub_file_t file, char *buf, + grub_size_t len, grub_size_t chunk_size) +{ + char *chunk = grub_malloc (chunk_size); + grub_size_t sum = 0; + + while (len) + { + grub_ssize_t rd; + grub_size_t sz = (len > chunk_size) ? chunk_size : len; + + rd = efi_net_interface (read, file, chunk, sz); + + if (rd <= 0) + return rd; + + if (buf) + { + grub_memcpy (buf, chunk, rd); + buf += rd; + } + sum += rd; + len -= rd; + } + + grub_free (chunk); + return sum; +} + +static grub_ssize_t +grub_efi_netfs_read (grub_file_t file __attribute__ ((unused)), + char *buf __attribute__ ((unused)), grub_size_t len __attribute__ ((unused))) +{ + if (file->offset > file->device->net->offset) + { + grub_efihttp_chunk_read (file, NULL, file->offset - file->device->net->offset, 10240); + } + else if (file->offset < file->device->net->offset) + { + efi_net_interface (close, file); + efi_net_interface (open, file, file->device->net->name); + if (file->offset) + grub_efihttp_chunk_read (file, NULL, file->offset, 10240); + } + + return efi_net_interface (read, file, buf, len); +} + +static grub_err_t +grub_efi_netfs_close (grub_file_t file) +{ + efi_net_interface (close, file); + return GRUB_ERR_NONE; +} + +static grub_efi_handle_t +grub_efi_service_binding (grub_efi_handle_t dev, grub_efi_guid_t *service_binding_guid) +{ + grub_efi_service_binding_t *service; + grub_efi_status_t status; + grub_efi_handle_t child_dev = NULL; + + service = grub_efi_open_protocol (dev, service_binding_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (!service) + { + grub_error (GRUB_ERR_IO, N_("couldn't open efi service binding protocol")); + return NULL; + } + + status = efi_call_2 (service->create_child, service, &child_dev); + if (status != GRUB_EFI_SUCCESS) + { + grub_error (GRUB_ERR_IO, N_("Failed to create child device of http service %x"), status); + return NULL; + } + + return child_dev; +} + +static grub_err_t +grub_efi_net_parse_address (const char *address, + grub_efi_ip4_config2_manual_address_t *ip4, + grub_efi_ip6_config_manual_address_t *ip6, + int *is_ip6, + int *has_cidr) +{ + const char *rest; + + if (grub_efi_string_to_ip4_address (address, &ip4->address, &rest)) + { + *is_ip6 = 0; + if (*rest == '/') + { + grub_uint32_t subnet_mask_size; + + subnet_mask_size = grub_strtoul (rest + 1, (char **) &rest, 0); + + if (!grub_errno && subnet_mask_size <= 32 && *rest == 0) + { + grub_uint32_t subnet_mask; + + subnet_mask = grub_cpu_to_be32 ((0xffffffffU << (32 - subnet_mask_size))); + grub_memcpy (ip4->subnet_mask, &subnet_mask, sizeof (ip4->subnet_mask)); + if (has_cidr) + *has_cidr = 1; + return GRUB_ERR_NONE; + } + } + else if (*rest == 0) + { + grub_uint32_t subnet_mask = 0xffffffffU; + grub_memcpy (ip4->subnet_mask, &subnet_mask, sizeof (ip4->subnet_mask)); + if (has_cidr) + *has_cidr = 0; + return GRUB_ERR_NONE; + } + } + else if (grub_efi_string_to_ip6_address (address, &ip6->address, &rest)) + { + *is_ip6 = 1; + if (*rest == '/') + { + grub_efi_uint8_t prefix_length; + + prefix_length = grub_strtoul (rest + 1, (char **) &rest, 0); + if (!grub_errno && prefix_length <= 128 && *rest == 0) + { + ip6->prefix_length = prefix_length; + ip6->is_anycast = 0; + if (has_cidr) + *has_cidr = 1; + return GRUB_ERR_NONE; + } + } + else if (*rest == 0) + { + ip6->prefix_length = 128; + ip6->is_anycast = 0; + if (has_cidr) + *has_cidr = 0; + return GRUB_ERR_NONE; + } + } + + return grub_error (GRUB_ERR_NET_BAD_ADDRESS, + N_("unrecognised network address `%s'"), + address); +} + +static grub_efi_net_interface_t * +match_route (const char *server) +{ + grub_err_t err; + grub_efi_ip4_config2_manual_address_t ip4; + grub_efi_ip6_config_manual_address_t ip6; + grub_efi_net_interface_t *inf; + int is_ip6 = 0; + + err = grub_efi_net_parse_address (server, &ip4, &ip6, &is_ip6, 0); + + if (err) + { + grub_print_error (); + return NULL; + } + + if (is_ip6) + { + struct grub_efi_net_device *dev; + grub_efi_net_ip_address_t addr; + + grub_memcpy (addr.ip6, ip6.address, sizeof(ip6.address)); + + for (dev = net_devices; dev; dev = dev->next) + if ((inf = efi_net_ip6_config->best_interface (dev, &addr))) + return inf; + } + else + { + struct grub_efi_net_device *dev; + grub_efi_net_ip_address_t addr; + + grub_memcpy (addr.ip4, ip4.address, sizeof(ip4.address)); + + for (dev = net_devices; dev; dev = dev->next) + if ((inf = efi_net_ip4_config->best_interface (dev, &addr))) + return inf; + } + + return 0; +} + +static void +grub_efi_net_add_pxebc_to_cards (void) +{ + grub_efi_uintn_t num_handles; + grub_efi_handle_t *handles; + grub_efi_handle_t *handle; + + handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &pxe_io_guid, + 0, &num_handles); + if (!handles) + return; + + for (handle = handles; num_handles--; handle++) + { + grub_efi_device_path_t *dp, *ddp, *ldp; + grub_efi_pxe_t *pxe; + struct grub_efi_net_device *d; + int is_ip6 = 0; + + dp = grub_efi_get_device_path (*handle); + if (!dp) + continue; + + ddp = grub_efi_duplicate_device_path (dp); + ldp = grub_efi_find_last_device_path (ddp); + + if (ldp->type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + && ldp->subtype == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE) + { + ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + ldp->length = sizeof (*ldp); + } + else if (ldp->type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + && ldp->subtype == GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE) + { + is_ip6 = 1; + ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + ldp->length = sizeof (*ldp); + } + + for (d = net_devices; d; d = d->next) + if (grub_efi_compare_device_paths (ddp, grub_efi_get_device_path (d->handle)) == 0) + break; + + if (!d) + { + grub_free (ddp); + continue; + } + + pxe = grub_efi_open_protocol (*handle, &pxe_io_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (!pxe) + { + grub_free (ddp); + continue; + } + + if (is_ip6) + { + d->ip6_pxe_handle = *handle; + d->ip6_pxe = pxe; + } + else + { + d->ip4_pxe_handle = *handle; + d->ip4_pxe = pxe; + } + + grub_free (ddp); + } + + grub_free (handles); +} + +static void +set_ip_policy_to_static (void) +{ + struct grub_efi_net_device *dev; + + for (dev = net_devices; dev; dev = dev->next) + { + grub_efi_ip4_config2_policy_t ip4_policy = GRUB_EFI_IP4_CONFIG2_POLICY_STATIC; + + if (efi_call_4 (dev->ip4_config->set_data, dev->ip4_config, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_POLICY, + sizeof (ip4_policy), &ip4_policy) != GRUB_EFI_SUCCESS) + grub_dprintf ("efinetfs", "could not set GRUB_EFI_IP4_CONFIG2_POLICY_STATIC on dev `%s'", dev->card_name); + + if (dev->ip6_config) + { + grub_efi_ip6_config_policy_t ip6_policy = GRUB_EFI_IP6_CONFIG_POLICY_MANUAL; + + if (efi_call_4 (dev->ip6_config->set_data, dev->ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_POLICY, + sizeof (ip6_policy), &ip6_policy) != GRUB_EFI_SUCCESS) + grub_dprintf ("efinetfs", "could not set GRUB_EFI_IP6_CONFIG_POLICY_MANUAL on dev `%s'", dev->card_name); + } + } +} + +/* FIXME: Do not fail if the card did not support any of the protocol (Eg http) */ +static void +grub_efi_net_find_cards (void) +{ + grub_efi_uintn_t num_handles; + grub_efi_handle_t *handles; + grub_efi_handle_t *handle; + int id; + + handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &ip4_config_guid, + 0, &num_handles); + if (!handles) + return; + + for (id = 0, handle = handles; num_handles--; handle++, id++) + { + grub_efi_device_path_t *dp; + grub_efi_ip4_config2_protocol_t *ip4_config; + grub_efi_ip6_config_protocol_t *ip6_config; + grub_efi_handle_t http_handle; + grub_efi_http_t *http; + grub_efi_handle_t dhcp4_handle; + grub_efi_dhcp4_protocol_t *dhcp4; + grub_efi_handle_t dhcp6_handle; + grub_efi_dhcp6_protocol_t *dhcp6; + + struct grub_efi_net_device *d; + + dp = grub_efi_get_device_path (*handle); + if (!dp) + continue; + + ip4_config = grub_efi_open_protocol (*handle, &ip4_config_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (!ip4_config) + continue; + + ip6_config = grub_efi_open_protocol (*handle, &ip6_config_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + http_handle = grub_efi_service_binding (*handle, &http_service_binding_guid); + grub_errno = GRUB_ERR_NONE; + http = (http_handle) + ? grub_efi_open_protocol (http_handle, &http_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL) + : NULL; + + dhcp4_handle = grub_efi_service_binding (*handle, &dhcp4_service_binding_guid); + grub_errno = GRUB_ERR_NONE; + dhcp4 = (dhcp4_handle) + ? grub_efi_open_protocol (dhcp4_handle, &dhcp4_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL) + : NULL; + + + dhcp6_handle = grub_efi_service_binding (*handle, &dhcp6_service_binding_guid); + grub_errno = GRUB_ERR_NONE; + dhcp6 = (dhcp6_handle) + ? grub_efi_open_protocol (dhcp6_handle, &dhcp6_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL) + : NULL; + + d = grub_malloc (sizeof (*d)); + if (!d) + { + grub_free (handles); + while (net_devices) + { + d = net_devices->next; + grub_free (net_devices); + net_devices = d; + } + return; + } + d->handle = *handle; + d->ip4_config = ip4_config; + d->ip6_config = ip6_config; + d->http_handle = http_handle; + d->http = http; + d->dhcp4_handle = dhcp4_handle; + d->dhcp4 = dhcp4; + d->dhcp6_handle = dhcp6_handle; + d->dhcp6 = dhcp6; + d->next = net_devices; + d->card_name = grub_xasprintf ("efinet%d", id); + d->net_interfaces = NULL; + net_devices = d; + } + + grub_efi_net_add_pxebc_to_cards (); + grub_free (handles); + set_ip_policy_to_static (); +} + +static void +listroutes_ip4 (struct grub_efi_net_device *netdev) +{ + char **routes; + + routes = NULL; + + if ((routes = efi_net_ip4_config->get_route_table (netdev))) + { + char **r; + + for (r = routes; *r; ++r) + grub_printf ("%s\n", *r); + } + + if (routes) + { + char **r; + + for (r = routes; *r; ++r) + grub_free (*r); + grub_free (routes); + } +} + +static void +listroutes_ip6 (struct grub_efi_net_device *netdev) +{ + char **routes; + + routes = NULL; + + if ((routes = efi_net_ip6_config->get_route_table (netdev))) + { + char **r; + + for (r = routes; *r; ++r) + grub_printf ("%s\n", *r); + } + + if (routes) + { + char **r; + + for (r = routes; *r; ++r) + grub_free (*r); + grub_free (routes); + } +} + +static grub_err_t +grub_cmd_efi_listroutes (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + struct grub_efi_net_device *netdev; + + for (netdev = net_devices; netdev; netdev = netdev->next) + { + listroutes_ip4 (netdev); + listroutes_ip6 (netdev); + } + + return GRUB_ERR_NONE; +} +static grub_err_t +grub_cmd_efi_listcards (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + struct grub_efi_net_device *dev; + + for (dev = net_devices; dev; dev = dev->next) + { + char *hw_addr; + + hw_addr = efi_net_ip4_config->get_hw_address (dev); + + if (hw_addr) + { + grub_printf ("%s %s\n", dev->card_name, hw_addr); + grub_free (hw_addr); + } + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_efi_listaddrs (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + struct grub_efi_net_device *dev; + grub_efi_net_interface_t *inf; + + for (dev = net_devices; dev; dev = dev->next) + for (inf = dev->net_interfaces; inf; inf = inf->next) + { + char *hw_addr = NULL; + char *addr = NULL; + + if ((hw_addr = efi_net_interface_get_hw_address (inf)) + && (addr = efi_net_interface_get_address (inf))) + grub_printf ("%s %s %s\n", inf->name, hw_addr, addr); + + if (hw_addr) + grub_free (hw_addr); + if (addr) + grub_free (addr); + } + + return GRUB_ERR_NONE; +} + +/* FIXME: support MAC specifying. */ +static grub_err_t +grub_cmd_efi_addaddr (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_efi_net_device *dev; + grub_err_t err; + grub_efi_ip4_config2_manual_address_t ip4; + grub_efi_ip6_config_manual_address_t ip6; + grub_efi_net_ip_manual_address_t net_ip; + int is_ip6 = 0; + int cidr = 0; + + if (argc != 3) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("three arguments expected")); + + for (dev = net_devices; dev; dev = dev->next) + { + if (grub_strcmp (dev->card_name, args[1]) == 0) + break; + } + + if (!dev) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("card not found")); + + err = grub_efi_net_parse_address (args[2], &ip4, &ip6, &is_ip6, &cidr); + + if (err) + return err; + + net_ip.is_ip6 = is_ip6; + if (is_ip6) + grub_memcpy (&net_ip.ip6, &ip6, sizeof(net_ip.ip6)); + else + grub_memcpy (&net_ip.ip4, &ip4, sizeof(net_ip.ip4)); + + if (!grub_efi_net_create_interface (dev, + args[0], + &net_ip, + cidr)) + return grub_errno; + + return GRUB_ERR_NONE; +} + +static struct grub_fs grub_efi_netfs; + +static grub_net_t +grub_net_open_real (const char *name __attribute__ ((unused))) +{ + grub_size_t protnamelen; + const char *protname, *server; + grub_net_t ret; + + net_interface = NULL; + + if (grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) == 0) + { + protname = "tftp"; + protnamelen = sizeof ("tftp") - 1; + server = name + sizeof ("pxe:") - 1; + } + else if (grub_strcmp (name, "pxe") == 0) + { + protname = "tftp"; + protnamelen = sizeof ("tftp") - 1; + server = default_server; + } + else + { + const char *comma; + + comma = grub_strchr (name, ','); + if (comma) + { + protnamelen = comma - name; + server = comma + 1; + protname = name; + } + else + { + protnamelen = grub_strlen (name); + server = default_server; + protname = name; + } + } + + if (!server) + { + grub_error (GRUB_ERR_NET_BAD_ADDRESS, + N_("no server is specified")); + return NULL; + } + + /*FIXME: Use DNS translate name to address */ + net_interface = match_route (server); + + /*XXX: should we check device with default gateway ? */ + if (!net_interface && !(net_interface = net_default_interface)) + { + grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' no route found"), + name); + return NULL; + } + + if ((protnamelen == (sizeof ("https") - 1) + && grub_memcmp ("https", protname, protnamelen) == 0)) + { + net_interface->io = &io_http; + net_interface->io_type = 1; + } + else if ((protnamelen == (sizeof ("http") - 1) + && grub_memcmp ("http", protname, protnamelen) == 0)) + { + net_interface->io = &io_http; + net_interface->io_type = 0; + } + else if (protnamelen == (sizeof ("tftp") - 1) + && grub_memcmp ("tftp", protname, protnamelen) == 0) + { + net_interface->io = &io_pxe; + net_interface->io_type = 0; + } + else + { + grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' not found"), + name); + return NULL; + } + + /*XXX: Should we try to avoid doing excess "reconfigure" here ??? */ + efi_net_interface (configure); + + ret = grub_zalloc (sizeof (*ret)); + if (!ret) + return NULL; + + ret->server = grub_strdup (server); + if (!ret->server) + { + grub_free (ret); + return NULL; + } + + ret->fs = &grub_efi_netfs; + return ret; +} +#if 0 +static grub_command_t cmd_efi_lsaddr; +static grub_command_t cmd_efi_lscards; +static grub_command_t cmd_efi_lsroutes; +static grub_command_t cmd_efi_addaddr; +#endif + +static struct grub_fs grub_efi_netfs = + { + .name = "efi netfs", + .fs_dir = grub_efi_netfs_dir, + .fs_open = grub_efi_netfs_open, + .fs_read = grub_efi_netfs_read, + .fs_close = grub_efi_netfs_close, + .fs_label = NULL, + .fs_uuid = NULL, + .fs_mtime = NULL, + }; + +int +grub_efi_net_boot_from_https (void) +{ + grub_efi_loaded_image_t *image = NULL; + grub_efi_device_path_t *dp; + + image = grub_efi_get_loaded_image (grub_efi_image_handle); + if (!image) + return 0; + + dp = grub_efi_get_device_path (image->device_handle); + + while (1) + { + grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp); + grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); + grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp); + + if ((type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE) + && (subtype == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)) + { + grub_efi_uri_device_path_t *uri_dp = (grub_efi_uri_device_path_t *) dp; + return (grub_strncmp ((const char*)uri_dp->uri, "https://", sizeof ("https://") - 1) == 0) ? 1 : 0; + } + + if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp)) + break; + dp = (grub_efi_device_path_t *) ((char *) dp + len); + } + + return 0; +} + +int +grub_efi_net_boot_from_opa (void) +{ + grub_efi_loaded_image_t *image = NULL; + grub_efi_device_path_t *dp; + + image = grub_efi_get_loaded_image (grub_efi_image_handle); + if (!image) + return 0; + + dp = grub_efi_get_device_path (image->device_handle); + + while (1) + { + grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp); + grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); + grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp); + + if ((type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE) + && (subtype == GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE)) + { + grub_efi_mac_address_device_path_t *mac_dp = (grub_efi_mac_address_device_path_t *)dp; + return (mac_dp->if_type == 0xC7) ? 1 : 0; + } + + if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp)) + break; + dp = (grub_efi_device_path_t *) ((char *) dp + len); + } + + return 0; +} + +static char * +grub_env_write_readonly (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + return NULL; +} + +grub_command_func_t grub_efi_net_list_routes = grub_cmd_efi_listroutes; +grub_command_func_t grub_efi_net_list_cards = grub_cmd_efi_listcards; +grub_command_func_t grub_efi_net_list_addrs = grub_cmd_efi_listaddrs; +grub_command_func_t grub_efi_net_add_addr = grub_cmd_efi_addaddr; + +int +grub_efi_net_fs_init () +{ + grub_efi_net_find_cards (); + grub_efi_net_config = grub_efi_net_config_real; + grub_net_open = grub_net_open_real; + grub_register_variable_hook ("net_default_server", grub_efi_net_var_get_server, + grub_efi_net_var_set_server); + grub_env_export ("net_default_server"); + grub_register_variable_hook ("pxe_default_server", grub_efi_net_var_get_server, + grub_efi_net_var_set_server); + grub_env_export ("pxe_default_server"); + grub_register_variable_hook ("net_default_interface", 0, + grub_efi_net_var_set_interface); + grub_env_export ("net_default_interface"); + grub_register_variable_hook ("net_default_ip", grub_efi_net_var_get_ip, + 0); + grub_env_export ("net_default_ip"); + grub_register_variable_hook ("net_default_mac", grub_efi_net_var_get_mac, + 0); + grub_env_export ("net_default_mac"); + + grub_env_set ("grub_netfs_type", "efi"); + grub_register_variable_hook ("grub_netfs_type", 0, grub_env_write_readonly); + grub_env_export ("grub_netfs_type"); + + return 1; +} + +void +grub_efi_net_fs_fini (void) +{ + grub_env_unset ("grub_netfs_type"); + grub_efi_net_unset_interface_vars (); + grub_register_variable_hook ("net_default_server", 0, 0); + grub_env_unset ("net_default_server"); + grub_register_variable_hook ("net_default_interface", 0, 0); + grub_env_unset ("net_default_interface"); + grub_register_variable_hook ("pxe_default_server", 0, 0); + grub_env_unset ("pxe_default_server"); + grub_register_variable_hook ("net_default_ip", 0, 0); + grub_env_unset ("net_default_ip"); + grub_register_variable_hook ("net_default_mac", 0, 0); + grub_env_unset ("net_default_mac"); + grub_efi_net_config = NULL; + grub_net_open = NULL; + grub_fs_unregister (&grub_efi_netfs); +} diff --git a/grub-core/net/efi/pxe.c b/grub-core/net/efi/pxe.c new file mode 100644 index 0000000000..531949cba5 --- /dev/null +++ b/grub-core/net/efi/pxe.c @@ -0,0 +1,424 @@ + +#include +#include +#include +#include +#include + +static grub_efi_ip6_config_manual_address_t * +efi_ip6_config_manual_address (grub_efi_ip6_config_protocol_t *ip6_config) +{ + grub_efi_uintn_t sz; + grub_efi_status_t status; + grub_efi_ip6_config_manual_address_t *manual_address; + + sz = sizeof (*manual_address); + manual_address = grub_malloc (sz); + if (!manual_address) + return NULL; + + status = efi_call_4 (ip6_config->get_data, ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS, + &sz, manual_address); + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (manual_address); + return NULL; + } + + return manual_address; +} + +static grub_efi_ip4_config2_manual_address_t * +efi_ip4_config_manual_address (grub_efi_ip4_config2_protocol_t *ip4_config) +{ + grub_efi_uintn_t sz; + grub_efi_status_t status; + grub_efi_ip4_config2_manual_address_t *manual_address; + + sz = sizeof (*manual_address); + manual_address = grub_malloc (sz); + if (!manual_address) + return NULL; + + status = efi_call_4 (ip4_config->get_data, ip4_config, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, + &sz, manual_address); + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (manual_address); + return NULL; + } + + return manual_address; +} + +static void +pxe_configure (struct grub_efi_net_device *dev, int prefer_ip6) +{ + grub_efi_pxe_t *pxe = (prefer_ip6) ? dev->ip6_pxe : dev->ip4_pxe; + + grub_efi_pxe_mode_t *mode = pxe->mode; + + if (!mode->started) + { + grub_efi_status_t status; + status = efi_call_2 (pxe->start, pxe, prefer_ip6); + + if (status != GRUB_EFI_SUCCESS) + grub_printf ("Couldn't start PXE\n"); + } + +#if 0 + grub_printf ("PXE STARTED: %u\n", mode->started); + grub_printf ("PXE USING IPV6: %u\n", mode->using_ipv6); +#endif + + if (mode->using_ipv6) + { + grub_efi_ip6_config_manual_address_t *manual_address; + manual_address = efi_ip6_config_manual_address (dev->ip6_config); + + if (manual_address && + grub_memcmp (manual_address->address, mode->station_ip.v6, sizeof (manual_address->address)) != 0) + { + grub_efi_status_t status; + grub_efi_pxe_ip_address_t station_ip; + + grub_memcpy (station_ip.v6.addr, manual_address->address, sizeof (station_ip.v6.addr)); + status = efi_call_3 (pxe->set_station_ip, pxe, &station_ip, NULL); + + if (status != GRUB_EFI_SUCCESS) + grub_printf ("Couldn't set station ip\n"); + + grub_free (manual_address); + } + } + else + { + grub_efi_ip4_config2_manual_address_t *manual_address; + manual_address = efi_ip4_config_manual_address (dev->ip4_config); + + if (manual_address && + grub_memcmp (manual_address->address, mode->station_ip.v4, sizeof (manual_address->address)) != 0) + { + grub_efi_status_t status; + grub_efi_pxe_ip_address_t station_ip; + grub_efi_pxe_ip_address_t subnet_mask; + + grub_memcpy (station_ip.v4.addr, manual_address->address, sizeof (station_ip.v4.addr)); + grub_memcpy (subnet_mask.v4.addr, manual_address->subnet_mask, sizeof (subnet_mask.v4.addr)); + + status = efi_call_3 (pxe->set_station_ip, pxe, &station_ip, &subnet_mask); + + if (status != GRUB_EFI_SUCCESS) + grub_printf ("Couldn't set station ip\n"); + + grub_free (manual_address); + } + } + +#if 0 + if (mode->using_ipv6) + { + grub_printf ("PXE STATION IP: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n", + mode->station_ip.v6.addr[0], + mode->station_ip.v6.addr[1], + mode->station_ip.v6.addr[2], + mode->station_ip.v6.addr[3], + mode->station_ip.v6.addr[4], + mode->station_ip.v6.addr[5], + mode->station_ip.v6.addr[6], + mode->station_ip.v6.addr[7], + mode->station_ip.v6.addr[8], + mode->station_ip.v6.addr[9], + mode->station_ip.v6.addr[10], + mode->station_ip.v6.addr[11], + mode->station_ip.v6.addr[12], + mode->station_ip.v6.addr[13], + mode->station_ip.v6.addr[14], + mode->station_ip.v6.addr[15]); + } + else + { + grub_printf ("PXE STATION IP: %d.%d.%d.%d\n", + mode->station_ip.v4.addr[0], + mode->station_ip.v4.addr[1], + mode->station_ip.v4.addr[2], + mode->station_ip.v4.addr[3]); + grub_printf ("PXE SUBNET MASK: %d.%d.%d.%d\n", + mode->subnet_mask.v4.addr[0], + mode->subnet_mask.v4.addr[1], + mode->subnet_mask.v4.addr[2], + mode->subnet_mask.v4.addr[3]); + } +#endif + + /* TODO: Set The Station IP to the IP2 Config */ +} + +static int +parse_ip6 (const char *val, grub_uint64_t *ip, const char **rest) +{ + grub_uint16_t newip[8]; + const char *ptr = val; + int word, quaddot = -1; + int bracketed = 0; + + if (ptr[0] == '[') { + bracketed = 1; + ptr++; + } + + if (ptr[0] == ':' && ptr[1] != ':') + return 0; + if (ptr[0] == ':') + ptr++; + + for (word = 0; word < 8; word++) + { + unsigned long t; + if (*ptr == ':') + { + quaddot = word; + word--; + ptr++; + continue; + } + t = grub_strtoul (ptr, (char **) &ptr, 16); + if (grub_errno) + { + grub_errno = GRUB_ERR_NONE; + break; + } + if (t & ~0xffff) + return 0; + newip[word] = grub_cpu_to_be16 (t); + if (*ptr != ':') + break; + ptr++; + } + if (quaddot == -1 && word < 7) + return 0; + if (quaddot != -1) + { + grub_memmove (&newip[quaddot + 7 - word], &newip[quaddot], + (word - quaddot + 1) * sizeof (newip[0])); + grub_memset (&newip[quaddot], 0, (7 - word) * sizeof (newip[0])); + } + grub_memcpy (ip, newip, 16); + if (bracketed && *ptr == ']') { + ptr++; + } + if (rest) + *rest = ptr; + return 1; +} + +static grub_err_t +pxe_open (struct grub_efi_net_device *dev, + int prefer_ip6, + grub_file_t file, + const char *filename, + int type __attribute__((unused))) +{ + int i; + char *p; + grub_efi_status_t status; + grub_efi_pxe_ip_address_t server_ip; + grub_efi_uint64_t file_size = 0; + grub_efi_pxe_t *pxe = (prefer_ip6) ? dev->ip6_pxe : dev->ip4_pxe; + + if (pxe->mode->using_ipv6) + { + const char *rest; + grub_uint64_t ip6[2]; + if (parse_ip6 (file->device->net->server, ip6, &rest) && *rest == 0) + grub_memcpy (server_ip.v6.addr, ip6, sizeof (server_ip.v6.addr)); + /* TODO: ERROR Handling Here */ +#if 0 + grub_printf ("PXE SERVER IP: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n", + server_ip.v6.addr[0], + server_ip.v6.addr[1], + server_ip.v6.addr[2], + server_ip.v6.addr[3], + server_ip.v6.addr[4], + server_ip.v6.addr[5], + server_ip.v6.addr[6], + server_ip.v6.addr[7], + server_ip.v6.addr[8], + server_ip.v6.addr[9], + server_ip.v6.addr[10], + server_ip.v6.addr[11], + server_ip.v6.addr[12], + server_ip.v6.addr[13], + server_ip.v6.addr[14], + server_ip.v6.addr[15]); +#endif + } + else + { + for (i = 0, p = file->device->net->server; i < 4; ++i, ++p) + server_ip.v4.addr[i] = grub_strtoul (p, &p, 10); + } + + status = efi_call_10 (pxe->mtftp, + pxe, + GRUB_EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + NULL, + 0, + &file_size, + NULL, + &server_ip, + (grub_efi_char8_t *)filename, + NULL, + 0); + + if (status != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_IO, "Couldn't get file size"); + + file->size = (grub_off_t)file_size; + file->not_easily_seekable = 0; + file->data = 0; + file->device->net->offset = 0; + + return GRUB_ERR_NONE; +} + +static grub_err_t +pxe_close (struct grub_efi_net_device *dev __attribute__((unused)), + int prefer_ip6 __attribute__((unused)), + grub_file_t file __attribute__((unused))) +{ + file->offset = 0; + file->size = 0; + file->device->net->offset = 0; + + if (file->data) + { + grub_free (file->data); + file->data = NULL; + } + + return GRUB_ERR_NONE; +} + +static grub_ssize_t +pxe_read (struct grub_efi_net_device *dev, + int prefer_ip6, + grub_file_t file, + char *buf, + grub_size_t len) +{ + int i; + char *p; + grub_efi_status_t status; + grub_efi_pxe_t *pxe = (prefer_ip6) ? dev->ip6_pxe : dev->ip4_pxe; + grub_efi_uint64_t bufsz = len; + grub_efi_pxe_ip_address_t server_ip; + char *buf2 = NULL; + + if (file->data) + { + /* TODO: RANGE Check for offset and file size */ + grub_memcpy (buf, (char*)file->data + file->device->net->offset, len); + file->device->net->offset += len; + return len; + } + + if (file->device->net->offset) + { + grub_error (GRUB_ERR_BUG, "No Offet Read Possible"); + grub_print_error (); + return 0; + } + + if (pxe->mode->using_ipv6) + { + const char *rest; + grub_uint64_t ip6[2]; + if (parse_ip6 (file->device->net->server, ip6, &rest) && *rest == 0) + grub_memcpy (server_ip.v6.addr, ip6, sizeof (server_ip.v6.addr)); + /* TODO: ERROR Handling Here */ + } + else + { + for (i = 0, p = file->device->net->server; i < 4; ++i, ++p) + server_ip.v4.addr[i] = grub_strtoul (p, &p, 10); + } + + status = efi_call_10 (pxe->mtftp, + pxe, + GRUB_EFI_PXE_BASE_CODE_TFTP_READ_FILE, + buf, + 0, + &bufsz, + NULL, + &server_ip, + (grub_efi_char8_t *)file->device->net->name, + NULL, + 0); + + if (bufsz != file->size) + { + grub_error (GRUB_ERR_BUG, "Short read should not happen here"); + grub_print_error (); + return 0; + } + + if (status == GRUB_EFI_BUFFER_TOO_SMALL) + { + + buf2 = grub_malloc (bufsz); + + if (!buf2) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "ERROR OUT OF MEMORY"); + grub_print_error (); + return 0; + } + + status = efi_call_10 (pxe->mtftp, + pxe, + GRUB_EFI_PXE_BASE_CODE_TFTP_READ_FILE, + buf2, + 0, + &bufsz, + NULL, + &server_ip, + (grub_efi_char8_t *)file->device->net->name, + NULL, + 0); + } + + if (status != GRUB_EFI_SUCCESS) + { + if (buf2) + grub_free (buf2); + + grub_error (GRUB_ERR_IO, "Failed to Read File"); + grub_print_error (); + return 0; + } + + if (buf2) + grub_memcpy (buf, buf2, len); + + file->device->net->offset = len; + + if (buf2) + file->data = buf2; + + return len; +} + +struct grub_efi_net_io io_pxe = + { + .configure = pxe_configure, + .open = pxe_open, + .read = pxe_read, + .close = pxe_close + }; + diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 0ce5e675ed..55aed92722 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -32,6 +32,9 @@ #include #include #include +#ifdef GRUB_MACHINE_EFI +#include +#endif GRUB_MOD_LICENSE ("GPLv3+"); @@ -2033,8 +2036,49 @@ static grub_command_t cmd_addaddr, cmd_deladdr, cmd_addroute, cmd_delroute; static grub_command_t cmd_lsroutes, cmd_lscards; static grub_command_t cmd_lsaddr, cmd_slaac; +#ifdef GRUB_MACHINE_EFI + +static enum { + INIT_MODE_NONE, + INIT_MODE_GRUB, + INIT_MODE_EFI +} init_mode; + +static grub_command_t cmd_bootp, cmd_bootp6; + +#endif + GRUB_MOD_INIT(net) { +#ifdef GRUB_MACHINE_EFI + if (grub_net_open) + return; + + if ((grub_efi_net_boot_from_https () || grub_efi_net_boot_from_opa ()) + && grub_efi_net_fs_init ()) + { + cmd_lsroutes = grub_register_command ("net_ls_routes", grub_efi_net_list_routes, + "", N_("list network routes")); + cmd_lscards = grub_register_command ("net_ls_cards", grub_efi_net_list_cards, + "", N_("list network cards")); + cmd_lsaddr = grub_register_command ("net_ls_addr", grub_efi_net_list_addrs, + "", N_("list network addresses")); + cmd_addaddr = grub_register_command ("net_add_addr", grub_efi_net_add_addr, + /* TRANSLATORS: HWADDRESS stands for + "hardware address". */ + N_("SHORTNAME CARD ADDRESS [HWADDRESS]"), + N_("Add a network address.")); + cmd_bootp = grub_register_command ("net_bootp", grub_efi_net_bootp, + N_("[CARD]"), + N_("perform a bootp autoconfiguration")); + cmd_bootp6 = grub_register_command ("net_bootp6", grub_efi_net_bootp6, + N_("[CARD]"), + N_("perform a bootp autoconfiguration")); + init_mode = INIT_MODE_EFI; + return; + } +#endif + grub_register_variable_hook ("net_default_server", defserver_get_env, defserver_set_env); grub_env_export ("net_default_server"); @@ -2082,10 +2126,37 @@ GRUB_MOD_INIT(net) grub_net_restore_hw, GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK); grub_net_poll_cards_idle = grub_net_poll_cards_idle_real; + +#ifdef GRUB_MACHINE_EFI + grub_env_set ("grub_netfs_type", "grub"); + grub_register_variable_hook ("grub_netfs_type", 0, grub_env_write_readonly); + grub_env_export ("grub_netfs_type"); + init_mode = INIT_MODE_GRUB; +#endif + } GRUB_MOD_FINI(net) { + +#ifdef GRUB_MACHINE_EFI + if (init_mode == INIT_MODE_NONE) + return; + + if (init_mode == INIT_MODE_EFI) + { + grub_unregister_command (cmd_lsroutes); + grub_unregister_command (cmd_lscards); + grub_unregister_command (cmd_lsaddr); + grub_unregister_command (cmd_addaddr); + grub_unregister_command (cmd_bootp); + grub_unregister_command (cmd_bootp6); + grub_efi_net_fs_fini (); + init_mode = INIT_MODE_NONE; + return; + } +#endif + grub_register_variable_hook ("net_default_server", 0, 0); grub_register_variable_hook ("pxe_default_server", 0, 0); @@ -2104,4 +2175,7 @@ GRUB_MOD_FINI(net) grub_net_fini_hw (0); grub_loader_unregister_preboot_hook (fini_hnd); grub_net_poll_cards_idle = grub_net_poll_cards_idle_real; +#ifdef GRUB_MACHINE_EFI + init_mode = INIT_MODE_NONE; +#endif } diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index 0b490195ad..f431f49973 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -622,6 +622,23 @@ typedef union typedef grub_efi_uint64_t grub_efi_physical_address_t; typedef grub_efi_uint64_t grub_efi_virtual_address_t; +typedef struct { + grub_uint8_t addr[4]; +} grub_efi_pxe_ipv4_address_t; + +typedef struct { + grub_uint8_t addr[16]; +} grub_efi_pxe_ipv6_address_t; + +typedef struct { + grub_uint8_t addr[32]; +} grub_efi_pxe_mac_address_t; + +typedef union { + grub_uint32_t addr[4]; + grub_efi_pxe_ipv4_address_t v4; + grub_efi_pxe_ipv6_address_t v6; +} grub_efi_pxe_ip_address_t; struct grub_efi_guid { @@ -889,6 +906,8 @@ struct grub_efi_ipv6_device_path grub_efi_uint16_t remote_port; grub_efi_uint16_t protocol; grub_efi_uint8_t static_ip_address; + grub_efi_uint8_t prefix_length; + grub_efi_ipv6_address_t gateway_ip_address; } GRUB_PACKED; typedef struct grub_efi_ipv6_device_path grub_efi_ipv6_device_path_t; @@ -938,6 +957,15 @@ struct grub_efi_uri_device_path } GRUB_PACKED; typedef struct grub_efi_uri_device_path grub_efi_uri_device_path_t; +#define GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE 31 +struct grub_efi_dns_device_path +{ + grub_efi_device_path_t header; + grub_efi_uint8_t is_ipv6; + grub_efi_pxe_ip_address_t dns_server_ip[0]; +} GRUB_PACKED; +typedef struct grub_efi_dns_device_path grub_efi_dns_device_path_t; + #define GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE 10 /* Media Device Path. */ @@ -1020,6 +1048,23 @@ struct grub_efi_bios_device_path } GRUB_PACKED; typedef struct grub_efi_bios_device_path grub_efi_bios_device_path_t; +/* Service Binding definitions */ +struct grub_efi_service_binding; + +typedef grub_efi_status_t +(*grub_efi_service_binding_create_child) (struct grub_efi_service_binding *this, + grub_efi_handle_t *child_handle); + +typedef grub_efi_status_t +(*grub_efi_service_binding_destroy_child) (struct grub_efi_service_binding *this, + grub_efi_handle_t *child_handle); + +typedef struct grub_efi_service_binding +{ + grub_efi_service_binding_create_child create_child; + grub_efi_service_binding_destroy_child destroy_child; +} grub_efi_service_binding_t; + struct grub_efi_open_protocol_information_entry { grub_efi_handle_t agent_handle; @@ -1569,23 +1614,27 @@ typedef struct grub_efi_pxe_tftp_error grub_efi_char8_t error_string[127]; } grub_efi_pxe_tftp_error_t; -typedef struct { - grub_uint8_t addr[4]; -} grub_efi_pxe_ipv4_address_t; +typedef grub_efi_uint16_t grub_efi_pxe_base_code_udp_port_t; -typedef struct { - grub_uint8_t addr[16]; -} grub_efi_pxe_ipv6_address_t; +typedef enum { + GRUB_EFI_PXE_BASE_CODE_TFTP_FIRST, + GRUB_EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + GRUB_EFI_PXE_BASE_CODE_TFTP_READ_FILE, + GRUB_EFI_PXE_BASE_CODE_TFTP_WRITE_FILE, + GRUB_EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY, + GRUB_EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE, + GRUB_EFI_PXE_BASE_CODE_MTFTP_READ_FILE, + GRUB_EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY, + GRUB_EFI_PXE_BASE_CODE_MTFTP_LAST +} grub_efi_pxe_base_code_tftp_opcode_t; typedef struct { - grub_uint8_t addr[32]; -} grub_efi_pxe_mac_address_t; - -typedef union { - grub_uint32_t addr[4]; - grub_efi_pxe_ipv4_address_t v4; - grub_efi_pxe_ipv6_address_t v6; -} grub_efi_pxe_ip_address_t; + grub_efi_ip_address_t mcast_ip; + grub_efi_pxe_base_code_udp_port_t c_port; + grub_efi_pxe_base_code_udp_port_t s_port; + grub_efi_uint16_t listen_timeout; + grub_efi_uint16_t transmit_timeout; +} grub_efi_pxe_base_code_mtftp_info_t; #define GRUB_EFI_PXE_BASE_CODE_MAX_IPCNT 8 typedef struct grub_efi_pxe_ip_filter @@ -1652,17 +1701,31 @@ typedef struct grub_efi_pxe_mode typedef struct grub_efi_pxe { grub_uint64_t rev; - void (*start) (void); + grub_efi_status_t (*start) (struct grub_efi_pxe *this, grub_efi_boolean_t use_ipv6); void (*stop) (void); - void (*dhcp) (void); + grub_efi_status_t (*dhcp) (struct grub_efi_pxe *this, + grub_efi_boolean_t sort_offers); void (*discover) (void); - void (*mftp) (void); + grub_efi_status_t (*mtftp) (struct grub_efi_pxe *this, + grub_efi_pxe_base_code_tftp_opcode_t operation, + void *buffer_ptr, + grub_efi_boolean_t overwrite, + grub_efi_uint64_t *buffer_size, + grub_efi_uintn_t *block_size, + grub_efi_pxe_ip_address_t *server_ip, + //grub_efi_ip_address_t *server_ip, + grub_efi_char8_t *filename, + grub_efi_pxe_base_code_mtftp_info_t *info, + grub_efi_boolean_t dont_use_buffer); void (*udpwrite) (void); void (*udpread) (void); void (*setipfilter) (void); void (*arp) (void); void (*setparams) (void); - void (*setstationip) (void); + grub_efi_status_t (*set_station_ip) (struct grub_efi_pxe *this, + grub_efi_pxe_ip_address_t *new_station_ip, + grub_efi_pxe_ip_address_t *new_subnet_mask); + //void (*setstationip) (void); void (*setpackets) (void); struct grub_efi_pxe_mode *mode; } grub_efi_pxe_t; @@ -1924,6 +1987,44 @@ struct grub_efi_ip4_config2_protocol }; typedef struct grub_efi_ip4_config2_protocol grub_efi_ip4_config2_protocol_t; +struct grub_efi_ip4_route_table { + grub_efi_ipv4_address_t subnet_address; + grub_efi_ipv4_address_t subnet_mask; + grub_efi_ipv4_address_t gateway_address; +}; + +typedef struct grub_efi_ip4_route_table grub_efi_ip4_route_table_t; + +#define GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE 32 + +struct grub_efi_ip4_config2_interface_info { + grub_efi_char16_t name[GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE]; + grub_efi_uint8_t if_type; + grub_efi_uint32_t hw_address_size; + grub_efi_mac_address_t hw_address; + grub_efi_ipv4_address_t station_address; + grub_efi_ipv4_address_t subnet_mask; + grub_efi_uint32_t route_table_size; + grub_efi_ip4_route_table_t *route_table; +}; + +typedef struct grub_efi_ip4_config2_interface_info grub_efi_ip4_config2_interface_info_t; + +enum grub_efi_ip4_config2_policy { + GRUB_EFI_IP4_CONFIG2_POLICY_STATIC, + GRUB_EFI_IP4_CONFIG2_POLICY_DHCP, + GRUB_EFI_IP4_CONFIG2_POLICY_MAX +}; + +typedef enum grub_efi_ip4_config2_policy grub_efi_ip4_config2_policy_t; + +struct grub_efi_ip4_config2_manual_address { + grub_efi_ipv4_address_t address; + grub_efi_ipv4_address_t subnet_mask; +}; + +typedef struct grub_efi_ip4_config2_manual_address grub_efi_ip4_config2_manual_address_t; + enum grub_efi_ip6_config_data_type { GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO, GRUB_EFI_IP6_CONFIG_DATA_TYPE_ALT_INTERFACEID, @@ -1958,6 +2059,49 @@ struct grub_efi_ip6_config_protocol }; typedef struct grub_efi_ip6_config_protocol grub_efi_ip6_config_protocol_t; +enum grub_efi_ip6_config_policy { + GRUB_EFI_IP6_CONFIG_POLICY_MANUAL, + GRUB_EFI_IP6_CONFIG_POLICY_AUTOMATIC +}; +typedef enum grub_efi_ip6_config_policy grub_efi_ip6_config_policy_t; + +struct grub_efi_ip6_address_info { + grub_efi_ipv6_address_t address; + grub_efi_uint8_t prefix_length; +}; +typedef struct grub_efi_ip6_address_info grub_efi_ip6_address_info_t; + +struct grub_efi_ip6_route_table { + grub_efi_pxe_ipv6_address_t gateway; + grub_efi_pxe_ipv6_address_t destination; + grub_efi_uint8_t prefix_length; +}; +typedef struct grub_efi_ip6_route_table grub_efi_ip6_route_table_t; + +struct grub_efi_ip6_config_interface_info { + grub_efi_char16_t name[32]; + grub_efi_uint8_t if_type; + grub_efi_uint32_t hw_address_size; + grub_efi_mac_address_t hw_address; + grub_efi_uint32_t address_info_count; + grub_efi_ip6_address_info_t *address_info; + grub_efi_uint32_t route_count; + grub_efi_ip6_route_table_t *route_table; +}; +typedef struct grub_efi_ip6_config_interface_info grub_efi_ip6_config_interface_info_t; + +struct grub_efi_ip6_config_dup_addr_detect_transmits { + grub_efi_uint32_t dup_addr_detect_transmits; +}; +typedef struct grub_efi_ip6_config_dup_addr_detect_transmits grub_efi_ip6_config_dup_addr_detect_transmits_t; + +struct grub_efi_ip6_config_manual_address { + grub_efi_ipv6_address_t address; + grub_efi_boolean_t is_anycast; + grub_efi_uint8_t prefix_length; +}; +typedef struct grub_efi_ip6_config_manual_address grub_efi_ip6_config_manual_address_t; + #if (GRUB_TARGET_SIZEOF_VOID_P == 4) || defined (__ia64__) \ || defined (__aarch64__) || defined (__MINGW64__) || defined (__CYGWIN__) \ || defined(__riscv) diff --git a/include/grub/efi/dhcp.h b/include/grub/efi/dhcp.h new file mode 100644 index 0000000000..fdb88eb810 --- /dev/null +++ b/include/grub/efi/dhcp.h @@ -0,0 +1,343 @@ +#ifndef GRUB_EFI_DHCP_HEADER +#define GRUB_EFI_DHCP_HEADER 1 + +#define GRUB_EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID \ + { 0x9d9a39d8, 0xbd42, 0x4a73, \ + { 0xa4, 0xd5, 0x8e, 0xe9, 0x4b, 0xe1, 0x13, 0x80 } \ + } + +#define GRUB_EFI_DHCP4_PROTOCOL_GUID \ + { 0x8a219718, 0x4ef5, 0x4761, \ + { 0x91, 0xc8, 0xc0, 0xf0, 0x4b, 0xda, 0x9e, 0x56 } \ + } + +#define GRUB_EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID \ + { 0x9fb9a8a1, 0x2f4a, 0x43a6, \ + { 0x88, 0x9c, 0xd0, 0xf7, 0xb6, 0xc4 ,0x7a, 0xd5 } \ + } + +#define GRUB_EFI_DHCP6_PROTOCOL_GUID \ + { 0x87c8bad7, 0x595, 0x4053, \ + { 0x82, 0x97, 0xde, 0xde, 0x39, 0x5f, 0x5d, 0x5b } \ + } + +typedef struct grub_efi_dhcp4_protocol grub_efi_dhcp4_protocol_t; + +enum grub_efi_dhcp4_state { + GRUB_EFI_DHCP4_STOPPED, + GRUB_EFI_DHCP4_INIT, + GRUB_EFI_DHCP4_SELECTING, + GRUB_EFI_DHCP4_REQUESTING, + GRUB_EFI_DHCP4_BOUND, + GRUB_EFI_DHCP4_RENEWING, + GRUB_EFI_DHCP4_REBINDING, + GRUB_EFI_DHCP4_INIT_REBOOT, + GRUB_EFI_DHCP4_REBOOTING +}; + +typedef enum grub_efi_dhcp4_state grub_efi_dhcp4_state_t; + +struct grub_efi_dhcp4_header { + grub_efi_uint8_t op_code; + grub_efi_uint8_t hw_type; + grub_efi_uint8_t hw_addr_len; + grub_efi_uint8_t hops; + grub_efi_uint32_t xid; + grub_efi_uint16_t seconds; + grub_efi_uint16_t reserved; + grub_efi_ipv4_address_t client_addr; + grub_efi_ipv4_address_t your_addr; + grub_efi_ipv4_address_t server_addr; + grub_efi_ipv4_address_t gateway_addr; + grub_efi_uint8_t client_hw_addr[16]; + grub_efi_char8_t server_name[64]; + grub_efi_char8_t boot_file_name[128]; +} GRUB_PACKED; + +typedef struct grub_efi_dhcp4_header grub_efi_dhcp4_header_t; + +struct grub_efi_dhcp4_packet { + grub_efi_uint32_t size; + grub_efi_uint32_t length; + struct { + grub_efi_dhcp4_header_t header; + grub_efi_uint32_t magik; + grub_efi_uint8_t option[1]; + } dhcp4; +} GRUB_PACKED; + +typedef struct grub_efi_dhcp4_packet grub_efi_dhcp4_packet_t; + +struct grub_efi_dhcp4_listen_point { + grub_efi_ipv4_address_t listen_address; + grub_efi_ipv4_address_t subnet_mask; + grub_efi_uint16_t listen_port; +}; + +typedef struct grub_efi_dhcp4_listen_point grub_efi_dhcp4_listen_point_t; + +struct grub_efi_dhcp4_transmit_receive_token { + grub_efi_status_t status; + grub_efi_event_t completion_event; + grub_efi_ipv4_address_t remote_address; + grub_efi_uint16_t remote_port; + grub_efi_ipv4_address_t gateway_address; + grub_efi_uint32_t listen_point_count; + grub_efi_dhcp4_listen_point_t *listen_points; + grub_efi_uint32_t timeout_value; + grub_efi_dhcp4_packet_t *packet; + grub_efi_uint32_t response_count; + grub_efi_dhcp4_packet_t *response_list; +}; + +typedef struct grub_efi_dhcp4_transmit_receive_token grub_efi_dhcp4_transmit_receive_token_t; + +enum grub_efi_dhcp4_event { + GRUB_EFI_DHCP4_SEND_DISCOVER = 0X01, + GRUB_EFI_DHCP4_RCVD_OFFER, + GRUB_EFI_DHCP4_SELECT_OFFER, + GRUB_EFI_DHCP4_SEND_REQUEST, + GRUB_EFI_DHCP4_RCVD_ACK, + GRUB_EFI_DHCP4_RCVD_NAK, + GRUB_EFI_DHCP4_SEND_DECLINE, + GRUB_EFI_DHCP4_BOUND_COMPLETED, + GRUB_EFI_DHCP4_ENTER_RENEWING, + GRUB_EFI_DHCP4_ENTER_REBINDING, + GRUB_EFI_DHCP4_ADDRESS_LOST, + GRUB_EFI_DHCP4_FAIL +}; + +typedef enum grub_efi_dhcp4_event grub_efi_dhcp4_event_t; + +struct grub_efi_dhcp4_packet_option { + grub_efi_uint8_t op_code; + grub_efi_uint8_t length; + grub_efi_uint8_t data[1]; +} GRUB_PACKED; + +typedef struct grub_efi_dhcp4_packet_option grub_efi_dhcp4_packet_option_t; + +struct grub_efi_dhcp4_config_data { + grub_efi_uint32_t discover_try_count; + grub_efi_uint32_t *discover_timeout; + grub_efi_uint32_t request_try_count; + grub_efi_uint32_t *request_timeout; + grub_efi_ipv4_address_t client_address; + grub_efi_status_t (*dhcp4_callback) ( + grub_efi_dhcp4_protocol_t *this, + void *context, + grub_efi_dhcp4_state_t current_state, + grub_efi_dhcp4_event_t dhcp4_event, + grub_efi_dhcp4_packet_t *packet, + grub_efi_dhcp4_packet_t **new_packet + ); + void *callback_context; + grub_efi_uint32_t option_count; + grub_efi_dhcp4_packet_option_t **option_list; +}; + +typedef struct grub_efi_dhcp4_config_data grub_efi_dhcp4_config_data_t; + +struct grub_efi_dhcp4_mode_data { + grub_efi_dhcp4_state_t state; + grub_efi_dhcp4_config_data_t config_data; + grub_efi_ipv4_address_t client_address; + grub_efi_mac_address_t client_mac_address; + grub_efi_ipv4_address_t server_address; + grub_efi_ipv4_address_t router_address; + grub_efi_ipv4_address_t subnet_mask; + grub_efi_uint32_t lease_time; + grub_efi_dhcp4_packet_t *reply_packet; +}; + +typedef struct grub_efi_dhcp4_mode_data grub_efi_dhcp4_mode_data_t; + +struct grub_efi_dhcp4_protocol { + grub_efi_status_t (*get_mode_data) (grub_efi_dhcp4_protocol_t *this, + grub_efi_dhcp4_mode_data_t *dhcp4_mode_data); + grub_efi_status_t (*configure) (grub_efi_dhcp4_protocol_t *this, + grub_efi_dhcp4_config_data_t *dhcp4_cfg_data); + grub_efi_status_t (*start) (grub_efi_dhcp4_protocol_t *this, + grub_efi_event_t completion_event); + grub_efi_status_t (*renew_rebind) (grub_efi_dhcp4_protocol_t *this, + grub_efi_boolean_t rebind_request, + grub_efi_event_t completion_event); + grub_efi_status_t (*release) (grub_efi_dhcp4_protocol_t *this); + grub_efi_status_t (*stop) (grub_efi_dhcp4_protocol_t *this); + grub_efi_status_t (*build) (grub_efi_dhcp4_protocol_t *this, + grub_efi_dhcp4_packet_t *seed_packet, + grub_efi_uint32_t delete_count, + grub_efi_uint8_t *delete_list, + grub_efi_uint32_t append_count, + grub_efi_dhcp4_packet_option_t *append_list[], + grub_efi_dhcp4_packet_t **new_packet); + grub_efi_status_t (*transmit_receive) (grub_efi_dhcp4_protocol_t *this, + grub_efi_dhcp4_transmit_receive_token_t *token); + grub_efi_status_t (*parse) (grub_efi_dhcp4_protocol_t *this, + grub_efi_dhcp4_packet_t *packet, + grub_efi_uint32_t *option_count, + grub_efi_dhcp4_packet_option_t *packet_option_list[]); +}; + +typedef struct grub_efi_dhcp6_protocol grub_efi_dhcp6_protocol_t; + +struct grub_efi_dhcp6_retransmission { + grub_efi_uint32_t irt; + grub_efi_uint32_t mrc; + grub_efi_uint32_t mrt; + grub_efi_uint32_t mrd; +}; + +typedef struct grub_efi_dhcp6_retransmission grub_efi_dhcp6_retransmission_t; + +enum grub_efi_dhcp6_event { + GRUB_EFI_DHCP6_SEND_SOLICIT, + GRUB_EFI_DHCP6_RCVD_ADVERTISE, + GRUB_EFI_DHCP6_SELECT_ADVERTISE, + GRUB_EFI_DHCP6_SEND_REQUEST, + GRUB_EFI_DHCP6_RCVD_REPLY, + GRUB_EFI_DHCP6_RCVD_RECONFIGURE, + GRUB_EFI_DHCP6_SEND_DECLINE, + GRUB_EFI_DHCP6_SEND_CONFIRM, + GRUB_EFI_DHCP6_SEND_RELEASE, + GRUB_EFI_DHCP6_SEND_RENEW, + GRUB_EFI_DHCP6_SEND_REBIND +}; + +typedef enum grub_efi_dhcp6_event grub_efi_dhcp6_event_t; + +struct grub_efi_dhcp6_packet_option { + grub_efi_uint16_t op_code; + grub_efi_uint16_t op_len; + grub_efi_uint8_t data[1]; +} GRUB_PACKED; + +typedef struct grub_efi_dhcp6_packet_option grub_efi_dhcp6_packet_option_t; + +struct grub_efi_dhcp6_header { + grub_efi_uint32_t transaction_id:24; + grub_efi_uint32_t message_type:8; +} GRUB_PACKED; + +typedef struct grub_efi_dhcp6_header grub_efi_dhcp6_header_t; + +struct grub_efi_dhcp6_packet { + grub_efi_uint32_t size; + grub_efi_uint32_t length; + struct { + grub_efi_dhcp6_header_t header; + grub_efi_uint8_t option[1]; + } dhcp6; +} GRUB_PACKED; + +typedef struct grub_efi_dhcp6_packet grub_efi_dhcp6_packet_t; + +struct grub_efi_dhcp6_ia_address { + grub_efi_ipv6_address_t ip_address; + grub_efi_uint32_t preferred_lifetime; + grub_efi_uint32_t valid_lifetime; +}; + +typedef struct grub_efi_dhcp6_ia_address grub_efi_dhcp6_ia_address_t; + +enum grub_efi_dhcp6_state { + GRUB_EFI_DHCP6_INIT, + GRUB_EFI_DHCP6_SELECTING, + GRUB_EFI_DHCP6_REQUESTING, + GRUB_EFI_DHCP6_DECLINING, + GRUB_EFI_DHCP6_CONFIRMING, + GRUB_EFI_DHCP6_RELEASING, + GRUB_EFI_DHCP6_BOUND, + GRUB_EFI_DHCP6_RENEWING, + GRUB_EFI_DHCP6_REBINDING +}; + +typedef enum grub_efi_dhcp6_state grub_efi_dhcp6_state_t; + +#define GRUB_EFI_DHCP6_IA_TYPE_NA 3 +#define GRUB_EFI_DHCP6_IA_TYPE_TA 4 + +struct grub_efi_dhcp6_ia_descriptor { + grub_efi_uint16_t type; + grub_efi_uint32_t ia_id; +}; + +typedef struct grub_efi_dhcp6_ia_descriptor grub_efi_dhcp6_ia_descriptor_t; + +struct grub_efi_dhcp6_ia { + grub_efi_dhcp6_ia_descriptor_t descriptor; + grub_efi_dhcp6_state_t state; + grub_efi_dhcp6_packet_t *reply_packet; + grub_efi_uint32_t ia_address_count; + grub_efi_dhcp6_ia_address_t ia_address[1]; +}; + +typedef struct grub_efi_dhcp6_ia grub_efi_dhcp6_ia_t; + +struct grub_efi_dhcp6_duid { + grub_efi_uint16_t length; + grub_efi_uint8_t duid[1]; +}; + +typedef struct grub_efi_dhcp6_duid grub_efi_dhcp6_duid_t; + +struct grub_efi_dhcp6_mode_data { + grub_efi_dhcp6_duid_t *client_id; + grub_efi_dhcp6_ia_t *ia; +}; + +typedef struct grub_efi_dhcp6_mode_data grub_efi_dhcp6_mode_data_t; + +struct grub_efi_dhcp6_config_data { + grub_efi_status_t (*dhcp6_callback) (grub_efi_dhcp6_protocol_t this, + void *context, + grub_efi_dhcp6_state_t current_state, + grub_efi_dhcp6_event_t dhcp6_event, + grub_efi_dhcp6_packet_t *packet, + grub_efi_dhcp6_packet_t **new_packet); + void *callback_context; + grub_efi_uint32_t option_count; + grub_efi_dhcp6_packet_option_t **option_list; + grub_efi_dhcp6_ia_descriptor_t ia_descriptor; + grub_efi_event_t ia_info_event; + grub_efi_boolean_t reconfigure_accept; + grub_efi_boolean_t rapid_commit; + grub_efi_dhcp6_retransmission_t *solicit_retransmission; +}; + +typedef struct grub_efi_dhcp6_config_data grub_efi_dhcp6_config_data_t; + +struct grub_efi_dhcp6_protocol { + grub_efi_status_t (*get_mode_data) (grub_efi_dhcp6_protocol_t *this, + grub_efi_dhcp6_mode_data_t *dhcp6_mode_data, + grub_efi_dhcp6_config_data_t *dhcp6_config_data); + grub_efi_status_t (*configure) (grub_efi_dhcp6_protocol_t *this, + grub_efi_dhcp6_config_data_t *dhcp6_cfg_data); + grub_efi_status_t (*start) (grub_efi_dhcp6_protocol_t *this); + grub_efi_status_t (*info_request) (grub_efi_dhcp6_protocol_t *this, + grub_efi_boolean_t send_client_id, + grub_efi_dhcp6_packet_option_t *option_request, + grub_efi_uint32_t option_count, + grub_efi_dhcp6_packet_option_t *option_list[], + grub_efi_dhcp6_retransmission_t *retransmission, + grub_efi_event_t timeout_event, + grub_efi_status_t (*reply_callback) (grub_efi_dhcp6_protocol_t *this, + void *context, + grub_efi_dhcp6_packet_t *packet), + void *callback_context); + grub_efi_status_t (*renew_rebind) (grub_efi_dhcp6_protocol_t *this, + grub_efi_boolean_t rebind_request); + grub_efi_status_t (*decline) (grub_efi_dhcp6_protocol_t *this, + grub_efi_uint32_t address_count, + grub_efi_ipv6_address_t *addresses); + grub_efi_status_t (*release) (grub_efi_dhcp6_protocol_t *this, + grub_efi_uint32_t address_count, + grub_efi_ipv6_address_t *addresses); + grub_efi_status_t (*stop) (grub_efi_dhcp6_protocol_t *this); + grub_efi_status_t (*parse) (grub_efi_dhcp6_protocol_t *this, + grub_efi_dhcp6_packet_t *packet, + grub_efi_uint32_t *option_count, + grub_efi_dhcp6_packet_option_t *packet_option_list[]); +}; + +#endif /* ! GRUB_EFI_DHCP_HEADER */ diff --git a/include/grub/efi/http.h b/include/grub/efi/http.h new file mode 100644 index 0000000000..c5e9a89f50 --- /dev/null +++ b/include/grub/efi/http.h @@ -0,0 +1,215 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_EFI_HTTP_HEADER +#define GRUB_EFI_HTTP_HEADER 1 + +#include +#include +#include + +#define GRUB_EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID \ + { 0xbdc8e6af, 0xd9bc, 0x4379, \ + { 0xa7, 0x2a, 0xe0, 0xc4, 0xe7, 0x5d, 0xae, 0x1c } \ + } + +#define GRUB_EFI_HTTP_PROTOCOL_GUID \ + { 0x7A59B29B, 0x910B, 0x4171, \ + { 0x82, 0x42, 0xA8, 0x5A, 0x0D, 0xF2, 0x5B, 0x5B } \ + } + +#define EFIHTTP_WAIT_TIME 10000 // 10000ms = 10s +#define EFIHTTP_RX_BUF_LEN 10240 + +//****************************************** +// Protocol Interface Structure +//****************************************** +struct grub_efi_http; + +//****************************************** +// EFI_HTTP_VERSION +//****************************************** +typedef enum { + GRUB_EFI_HTTPVERSION10, + GRUB_EFI_HTTPVERSION11, + GRUB_EFI_HTTPVERSIONUNSUPPORTED +} grub_efi_http_version_t; + +//****************************************** +// EFI_HTTPv4_ACCESS_POINT +//****************************************** +typedef struct { + grub_efi_boolean_t use_default_address; + grub_efi_ipv4_address_t local_address; + grub_efi_ipv4_address_t local_subnet; + grub_efi_uint16_t local_port; +} grub_efi_httpv4_access_point_t; + +//****************************************** +// EFI_HTTPv6_ACCESS_POINT +//****************************************** +typedef struct { + grub_efi_ipv6_address_t local_address; + grub_efi_uint16_t local_port; +} grub_efi_httpv6_access_point_t; + +//****************************************** +// EFI_HTTP_CONFIG_DATA +//****************************************** +typedef struct { + grub_efi_http_version_t http_version; + grub_efi_uint32_t timeout_millisec; + grub_efi_boolean_t local_address_is_ipv6; + union { + grub_efi_httpv4_access_point_t *ipv4_node; + grub_efi_httpv6_access_point_t *ipv6_node; + } access_point; +} grub_efi_http_config_data_t; + +//****************************************** +// EFI_HTTP_METHOD +//****************************************** +typedef enum { + GRUB_EFI_HTTPMETHODGET, + GRUB_EFI_HTTPMETHODPOST, + GRUB_EFI_HTTPMETHODPATCH, + GRUB_EFI_HTTPMETHODOPTIONS, + GRUB_EFI_HTTPMETHODCONNECT, + GRUB_EFI_HTTPMETHODHEAD, + GRUB_EFI_HTTPMETHODPUT, + GRUB_EFI_HTTPMETHODDELETE, + GRUB_EFI_HTTPMETHODTRACE, +} grub_efi_http_method_t; + +//****************************************** +// EFI_HTTP_REQUEST_DATA +//****************************************** +typedef struct { + grub_efi_http_method_t method; + grub_efi_char16_t *url; +} grub_efi_http_request_data_t; + +typedef enum { + GRUB_EFI_HTTP_STATUS_UNSUPPORTED_STATUS = 0, + GRUB_EFI_HTTP_STATUS_100_CONTINUE, + GRUB_EFI_HTTP_STATUS_101_SWITCHING_PROTOCOLS, + GRUB_EFI_HTTP_STATUS_200_OK, + GRUB_EFI_HTTP_STATUS_201_CREATED, + GRUB_EFI_HTTP_STATUS_202_ACCEPTED, + GRUB_EFI_HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION, + GRUB_EFI_HTTP_STATUS_204_NO_CONTENT, + GRUB_EFI_HTTP_STATUS_205_RESET_CONTENT, + GRUB_EFI_HTTP_STATUS_206_PARTIAL_CONTENT, + GRUB_EFI_HTTP_STATUS_300_MULTIPLE_CHIOCES, + GRUB_EFI_HTTP_STATUS_301_MOVED_PERMANENTLY, + GRUB_EFI_HTTP_STATUS_302_FOUND, + GRUB_EFI_HTTP_STATUS_303_SEE_OTHER, + GRUB_EFI_HTTP_STATUS_304_NOT_MODIFIED, + GRUB_EFI_HTTP_STATUS_305_USE_PROXY, + GRUB_EFI_HTTP_STATUS_307_TEMPORARY_REDIRECT, + GRUB_EFI_HTTP_STATUS_400_BAD_REQUEST, + GRUB_EFI_HTTP_STATUS_401_UNAUTHORIZED, + GRUB_EFI_HTTP_STATUS_402_PAYMENT_REQUIRED, + GRUB_EFI_HTTP_STATUS_403_FORBIDDEN, + GRUB_EFI_HTTP_STATUS_404_NOT_FOUND, + GRUB_EFI_HTTP_STATUS_405_METHOD_NOT_ALLOWED, + GRUB_EFI_HTTP_STATUS_406_NOT_ACCEPTABLE, + GRUB_EFI_HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED, + GRUB_EFI_HTTP_STATUS_408_REQUEST_TIME_OUT, + GRUB_EFI_HTTP_STATUS_409_CONFLICT, + GRUB_EFI_HTTP_STATUS_410_GONE, + GRUB_EFI_HTTP_STATUS_411_LENGTH_REQUIRED, + GRUB_EFI_HTTP_STATUS_412_PRECONDITION_FAILED, + GRUB_EFI_HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE, + GRUB_EFI_HTTP_STATUS_414_REQUEST_URI_TOO_LARGE, + GRUB_EFI_HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE, + GRUB_EFI_HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED, + GRUB_EFI_HTTP_STATUS_417_EXPECTATION_FAILED, + GRUB_EFI_HTTP_STATUS_500_INTERNAL_SERVER_ERROR, + GRUB_EFI_HTTP_STATUS_501_NOT_IMPLEMENTED, + GRUB_EFI_HTTP_STATUS_502_BAD_GATEWAY, + GRUB_EFI_HTTP_STATUS_503_SERVICE_UNAVAILABLE, + GRUB_EFI_HTTP_STATUS_504_GATEWAY_TIME_OUT, + GRUB_EFI_HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED +} grub_efi_http_status_code_t; + +//****************************************** +// EFI_HTTP_RESPONSE_DATA +//****************************************** +typedef struct { + grub_efi_http_status_code_t status_code; +} grub_efi_http_response_data_t; + +//****************************************** +// EFI_HTTP_HEADER +//****************************************** +typedef struct { + grub_efi_char8_t *field_name; + grub_efi_char8_t *field_value; +} grub_efi_http_header_t; + +//****************************************** +// EFI_HTTP_MESSAGE +//****************************************** +typedef struct { + union { + grub_efi_http_request_data_t *request; + grub_efi_http_response_data_t *response; + } data; + grub_efi_uint32_t header_count; + grub_efi_http_header_t *headers; + grub_efi_uint32_t body_length; + void *body; +} grub_efi_http_message_t; + +//****************************************** +// EFI_HTTP_TOKEN +//****************************************** +typedef struct { + grub_efi_event_t event; + grub_efi_status_t status; + grub_efi_http_message_t *message; +} grub_efi_http_token_t; + +struct grub_efi_http { + grub_efi_status_t + (*get_mode_data) (struct grub_efi_http *this, + grub_efi_http_config_data_t *http_config_data); + + grub_efi_status_t + (*configure) (struct grub_efi_http *this, + grub_efi_http_config_data_t *http_config_data); + + grub_efi_status_t + (*request) (struct grub_efi_http *this, + grub_efi_http_token_t *token); + + grub_efi_status_t + (*cancel) (struct grub_efi_http *this, + grub_efi_http_token_t *token); + + grub_efi_status_t + (*response) (struct grub_efi_http *this, + grub_efi_http_token_t *token); + + grub_efi_status_t + (*poll) (struct grub_efi_http *this); +}; +typedef struct grub_efi_http grub_efi_http_t; + +#endif /* !GRUB_EFI_HTTP_HEADER */ diff --git a/include/grub/net/efi.h b/include/grub/net/efi.h new file mode 100644 index 0000000000..de90d223e8 --- /dev/null +++ b/include/grub/net/efi.h @@ -0,0 +1,144 @@ +#ifndef GRUB_NET_EFI_HEADER +#define GRUB_NET_EFI_HEADER 1 + +#include +#include +#include +#include + +typedef struct grub_efi_net_interface grub_efi_net_interface_t; +typedef struct grub_efi_net_ip_config grub_efi_net_ip_config_t; +typedef union grub_efi_net_ip_address grub_efi_net_ip_address_t; +typedef struct grub_efi_net_ip_manual_address grub_efi_net_ip_manual_address_t; + +struct grub_efi_net_interface +{ + char *name; + int prefer_ip6; + struct grub_efi_net_device *dev; + struct grub_efi_net_io *io; + grub_efi_net_ip_config_t *ip_config; + int io_type; + struct grub_efi_net_interface *next; +}; + +#define efi_net_interface_get_hw_address(inf) inf->ip_config->get_hw_address (inf->dev) +#define efi_net_interface_get_address(inf) inf->ip_config->get_address (inf->dev) +#define efi_net_interface_get_route_table(inf) inf->ip_config->get_route_table (inf->dev) +#define efi_net_interface_set_address(inf, addr, with_subnet) inf->ip_config->set_address (inf->dev, addr, with_subnet) +#define efi_net_interface_set_gateway(inf, addr) inf->ip_config->set_gateway (inf->dev, addr) +#define efi_net_interface_set_dns(inf, addr) inf->ip_config->set_dns (inf->dev, addr) + +struct grub_efi_net_ip_config +{ + char * (*get_hw_address) (struct grub_efi_net_device *dev); + char * (*get_address) (struct grub_efi_net_device *dev); + char ** (*get_route_table) (struct grub_efi_net_device *dev); + grub_efi_net_interface_t * (*best_interface) (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *address); + int (*set_address) (struct grub_efi_net_device *dev, grub_efi_net_ip_manual_address_t *net_ip, int with_subnet); + int (*set_gateway) (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *address); + int (*set_dns) (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *dns); +}; + +union grub_efi_net_ip_address +{ + grub_efi_ipv4_address_t ip4; + grub_efi_ipv6_address_t ip6; +}; + +struct grub_efi_net_ip_manual_address +{ + int is_ip6; + union + { + grub_efi_ip4_config2_manual_address_t ip4; + grub_efi_ip6_config_manual_address_t ip6; + }; +}; + +struct grub_efi_net_device +{ + grub_efi_handle_t handle; + grub_efi_ip4_config2_protocol_t *ip4_config; + grub_efi_ip6_config_protocol_t *ip6_config; + grub_efi_handle_t http_handle; + grub_efi_http_t *http; + grub_efi_handle_t ip4_pxe_handle; + grub_efi_pxe_t *ip4_pxe; + grub_efi_handle_t ip6_pxe_handle; + grub_efi_pxe_t *ip6_pxe; + grub_efi_handle_t dhcp4_handle; + grub_efi_dhcp4_protocol_t *dhcp4; + grub_efi_handle_t dhcp6_handle; + grub_efi_dhcp6_protocol_t *dhcp6; + char *card_name; + grub_efi_net_interface_t *net_interfaces; + struct grub_efi_net_device *next; +}; + +struct grub_efi_net_io +{ + void (*configure) (struct grub_efi_net_device *dev, int prefer_ip6); + grub_err_t (*open) (struct grub_efi_net_device *dev, + int prefer_ip6, + grub_file_t file, + const char *filename, + int type); + grub_ssize_t (*read) (struct grub_efi_net_device *dev, + int prefer_ip6, + grub_file_t file, + char *buf, + grub_size_t len); + grub_err_t (*close) (struct grub_efi_net_device *dev, + int prefer_ip6, + grub_file_t file); +}; + +extern struct grub_efi_net_device *net_devices; + +extern struct grub_efi_net_io io_http; +extern struct grub_efi_net_io io_pxe; + +extern grub_efi_net_ip_config_t *efi_net_ip4_config; +extern grub_efi_net_ip_config_t *efi_net_ip6_config; + +char * +grub_efi_ip4_address_to_string (grub_efi_ipv4_address_t *address); + +char * +grub_efi_ip6_address_to_string (grub_efi_pxe_ipv6_address_t *address); + +char * +grub_efi_hw_address_to_string (grub_efi_uint32_t hw_address_size, grub_efi_mac_address_t hw_address); + +int +grub_efi_string_to_ip4_address (const char *val, grub_efi_ipv4_address_t *address, const char **rest); + +int +grub_efi_string_to_ip6_address (const char *val, grub_efi_ipv6_address_t *address, const char **rest); + +char * +grub_efi_ip6_interface_name (struct grub_efi_net_device *dev); + +char * +grub_efi_ip4_interface_name (struct grub_efi_net_device *dev); + +grub_efi_net_interface_t * +grub_efi_net_create_interface (struct grub_efi_net_device *dev, + const char *interface_name, + grub_efi_net_ip_manual_address_t *net_ip, + int has_subnet); + +int grub_efi_net_fs_init (void); +void grub_efi_net_fs_fini (void); +int grub_efi_net_boot_from_https (void); +int grub_efi_net_boot_from_opa (void); + +extern grub_command_func_t grub_efi_net_list_routes; +extern grub_command_func_t grub_efi_net_list_cards; +extern grub_command_func_t grub_efi_net_list_addrs; +extern grub_command_func_t grub_efi_net_add_addr; +extern grub_command_func_t grub_efi_net_bootp; +extern grub_command_func_t grub_efi_net_bootp6; + +#endif /* ! GRUB_NET_EFI_HEADER */ diff --git a/util/grub-mknetdir.c b/util/grub-mknetdir.c index a2461cda1c..77958dd9dd 100644 --- a/util/grub-mknetdir.c +++ b/util/grub-mknetdir.c @@ -32,13 +32,15 @@ static char *rootdir = NULL, *subdir = NULL; static char *debug_image = NULL; +static char efi_netfs = 0; enum { OPTION_NET_DIRECTORY = 0x301, OPTION_SUBDIR, OPTION_DEBUG, - OPTION_DEBUG_IMAGE + OPTION_DEBUG_IMAGE, + OPTION_DEBUG_EFI_NETFS }; static struct argp_option options[] = { @@ -49,6 +51,7 @@ static struct argp_option options[] = { 0, N_("relative subdirectory on network server"), 2}, {"debug", OPTION_DEBUG, 0, OPTION_HIDDEN, 0, 2}, {"debug-image", OPTION_DEBUG_IMAGE, N_("STRING"), OPTION_HIDDEN, 0, 2}, + {"debug-efi-netfs", OPTION_DEBUG_EFI_NETFS, 0, OPTION_HIDDEN, 0, 2}, {0, 0, 0, 0, 0, 0} }; @@ -67,6 +70,9 @@ argp_parser (int key, char *arg, struct argp_state *state) free (subdir); subdir = xstrdup (arg); return 0; + case OPTION_DEBUG_EFI_NETFS: + efi_netfs = 1; + return 0; /* This is an undocumented feature... */ case OPTION_DEBUG: verbosity++; @@ -82,7 +88,6 @@ argp_parser (int key, char *arg, struct argp_state *state) } } - struct argp argp = { options, argp_parser, NULL, "\v"N_("Prepares GRUB network boot images at net_directory/subdir " @@ -92,7 +97,7 @@ struct argp argp = { static char *base; -static const struct +static struct { const char *mkimage_target; const char *netmodule; @@ -156,6 +161,7 @@ process_input_dir (const char *input_dir, enum grub_install_plat platform) grub_install_push_module (targets[platform].netmodule); output = grub_util_path_concat_ext (2, grubdir, "core", targets[platform].ext); + grub_install_make_image_wrap (input_dir, prefix, output, 0, load_cfg, targets[platform].mkimage_target, 0); @@ -195,7 +201,16 @@ main (int argc, char *argv[]) grub_install_mkdir_p (base); - grub_install_push_module ("tftp"); + if (!efi_netfs) + { + grub_install_push_module ("tftp"); + grub_install_push_module ("http"); + } + else + { + targets[GRUB_INSTALL_PLATFORM_I386_EFI].netmodule = "efi_netfs"; + targets[GRUB_INSTALL_PLATFORM_X86_64_EFI].netmodule = "efi_netfs"; + } if (!grub_install_source_directory) { From 51906d40a2ab7ef0d0cff9a8ebf0b0e69ff5c2cc Mon Sep 17 00:00:00 2001 From: Sebastian Krahmer Date: Tue, 28 Nov 2017 17:24:38 +0800 Subject: [PATCH 079/367] AUDIT-0: http boot tracker bug Fixing a memory leak in case of error, and a integer overflow, leading to a heap overflow due to overly large chunk sizes. We need to check against some maximum value, otherwise values like 0xffffffff will eventually lead in the allocation functions to small sized buffers, since the len is rounded up to the next reasonable alignment. The following memcpy will then smash the heap, leading to RCE. This is no big issue for pure http boot, since its going to execute an untrusted kernel anyway, but it will break trusted boot scenarios, where only signed code is allowed to be executed. Signed-off-by: Michael Chang --- grub-core/net/efi/net.c | 4 +++- grub-core/net/http.c | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/grub-core/net/efi/net.c b/grub-core/net/efi/net.c index 86bce6535d..4bb308026c 100644 --- a/grub-core/net/efi/net.c +++ b/grub-core/net/efi/net.c @@ -645,8 +645,10 @@ grub_efihttp_chunk_read (grub_file_t file, char *buf, rd = efi_net_interface (read, file, chunk, sz); - if (rd <= 0) + if (rd <= 0) { + grub_free (chunk); return rd; + } if (buf) { diff --git a/grub-core/net/http.c b/grub-core/net/http.c index 12a2632ea5..b52b558d63 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -31,7 +31,8 @@ GRUB_MOD_LICENSE ("GPLv3+"); enum { - HTTP_PORT = 80 + HTTP_PORT = 80, + HTTP_MAX_CHUNK_SIZE = 0x80000000 }; @@ -78,6 +79,8 @@ parse_line (grub_file_t file, http_data_t data, char *ptr, grub_size_t len) if (data->in_chunk_len == 2) { data->chunk_rem = grub_strtoul (ptr, 0, 16); + if (data->chunk_rem > HTTP_MAX_CHUNK_SIZE) + return GRUB_ERR_NET_PACKET_TOO_BIG; grub_errno = GRUB_ERR_NONE; if (data->chunk_rem == 0) { From ff5a45542977324bc2646defa1850dfedaa04c22 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 4 Jun 2018 19:49:47 +0200 Subject: [PATCH 080/367] grub-editenv: Add "incr" command to increment integer value env. variables To be able to automatically detect if the last boot was successful, We want to keep count of succesful / failed boots in some integer environment variable. This commit adds a grub-editenvt "incr" command to increment such integer value env. variables by 1 for use from various boot scripts. Signed-off-by: Hans de Goede --- util/grub-editenv.c | 50 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/util/grub-editenv.c b/util/grub-editenv.c index db6f187cc6..948eec8a11 100644 --- a/util/grub-editenv.c +++ b/util/grub-editenv.c @@ -53,6 +53,9 @@ static struct argp_option options[] = { /* TRANSLATORS: "unset" is a keyword. It's a summary of "unset" subcommand. */ {N_("unset [NAME ...]"), 0, 0, OPTION_DOC|OPTION_NO_USAGE, N_("Delete variables."), 0}, + /* TRANSLATORS: "incr" is a keyword. It's a summary of "incr" subcommand. */ + {N_("incr [NAME ...]"), 0, 0, OPTION_DOC|OPTION_NO_USAGE, + N_("Increase value of integer variables."), 0}, {0, 0, 0, OPTION_DOC, N_("Options:"), -1}, {"verbose", 'v', 0, 0, N_("print verbose messages."), 0}, @@ -253,6 +256,51 @@ unset_variables (const char *name, int argc, char *argv[]) grub_envblk_close (envblk); } +struct get_int_value_params { + char *varname; + int value; +}; + +static int +get_int_value (const char *varname, const char *value, void *hook_data) +{ + struct get_int_value_params *params = hook_data; + + if (strcmp (varname, params->varname) == 0) { + params->value = strtol (value, NULL, 10); + return 1; + } + return 0; +} + +static void +incr_variables (const char *name, int argc, char *argv[]) +{ + grub_envblk_t envblk; + char buf[16]; + + envblk = open_envblk_file (name); + while (argc) + { + struct get_int_value_params params = { + .varname = argv[0], + .value = 0, /* Consider unset variables 0 */ + }; + + grub_envblk_iterate (envblk, ¶ms, get_int_value); + snprintf(buf, sizeof(buf), "%d", params.value + 1); + + if (! grub_envblk_set (envblk, argv[0], buf)) + grub_util_error ("%s", _("environment block too small")); + + argc--; + argv++; + } + + write_envblk (name, envblk); + grub_envblk_close (envblk); +} + int main (int argc, char *argv[]) { @@ -292,6 +340,8 @@ main (int argc, char *argv[]) set_variables (filename, argc - curindex, argv + curindex); else if (strcmp (command, "unset") == 0) unset_variables (filename, argc - curindex, argv + curindex); + else if (strcmp (command, "incr") == 0) + incr_variables (filename, argc - curindex, argv + curindex); else { char *program = xstrdup(program_name); From 5c568cb9507e980f0664aa694c918517cbf8905f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 6 Jun 2018 08:44:11 +0200 Subject: [PATCH 081/367] Add auto-hide menu support On single-os systems we do not want to show the menu, unless something went wrong with the previous boot, in which case the user may need the menu to debug/fix the problem. This commit adds a new grub.d/00_menu_auto_hide file which emits a config snippet implementing this. I've chosen to do this in a separate grub.d file because chances of this going upstream are small and this way it will be easier to rebase. Since auto-hiding the menu requires detecting the previous boot was ok, we get fastboot support (where we don't check for a key at all) for free so this commit also adds support for this. The new config-file code uses the following variables: menu_auto_hide Set this to "1" to activate the new auto-hide feature Set this to "2" to auto-hide the menu even when multiple operating systems are installed. Note the menu will still auto show after booting an other os as that won't set boot_success. menu_show_once Set this to "1" to force showing the menu once. boot_success The OS sets this to "1" to indicate a successful boot. boot_indeterminate The OS increments this integer when rebooting after e.g. installing updates or a selinux relabel. fastboot If set to "1" and the conditions for auto-hiding the menu are met, the menu is not shown and all checks for keypresses are skipped, booting the default immediately. 30_os-prober.in changes somewhat inspired by: https://git.launchpad.net/~ubuntu-core-dev/grub/+git/ubuntu/tree/debian/patches/quick_boot.patch Signed-off-by: Hans de Goede --- Makefile.util.def | 6 ++++ util/grub.d/01_menu_auto_hide.in | 48 ++++++++++++++++++++++++++++++++ util/grub.d/30_os-prober.in | 18 ++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 util/grub.d/01_menu_auto_hide.in diff --git a/Makefile.util.def b/Makefile.util.def index bda9fd1211..cb8e3c3270 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -458,6 +458,12 @@ script = { installdir = grubconf; }; +script = { + name = '01_menu_auto_hide'; + common = util/grub.d/01_menu_auto_hide.in; + installdir = grubconf; +}; + script = { name = '01_users'; common = util/grub.d/01_users.in; diff --git a/util/grub.d/01_menu_auto_hide.in b/util/grub.d/01_menu_auto_hide.in new file mode 100644 index 0000000000..ad175870a5 --- /dev/null +++ b/util/grub.d/01_menu_auto_hide.in @@ -0,0 +1,48 @@ +#! /bin/sh + +# Disable / skip generating menu-auto-hide config parts on serial terminals +for x in ${GRUB_TERMINAL_INPUT} ${GRUB_TERMINAL_OUTPUT}; do + case "$x" in + serial*) + exit 0 + ;; + esac +done + +cat << EOF +if [ "\${boot_success}" = "1" -o "\${boot_indeterminate}" = "1" ]; then + set last_boot_ok=1 +else + set last_boot_ok=0 +fi + +# Reset boot_indeterminate after a successful boot +if [ "\${boot_success}" = "1" ] ; then + set boot_indeterminate=0 +# Avoid boot_indeterminate causing the menu to be hidden more then once +elif [ "\${boot_indeterminate}" = "1" ]; then + set boot_indeterminate=2 +fi +set boot_success=0 +save_env boot_success boot_indeterminate + +if [ x\$feature_timeout_style = xy ] ; then + if [ "\${menu_show_once}" ]; then + unset menu_show_once + save_env menu_show_once + set timeout_style=menu + set timeout=60 + elif [ "\${menu_auto_hide}" -a "\${last_boot_ok}" = "1" ]; then + set orig_timeout_style=\${timeout_style} + set orig_timeout=\${timeout} + if [ "\${fastboot}" = "1" ]; then + # timeout_style=menu + timeout=0 avoids the countdown code keypress check + set timeout_style=menu + set timeout=0 + else + set timeout_style=hidden + set timeout=1 + fi + fi +fi +EOF diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in index 4b27bd2015..3c9431cfcf 100644 --- a/util/grub.d/30_os-prober.in +++ b/util/grub.d/30_os-prober.in @@ -42,6 +42,7 @@ if [ -z "${OSPROBED}" ] ; then fi osx_entry() { + found_other_os=1 # TRANSLATORS: it refers on the OS residing on device %s onstr="$(gettext_printf "(on %s)" "${DEVICE}")" hints="" @@ -102,6 +103,7 @@ for OS in ${OSPROBED} ; do case ${BOOT} in chain) + found_other_os=1 onstr="$(gettext_printf "(on %s)" "${DEVICE}")" cat << EOF @@ -132,6 +134,7 @@ EOF EOF ;; efi) + found_other_os=1 EFIPATH=${DEVICE#*@} DEVICE=${DEVICE%@*} @@ -176,6 +179,7 @@ EOF LINITRD="${LINITRD#/boot}" fi + found_other_os=1 onstr="$(gettext_printf "(on %s)" "${DEVICE}")" recovery_params="$(echo "${LPARAMS}" | grep single)" || true counter=1 @@ -257,6 +261,7 @@ EOF done ;; hurd) + found_other_os=1 onstr="$(gettext_printf "(on %s)" "${DEVICE}")" cat << EOF menuentry '$(echo "${LONGNAME} $onstr" | grub_quote)' --class hurd --class gnu --class os \$menuentry_id_option 'osprober-gnuhurd-/boot/gnumach.gz-false-$(grub_get_device_id "${DEVICE}")' { @@ -283,6 +288,7 @@ EOF EOF ;; minix) + found_other_os=1 cat << EOF menuentry "${LONGNAME} (on ${DEVICE}, Multiboot)" { EOF @@ -299,3 +305,15 @@ EOF ;; esac done + +# We override the results of the menu_auto_hide code here, this is a bit ugly, +# but grub-mkconfig writes out the file linearly, so this is the only way +if [ "${found_other_os}" = "1" ]; then + cat << EOF +# Other OS found, undo autohiding of menu unless menu_auto_hide=2 +if [ "\${orig_timeout_style}" -a "\${menu_auto_hide}" != "2" ]; then + set timeout_style=\${orig_timeout_style} + set timeout=\${orig_timeout} +fi +EOF +fi From 9f7d82e6e619060324e428035e1943587955bb25 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 12 Jun 2018 13:25:16 +0200 Subject: [PATCH 082/367] Add grub-set-bootflag utility This commit adds a new grub-set-bootflag utility, which can be used to set known bootflags in the grubenv: boot_success or menu_show_once. grub-set-bootflag is different from grub-editenv in 2 ways: 1) It is intended to be executed by regular users so must be installed as suid root. As such it is written to not use any existing grubenv related code for easy auditing. It can't be executed through pkexec because we want to call it under gdm and pkexec does not work under gdm due the gdm user having /sbin/nologin as shell. 2) Since it can be executed by regular users it only allows setting (assigning a value of 1 to) bootflags which it knows about. Currently those are just boot_success and menu_show_once. This commit also adds a couple of example systemd and files which show how this can be used to set boot_success from a user-session: docs/grub-boot-success.service docs/grub-boot-success.timer The 2 grub-boot-success.systemd files should be placed in /lib/systemd/user and a symlink to grub-boot-success.timer should be added to /lib/systemd/user/timers.target.wants. Signed-off-by: Hans de Goede [makhomed: grub-boot-success.timer: Only run if not in a container] Signed-off-by: Gena Makhomed [rharwood: migrate to h2m] Signed-off-by: Robbie Harwood --- Makefile.util.def | 7 ++ conf/Makefile.extra-dist | 3 + docs/grub-boot-success.service | 6 ++ docs/grub-boot-success.timer | 7 ++ docs/man/grub-set-bootflag.h2m | 2 + util/grub-set-bootflag.c | 172 +++++++++++++++++++++++++++++++++ 6 files changed, 197 insertions(+) create mode 100644 docs/grub-boot-success.service create mode 100644 docs/grub-boot-success.timer create mode 100644 docs/man/grub-set-bootflag.h2m create mode 100644 util/grub-set-bootflag.c diff --git a/Makefile.util.def b/Makefile.util.def index cb8e3c3270..d066652e9b 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -1429,3 +1429,10 @@ program = { ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; + +program = { + name = grub-set-bootflag; + installdir = sbin; + mansection = 1; + common = util/grub-set-bootflag.c; +}; diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index 8f1485d52a..ad235de7fc 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -15,6 +15,9 @@ EXTRA_DIST += docs/man EXTRA_DIST += docs/autoiso.cfg EXTRA_DIST += docs/grub.cfg EXTRA_DIST += docs/osdetect.cfg +EXTRA_DIST += docs/org.gnu.grub.policy +EXTRA_DIST += docs/grub-boot-success.service +EXTRA_DIST += docs/grub-boot-success.timer EXTRA_DIST += conf/i386-cygwin-img-ld.sc diff --git a/docs/grub-boot-success.service b/docs/grub-boot-success.service new file mode 100644 index 0000000000..80e79584c9 --- /dev/null +++ b/docs/grub-boot-success.service @@ -0,0 +1,6 @@ +[Unit] +Description=Mark boot as successful + +[Service] +Type=oneshot +ExecStart=/usr/sbin/grub2-set-bootflag boot_success diff --git a/docs/grub-boot-success.timer b/docs/grub-boot-success.timer new file mode 100644 index 0000000000..406f172005 --- /dev/null +++ b/docs/grub-boot-success.timer @@ -0,0 +1,7 @@ +[Unit] +Description=Mark boot as successful after the user session has run 2 minutes +ConditionUser=!@system +ConditionVirtualization=!container + +[Timer] +OnActiveSec=2min diff --git a/docs/man/grub-set-bootflag.h2m b/docs/man/grub-set-bootflag.h2m new file mode 100644 index 0000000000..94ec0b92ed --- /dev/null +++ b/docs/man/grub-set-bootflag.h2m @@ -0,0 +1,2 @@ +[NAME] +grub-set-bootflag \- set a bootflag in the GRUB environment block diff --git a/util/grub-set-bootflag.c b/util/grub-set-bootflag.c new file mode 100644 index 0000000000..d506f7e75b --- /dev/null +++ b/util/grub-set-bootflag.c @@ -0,0 +1,172 @@ +/* grub-set-bootflag.c - tool to set boot-flags in the grubenv. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +/* + * NOTE this gets run by users as root (through pkexec), so this does not + * use any grub library / util functions to allow for easy auditing. + * The grub headers are only included to get certain defines. + */ + +#include /* For *_DIR_NAME defines */ +#include +#include /* For GRUB_ENVBLK_DEFCFG define */ +#include +#include +#include +#include + +#include "progname.h" + +#define GRUBENV "/" GRUB_BOOT_DIR_NAME "/" GRUB_DIR_NAME "/" GRUB_ENVBLK_DEFCFG +#define GRUBENV_SIZE 1024 + +const char *bootflags[] = { + "boot_success", + "menu_show_once", + NULL +}; + +static void usage(FILE *out) +{ + int i; + + fprintf (out, "Usage: 'grub-set-bootflag ', where is one of:\n"); + for (i = 0; bootflags[i]; i++) + fprintf (out, " %s\n", bootflags[i]); +} + +int main(int argc, char *argv[]) +{ + /* NOTE buf must be at least the longest bootflag length + 4 bytes */ + char env[GRUBENV_SIZE + 1], buf[64], *s; + const char *bootflag; + int i, len, ret; + FILE *f; + + if (argc != 2) + { + usage (stderr); + return 1; + } + else if (!strcmp (argv[1], "--help")) + { + usage (stdout); + return 0; + } + else if (!strcmp (argv[1], "--version")) + { + printf ("grub-set-bootflag (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); + return 0; + } + + for (i = 0; bootflags[i]; i++) + if (!strcmp (argv[1], bootflags[i])) + break; + if (!bootflags[i]) + { + fprintf (stderr, "Invalid bootflag: '%s'\n", argv[1]); + usage (stderr); + return 1; + } + + bootflag = bootflags[i]; + len = strlen (bootflag); + + f = fopen (GRUBENV, "r"); + if (!f) + { + perror ("Error opening " GRUBENV " for reading"); + return 1; + } + + ret = fread (env, 1, GRUBENV_SIZE, f); + fclose (f); + if (ret != GRUBENV_SIZE) + { + errno = EINVAL; + perror ("Error reading from " GRUBENV); + return 1; + } + + /* 0 terminate env */ + env[GRUBENV_SIZE] = 0; + + if (strncmp (env, GRUB_ENVBLK_SIGNATURE, strlen (GRUB_ENVBLK_SIGNATURE))) + { + fprintf (stderr, "Error invalid environment block\n"); + return 1; + } + + /* Find a pre-existing definition of the bootflag */ + s = strstr (env, bootflag); + while (s && s[len] != '=') + s = strstr (s + len, bootflag); + + if (s && ((s[len + 1] != '0' && s[len + 1] != '1') || s[len + 2] != '\n')) + { + fprintf (stderr, "Pre-existing bootflag '%s' has unexpected value\n", bootflag); + return 1; + } + + /* No pre-existing bootflag? -> find free space */ + if (!s) + { + for (i = 0; i < (len + 3); i++) + buf[i] = '#'; + buf[i] = 0; + s = strstr (env, buf); + } + + if (!s) + { + fprintf (stderr, "No space in grubenv to store bootflag '%s'\n", bootflag); + return 1; + } + + /* The grubenv is not 0 terminated, so memcpy the name + '=' , '1', '\n' */ + snprintf(buf, sizeof(buf), "%s=1\n", bootflag); + memcpy(s, buf, len + 3); + + /* "r+", don't truncate so that the diskspace stays reserved */ + f = fopen (GRUBENV, "r+"); + if (!f) + { + perror ("Error opening " GRUBENV " for writing"); + return 1; + } + + ret = fwrite (env, 1, GRUBENV_SIZE, f); + if (ret != GRUBENV_SIZE) + { + perror ("Error writing to " GRUBENV); + return 1; + } + + ret = fflush (f); + if (ret) + { + perror ("Error flushing " GRUBENV); + return 1; + } + + fsync (fileno (f)); + fclose (f); + + return 0; +} From b0b06ea15e2909e75458c2de03f4569585da4133 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 19 Jun 2018 15:20:54 +0200 Subject: [PATCH 083/367] docs: Add grub-boot-indeterminate.service example This is an example service file, for use from /lib/systemd/system/system-update.target.wants to increment the boot_indeterminate variable when doing offline updates. Signed-off-by: Hans de Goede --- docs/grub-boot-indeterminate.service | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 docs/grub-boot-indeterminate.service diff --git a/docs/grub-boot-indeterminate.service b/docs/grub-boot-indeterminate.service new file mode 100644 index 0000000000..6c8dcb186b --- /dev/null +++ b/docs/grub-boot-indeterminate.service @@ -0,0 +1,11 @@ +[Unit] +Description=Mark boot as indeterminate +DefaultDependencies=false +Requires=sysinit.target +After=sysinit.target +Wants=system-update-pre.target +Before=system-update-pre.target + +[Service] +Type=oneshot +ExecStart=/usr/bin/grub2-editenv - incr boot_indeterminate From 33fabd4c5dc8d82f1ae636a4449d6f44a7f99c67 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 11 Jul 2018 13:43:15 -0400 Subject: [PATCH 084/367] gentpl: add 'disable = ' support Signed-off-by: Peter Jones --- gentpl.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/gentpl.py b/gentpl.py index c86550d4f9..f3c5f84f85 100644 --- a/gentpl.py +++ b/gentpl.py @@ -592,11 +592,21 @@ def platform_conditional(platform, closure): # }; # def foreach_enabled_platform(defn, closure): + enabled = False + disabled = False if 'enable' in defn: + enabled = True for platform in GRUB_PLATFORMS: if platform_tagged(defn, platform, "enable"): platform_conditional(platform, closure) - else: + + if 'disable' in defn: + disabled = True + for platform in GRUB_PLATFORMS: + if not platform_tagged(defn, platform, "disable"): + platform_conditional(platform, closure) + + if not enabled and not disabled: for platform in GRUB_PLATFORMS: platform_conditional(platform, closure) @@ -655,6 +665,8 @@ def first_time(defn, snippet): def is_platform_independent(defn): if 'enable' in defn: return False + if 'disable' in defn: + return False for suffix in [ "", "_nodist" ]: template = platform_values(defn, GRUB_PLATFORMS[0], suffix) for platform in GRUB_PLATFORMS[1:]: From 66b9ec1d1a29ec7a6e48e2294a5156173fd81420 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 11 Jul 2019 11:04:24 +0200 Subject: [PATCH 085/367] gentpl: add 'pc' firmware type Signed-off-by: Peter Jones --- gentpl.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gentpl.py b/gentpl.py index f3c5f84f85..f09b336869 100644 --- a/gentpl.py +++ b/gentpl.py @@ -51,6 +51,7 @@ GROUPS["riscv64"] = [ "riscv64_efi" ] # Groups based on firmware +GROUPS["pc"] = [ "i386_pc" ] GROUPS["efi"] = [ "i386_efi", "x86_64_efi", "ia64_efi", "arm_efi", "arm64_efi", "riscv32_efi", "riscv64_efi" ] GROUPS["ieee1275"] = [ "i386_ieee1275", "sparc64_ieee1275", "powerpc_ieee1275" ] From 496b61e9323dc98024a0d666309489e391ab1c8b Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 30 Jul 2018 14:06:42 -0400 Subject: [PATCH 086/367] efinet: also use the firmware acceleration for http Signed-off-by: Peter Jones --- grub-core/net/efi/net.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/grub-core/net/efi/net.c b/grub-core/net/efi/net.c index 4bb308026c..6603cd83ed 100644 --- a/grub-core/net/efi/net.c +++ b/grub-core/net/efi/net.c @@ -1324,7 +1324,9 @@ grub_efi_net_boot_from_https (void) && (subtype == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)) { grub_efi_uri_device_path_t *uri_dp = (grub_efi_uri_device_path_t *) dp; - return (grub_strncmp ((const char*)uri_dp->uri, "https://", sizeof ("https://") - 1) == 0) ? 1 : 0; + grub_dprintf ("efinet", "url:%s\n", (const char *)uri_dp->uri); + return (grub_strncmp ((const char *)uri_dp->uri, "https://", sizeof ("https://") - 1) == 0 || + grub_strncmp ((const char *)uri_dp->uri, "http://", sizeof ("http://") - 1) == 0); } if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp)) From 7463b00eba85be61feebfec010633835f4ac0c08 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 30 Jul 2018 16:39:57 -0400 Subject: [PATCH 087/367] efi/http: Make root_url reflect the protocol+hostname of our boot url. This lets you write config files that don't know urls. Signed-off-by: Peter Jones --- grub-core/net/efi/http.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/grub-core/net/efi/http.c b/grub-core/net/efi/http.c index 3f61fd2fa5..243acbaa35 100644 --- a/grub-core/net/efi/http.c +++ b/grub-core/net/efi/http.c @@ -4,6 +4,7 @@ #include #include #include +#include static void http_configure (struct grub_efi_net_device *dev, int prefer_ip6) @@ -351,6 +352,24 @@ grub_efihttp_open (struct grub_efi_net_device *dev, grub_err_t err; grub_off_t size; char *buf; + char *root_url; + grub_efi_ipv6_address_t address; + const char *rest; + + if (grub_efi_string_to_ip6_address (file->device->net->server, &address, &rest) && *rest == 0) + root_url = grub_xasprintf ("%s://[%s]", type ? "https" : "http", file->device->net->server); + else + root_url = grub_xasprintf ("%s://%s", type ? "https" : "http", file->device->net->server); + if (root_url) + { + grub_env_unset ("root_url"); + grub_env_set ("root_url", root_url); + grub_free (root_url); + } + else + { + return grub_errno; + } err = efihttp_request (dev->http, file->device->net->server, file->device->net->name, type, 1, 0); if (err != GRUB_ERR_NONE) From 22f43201a92e7b5ccf71dc4e10aa63777c3fe893 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 26 Jun 2018 17:16:06 -0400 Subject: [PATCH 088/367] Make it so we can tell configure which cflags utils are built with This lets us have kernel.img be built with TARGET_CFLAGS but grub-mkimage and friends built with HOST_CFLAGS. That in turn lets us build with an ARM compiler that only has hard-float ABI versions of crt*.o and libgcc*, but still use soft float for grub.efi. Signed-off-by: Peter Jones --- conf/Makefile.common | 23 +++++++++++---------- configure.ac | 49 +++++++++++++++++++++++++++++++++++++++++++- gentpl.py | 8 ++++---- 3 files changed, 64 insertions(+), 16 deletions(-) diff --git a/conf/Makefile.common b/conf/Makefile.common index 5f0ef96985..2ff9b39357 100644 --- a/conf/Makefile.common +++ b/conf/Makefile.common @@ -40,24 +40,25 @@ CPPFLAGS_KERNEL = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) -DGRUB_KERNEL=1 CCASFLAGS_KERNEL = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM) STRIPFLAGS_KERNEL = -R .eh_frame -R .rel.dyn -R .reginfo -R .note -R .comment -R .drectve -R .note.gnu.gold-version -R .MIPS.abiflags -R .ARM.exidx -R .note.gnu.property -R .gnu.build.attributes -CFLAGS_MODULE = $(CFLAGS_PLATFORM) -ffreestanding -LDFLAGS_MODULE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-r,-d -CPPFLAGS_MODULE = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) -CCASFLAGS_MODULE = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM) +CFLAGS_MODULE = $(TARGET_CFLAGS) $(CFLAGS_PLATFORM) -ffreestanding +LDFLAGS_MODULE = $(TARGET_LDFLAGS) $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-r,-d +CPPFLAGS_MODULE = $(TARGET_CPPFLAGS) $(CPPFLAGS_DEFAULT) $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) +CCASFLAGS_MODULE = $(TARGET_CCASFLAGS) $(CCASFLAGS_DEFAULT) $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM) CFLAGS_IMAGE = $(CFLAGS_PLATFORM) -fno-builtin LDFLAGS_IMAGE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-S CPPFLAGS_IMAGE = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) CCASFLAGS_IMAGE = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM) -CFLAGS_PROGRAM = -LDFLAGS_PROGRAM = -CPPFLAGS_PROGRAM = -CCASFLAGS_PROGRAM = +CFLAGS_PROGRAM = $(UTILS_CFLAGS) +LDFLAGS_PROGRAM = $(UTILS_LDFLAGS) +CPPFLAGS_PROGRAM = $(UTILS_CPPFLAGS) +CCASFLAGS_PROGRAM = $(UTILS_CCASFLAGS) -CFLAGS_LIBRARY = -CPPFLAGS_LIBRARY = -CCASFLAGS_LIBRARY = +CFLAGS_LIBRARY = $(UTILS_CFLAGS) +LDFLAGS_LIBRARY = $(UTILS_LDFLAGS) +CPPFLAGS_LIBRARY = $(UTILS_CPPFLAGS) +CCASFLAGS_LIBRARY = $(UTILS_CCASFLAGS) # Other variables diff --git a/configure.ac b/configure.ac index 302300711f..008f6c273b 100644 --- a/configure.ac +++ b/configure.ac @@ -849,11 +849,23 @@ if ( test "x$target_cpu" = xi386 || test "x$target_cpu" = xx86_64 ) && test "x$p TARGET_CFLAGS="$TARGET_CFLAGS -mno-mmx -mno-sse -mno-sse2 -mno-sse3 -mno-3dnow" fi +# Should grub utils get the host CFLAGS, or the target CFLAGS? +AC_ARG_WITH([utils], + AS_HELP_STRING([--with-utils=host|target|build], + [choose which flags to build utilities with. (default=target)]), + [have_with_utils=y], + [have_with_utils=n]) +if test x"$have_with_utils" = xy ; then + with_utils="$withval" +else + with_utils=target +fi + # GRUB doesn't use float or doubles at all. Yet some toolchains may decide # that floats are a good fit to run instead of what's written in the code. # Given that floating point unit is disabled (if present to begin with) # when GRUB is running which may result in various hard crashes. -if test x"$platform" != xemu ; then +if test x"$platform" != xemu -a x"$with_utils" == xtarget ; then AC_CACHE_CHECK([for options to get soft-float], grub_cv_target_cc_soft_float, [ grub_cv_target_cc_soft_float=no if test "x$target_cpu" = xarm64; then @@ -1954,6 +1966,41 @@ HOST_CPPFLAGS="$HOST_CPPFLAGS -I\$(top_builddir)/include" TARGET_CPPFLAGS="$TARGET_CPPFLAGS -I\$(top_srcdir)/include" TARGET_CPPFLAGS="$TARGET_CPPFLAGS -I\$(top_builddir)/include" +case "$with_utils" in + host) + UTILS_CFLAGS=$HOST_CFLAGS + UTILS_CPPFLAGS=$HOST_CPPFLAGS + UTILS_CCASFLAGS=$HOST_CCASFLAGS + UTILS_LDFLAGS=$HOST_LDFLAGS + ;; + target) + UTILS_CFLAGS=$TARGET_CFLAGS + UTILS_CPPFLAGS=$TARGET_CPPFLAGS + UTILS_CCASFLAGS=$TARGET_CCASFLAGS + UTILS_LDFLAGS=$TARGET_LDFLAGS + ;; + build) + UTILS_CFLAGS=$BUILD_CFLAGS + UTILS_CPPFLAGS=$BUILD_CPPFLAGS + UTILS_CCASFLAGS=$BUILD_CCASFLAGS + UTILS_LDFLAGS=$BUILD_LDFLAGS + ;; + *) + AC_MSG_ERROR([--with-utils must be either host, target, or build]) + ;; +esac +AC_MSG_NOTICE([Using $with_utils flags for utilities.]) + +unset CFLAGS +unset CPPFLAGS +unset CCASFLAGS +unset LDFLAGS + +AC_SUBST(UTILS_CFLAGS) +AC_SUBST(UTILS_CPPFLAGS) +AC_SUBST(UTILS_CCASFLAGS) +AC_SUBST(UTILS_LDFLAGS) + GRUB_TARGET_CPU="${target_cpu}" GRUB_PLATFORM="${platform}" diff --git a/gentpl.py b/gentpl.py index f09b336869..0e62e14666 100644 --- a/gentpl.py +++ b/gentpl.py @@ -697,10 +697,10 @@ def module(defn, platform): var_set(cname(defn) + "_SOURCES", platform_sources(defn, platform) + " ## platform sources") var_set("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform) + " ## platform nodist sources") var_set(cname(defn) + "_LDADD", platform_ldadd(defn, platform)) - var_set(cname(defn) + "_CFLAGS", "$(AM_CFLAGS) $(CFLAGS_MODULE) " + platform_cflags(defn, platform)) - var_set(cname(defn) + "_LDFLAGS", "$(AM_LDFLAGS) $(LDFLAGS_MODULE) " + platform_ldflags(defn, platform)) - var_set(cname(defn) + "_CPPFLAGS", "$(AM_CPPFLAGS) $(CPPFLAGS_MODULE) " + platform_cppflags(defn, platform)) - var_set(cname(defn) + "_CCASFLAGS", "$(AM_CCASFLAGS) $(CCASFLAGS_MODULE) " + platform_ccasflags(defn, platform)) + var_set(cname(defn) + "_CFLAGS", "$(CFLAGS_MODULE) " + platform_cflags(defn, platform)) + var_set(cname(defn) + "_LDFLAGS", "$(LDFLAGS_MODULE) " + platform_ldflags(defn, platform)) + var_set(cname(defn) + "_CPPFLAGS", "$(CPPFLAGS_MODULE) " + platform_cppflags(defn, platform)) + var_set(cname(defn) + "_CCASFLAGS", "$(CCASFLAGS_MODULE) " + platform_ccasflags(defn, platform)) var_set(cname(defn) + "_DEPENDENCIES", "$(TARGET_OBJ2ELF) " + platform_dependencies(defn, platform)) gvar_add("dist_noinst_DATA", extra_dist(defn)) From 601d7540b620bd72629b3d83a86c15b72f66f0b4 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 1 Aug 2018 10:24:52 -0400 Subject: [PATCH 089/367] module-verifier: make it possible to run checkers on grub-module-verifierxx.c This makes it so you can treat grub-module-verifierxx.c as a file you can build directly, so syntax checkers like vim's "syntastic" plugin, which uses "gcc -x c -fsyntax-only" to build it, will work. One still has to do whatever setup is required to make it pick the right include dirs, which -W options we use, etc., but this makes it so you can do the checking on the file you're editing, rather than on a different file. v2: fix the typo in the #else clause in util/grub-module-verifierXX.c Signed-off-by: Peter Jones --- util/grub-module-verifier32.c | 2 ++ util/grub-module-verifier64.c | 2 ++ util/grub-module-verifierXX.c | 9 +++++++++ 3 files changed, 13 insertions(+) diff --git a/util/grub-module-verifier32.c b/util/grub-module-verifier32.c index 257229f8f0..ba7d41aafe 100644 --- a/util/grub-module-verifier32.c +++ b/util/grub-module-verifier32.c @@ -1,2 +1,4 @@ #define MODULEVERIFIER_ELF32 1 +#ifndef GRUB_MODULE_VERIFIERXX #include "grub-module-verifierXX.c" +#endif diff --git a/util/grub-module-verifier64.c b/util/grub-module-verifier64.c index 4db6b4bedd..fc23ef800b 100644 --- a/util/grub-module-verifier64.c +++ b/util/grub-module-verifier64.c @@ -1,2 +1,4 @@ #define MODULEVERIFIER_ELF64 1 +#ifndef GRUB_MODULE_VERIFIERXX #include "grub-module-verifierXX.c" +#endif diff --git a/util/grub-module-verifierXX.c b/util/grub-module-verifierXX.c index ceb24309ae..a98e2f9b1a 100644 --- a/util/grub-module-verifierXX.c +++ b/util/grub-module-verifierXX.c @@ -1,3 +1,12 @@ +#define GRUB_MODULE_VERIFIERXX +#if !defined(MODULEVERIFIER_ELF32) && !defined(MODULEVERIFIER_ELF64) +#if __SIZEOF_POINTER__ == 8 +#include "grub-module-verifier64.c" +#else +#include "grub-module-verifier32.c" +#endif +#endif + #include #include From d8c41f88d13467bad779ae810e5319776f2595c9 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 11 Jul 2019 13:01:41 +0200 Subject: [PATCH 090/367] Rework how the fdt command builds. Trying to avoid all variants of: cat syminfo.lst | sort | gawk -f ../../grub-core/genmoddep.awk > moddep.lst || (rm -f moddep.lst; exit 1) grub_fdt_install in linux is not defined grub_fdt_load in linux is not defined grub_fdt_unload in linux is not defined grub_fdt_install in xen_boot is not defined grub_fdt_load in xen_boot is not defined grub_fdt_unload in xen_boot is not defined Signed-off-by: Peter Jones [javierm: Fix build with platform emu, aarch64, and risc-v] Signed-off-by: Javier Martinez Canillas Signed-off-by: Robbie Harwood --- grub-core/Makefile.am | 1 + grub-core/Makefile.core.def | 5 ++--- grub-core/lib/fdt.c | 2 -- grub-core/loader/efi/fdt.c | 2 ++ include/grub/fdt.h | 6 ++++++ 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index bfd29a3bf0..c2e8a82bce 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -76,6 +76,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/sb.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/env.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/env_private.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/err.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/fdt.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/file.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/fs.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i18n.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 12797336c9..4e7d90da76 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -177,7 +177,6 @@ kernel = { arm_coreboot = kern/arm/coreboot/init.c; arm_coreboot = kern/arm/coreboot/timer.c; arm_coreboot = kern/arm/coreboot/coreboot.S; - arm_coreboot = lib/fdt.c; arm_coreboot = bus/fdt.c; arm_coreboot = term/ps2.c; arm_coreboot = term/arm/pl050.c; @@ -351,6 +350,8 @@ kernel = { riscv64 = kern/riscv/cache_flush.S; riscv64 = kern/riscv/dl.c; + fdt = lib/fdt.c; + emu = disk/host.c; emu = kern/emu/cache_s.S; emu = kern/emu/hostdisk.c; @@ -1825,7 +1826,6 @@ module = { riscv32 = loader/riscv/linux.c; riscv64 = loader/riscv/linux.c; emu = loader/emu/linux.c; - fdt = lib/fdt.c; common = loader/linux.c; common = lib/cmdline.c; @@ -1837,7 +1837,6 @@ module = { module = { name = fdt; efi = loader/efi/fdt.c; - common = lib/fdt.c; enable = fdt; }; diff --git a/grub-core/lib/fdt.c b/grub-core/lib/fdt.c index 0d371c5633..37e04bd69e 100644 --- a/grub-core/lib/fdt.c +++ b/grub-core/lib/fdt.c @@ -21,8 +21,6 @@ #include #include -GRUB_MOD_LICENSE ("GPLv3+"); - #define FDT_SUPPORTED_VERSION 17 #define FDT_BEGIN_NODE 0x00000001 diff --git a/grub-core/loader/efi/fdt.c b/grub-core/loader/efi/fdt.c index c86f283d75..c572415d38 100644 --- a/grub-core/loader/efi/fdt.c +++ b/grub-core/loader/efi/fdt.c @@ -27,6 +27,8 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + static void *loaded_fdt; static void *fdt; diff --git a/include/grub/fdt.h b/include/grub/fdt.h index e609c7e411..3514aa4a5b 100644 --- a/include/grub/fdt.h +++ b/include/grub/fdt.h @@ -19,6 +19,9 @@ #ifndef GRUB_FDT_HEADER #define GRUB_FDT_HEADER 1 +#if !defined(GRUB_MACHINE_EMU) && \ + (defined(__arm__) || defined(__aarch64__) || defined(__riscv)) + #include #include @@ -144,4 +147,7 @@ int EXPORT_FUNC(grub_fdt_set_prop) (void *fdt, unsigned int nodeoffset, const ch grub_fdt_set_prop ((fdt), (nodeoffset), "reg", reg_64, 16); \ }) +#endif /* !defined(GRUB_MACHINE_EMU) && \ + (defined(__arm__) || defined(__aarch64__) || defined(__riscv)) */ + #endif /* ! GRUB_FDT_HEADER */ From d9d89bbe999184c839718fd376450cd1ff4652ab Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 2 Aug 2018 10:56:38 -0400 Subject: [PATCH 091/367] Disable non-wordsize allocations on arm Signed-off-by: Peter Jones --- configure.ac | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/configure.ac b/configure.ac index 008f6c273b..54462e0892 100644 --- a/configure.ac +++ b/configure.ac @@ -1260,6 +1260,26 @@ if test "x$target_cpu" = xarm; then done ]) + AC_CACHE_CHECK([for options to disable movt and movw relocations], + grub_cv_target_cc_mword_relocations, + [grub_cv_target_cc_mword_relocations=no + for cand in "-mword-relocations" ; do + if test x"$grub_cv_target_cc_mword_relocations" != xno ; then + break + fi + CFLAGS="$TARGET_CFLAGS $cand -Werror" + CPPFLAGS="$TARGET_CPPFLAGS" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_target_cc_mword_relocations="$cand"], + []) + done + ]) + if test x"$grub_cv_target_cc_mword_relocations" = xno ; then + AC_MSG_ERROR(["your compiler doesn't support disabling movw/movt relocations"]) + else + TARGET_CFLAGS="$TARGET_CFLAGS $grub_cv_target_cc_mword_relocations" + fi + if test x"$grub_cv_target_cc_mno_movt" != xno ; then # A trick so that clang doesn't see it on link stage TARGET_CPPFLAGS="$TARGET_CPPFLAGS $grub_cv_target_cc_mno_movt" From d67f15f7db5f481838a1366b560df38cc12a350d Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Thu, 16 Aug 2018 16:58:51 -0400 Subject: [PATCH 092/367] Prepend prefix when HTTP path is relative This sets a couple of variables. With the url http://www.example.com/foo/bar : http_path: /foo/bar http_url: http://www.example.com/foo/bar Signed-off-by: Peter Jones Signed-off-by: Stephen Benjamin Signed-off-by: Robbie Harwood --- grub-core/kern/main.c | 10 ++++- grub-core/net/efi/http.c | 84 ++++++++++++++++++++++++++++++---------- 2 files changed, 72 insertions(+), 22 deletions(-) diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c index d1de9fa687..1c540fc8c2 100644 --- a/grub-core/kern/main.c +++ b/grub-core/kern/main.c @@ -131,11 +131,19 @@ grub_set_prefix_and_root (void) if (fwdevice && fwpath) { char *fw_path; + char separator[3] = ")"; - fw_path = grub_xasprintf ("(%s)/%s", fwdevice, fwpath); + grub_dprintf ("fw_path", "\n"); + grub_dprintf ("fw_path", "fwdevice:\"%s\" fwpath:\"%s\"\n", fwdevice, fwpath); + + if (!grub_strncmp(fwdevice, "http", 4) && fwpath[0] != '/') + grub_strcpy(separator, ")/"); + + fw_path = grub_xasprintf ("(%s%s%s", fwdevice, separator, fwpath); if (fw_path) { grub_env_set ("fw_path", fw_path); + grub_dprintf ("fw_path", "fw_path:\"%s\"\n", fw_path); grub_free (fw_path); } } diff --git a/grub-core/net/efi/http.c b/grub-core/net/efi/http.c index 243acbaa35..de351b2cd0 100644 --- a/grub-core/net/efi/http.c +++ b/grub-core/net/efi/http.c @@ -9,10 +9,52 @@ static void http_configure (struct grub_efi_net_device *dev, int prefer_ip6) { + grub_efi_ipv6_address_t address; grub_efi_http_config_data_t http_config; grub_efi_httpv4_access_point_t httpv4_node; grub_efi_httpv6_access_point_t httpv6_node; grub_efi_status_t status; + int https; + char *http_url; + const char *rest, *http_server, *http_path = NULL; + + http_server = grub_env_get ("root"); + https = (grub_strncmp (http_server, "https", 5) == 0) ? 1 : 0; + + /* extract http server + port */ + if (http_server) + { + http_server = grub_strchr (http_server, ','); + if (http_server) + http_server++; + } + + /* fw_path is like (http,192.168.1.1:8000)/httpboot, extract path part */ + http_path = grub_env_get ("fw_path"); + if (http_path) + { + http_path = grub_strchr (http_path, ')'); + if (http_path) + { + http_path++; + grub_env_unset ("http_path"); + grub_env_set ("http_path", http_path); + } + } + + if (http_server && http_path) + { + if (grub_efi_string_to_ip6_address (http_server, &address, &rest) && *rest == 0) + http_url = grub_xasprintf ("%s://[%s]%s", https ? "https" : "http", http_server, http_path); + else + http_url = grub_xasprintf ("%s://%s%s", https ? "https" : "http", http_server, http_path); + if (http_url) + { + grub_env_unset ("http_url"); + grub_env_set ("http_url", http_url); + grub_free (http_url); + } + } grub_efi_http_t *http = dev->http; @@ -352,32 +394,32 @@ grub_efihttp_open (struct grub_efi_net_device *dev, grub_err_t err; grub_off_t size; char *buf; - char *root_url; - grub_efi_ipv6_address_t address; - const char *rest; - - if (grub_efi_string_to_ip6_address (file->device->net->server, &address, &rest) && *rest == 0) - root_url = grub_xasprintf ("%s://[%s]", type ? "https" : "http", file->device->net->server); - else - root_url = grub_xasprintf ("%s://%s", type ? "https" : "http", file->device->net->server); - if (root_url) - { - grub_env_unset ("root_url"); - grub_env_set ("root_url", root_url); - grub_free (root_url); - } - else - { + char *file_name = NULL; + const char *http_path; + + /* If path is relative, prepend http_path */ + http_path = grub_env_get ("http_path"); + if (http_path && file->device->net->name[0] != '/') { + file_name = grub_xasprintf ("%s/%s", http_path, file->device->net->name); + if (!file_name) return grub_errno; - } + } - err = efihttp_request (dev->http, file->device->net->server, file->device->net->name, type, 1, 0); + err = efihttp_request (dev->http, file->device->net->server, + file_name ? file_name : file->device->net->name, type, 1, 0); if (err != GRUB_ERR_NONE) - return err; + { + grub_free (file_name); + return err; + } - err = efihttp_request (dev->http, file->device->net->server, file->device->net->name, type, 0, &size); + err = efihttp_request (dev->http, file->device->net->server, + file_name ? file_name : file->device->net->name, type, 0, &size); + grub_free (file_name); if (err != GRUB_ERR_NONE) - return err; + { + return err; + } buf = grub_malloc (size); efihttp_read (dev, buf, size); From 96bd392fde16437291a5666de6ea114874e5fc04 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 27 Aug 2018 13:14:06 -0400 Subject: [PATCH 093/367] Make grub_error() more verbose Signed-off-by: Peter Jones --- grub-core/kern/err.c | 13 +++++++++++-- include/grub/err.h | 8 ++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/grub-core/kern/err.c b/grub-core/kern/err.c index 53c734de70..aebfe0cf83 100644 --- a/grub-core/kern/err.c +++ b/grub-core/kern/err.c @@ -33,15 +33,24 @@ static struct grub_error_saved grub_error_stack_items[GRUB_ERROR_STACK_SIZE]; static int grub_error_stack_pos; static int grub_error_stack_assert; +#ifdef grub_error +#undef grub_error +#endif + grub_err_t -grub_error (grub_err_t n, const char *fmt, ...) +grub_error (grub_err_t n, const char *file, const int line, const char *fmt, ...) { va_list ap; + int m; grub_errno = n; + m = grub_snprintf (grub_errmsg, sizeof (grub_errmsg), "%s:%d:", file, line); + if (m < 0) + m = 0; + va_start (ap, fmt); - grub_vsnprintf (grub_errmsg, sizeof (grub_errmsg), _(fmt), ap); + grub_vsnprintf (grub_errmsg + m, sizeof (grub_errmsg) - m, _(fmt), ap); va_end (ap); return n; diff --git a/include/grub/err.h b/include/grub/err.h index b08d5d0de4..c0f90ef07c 100644 --- a/include/grub/err.h +++ b/include/grub/err.h @@ -85,8 +85,12 @@ struct grub_error_saved extern grub_err_t EXPORT_VAR(grub_errno); extern char EXPORT_VAR(grub_errmsg)[GRUB_MAX_ERRMSG]; -grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *fmt, ...) - __attribute__ ((format (GNU_PRINTF, 2, 3))); +grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *file, const int line, const char *fmt, ...) + __attribute__ ((format (GNU_PRINTF, 4, 5))); + +#define grub_error(n, fmt, ...) grub_error (n, __FILE__, __LINE__, fmt, ##__VA_ARGS__) + + void EXPORT_FUNC(grub_fatal) (const char *fmt, ...) __attribute__ ((noreturn)); void EXPORT_FUNC(grub_error_push) (void); int EXPORT_FUNC(grub_error_pop) (void); From 84e942108dcb9ebc6f3f4db0b3c77ed6db5d2f9c Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 31 Aug 2018 16:42:03 -0400 Subject: [PATCH 094/367] Make "reset" an alias for the "reboot" command. I'm really tired of half the tools I get to use having one and the other half having the other. Signed-off-by: Peter Jones --- grub-core/commands/reboot.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/grub-core/commands/reboot.c b/grub-core/commands/reboot.c index 46d364c99a..f5cc228363 100644 --- a/grub-core/commands/reboot.c +++ b/grub-core/commands/reboot.c @@ -32,15 +32,18 @@ grub_cmd_reboot (grub_command_t cmd __attribute__ ((unused)), grub_reboot (); } -static grub_command_t cmd; +static grub_command_t reboot_cmd, reset_cmd; GRUB_MOD_INIT(reboot) { - cmd = grub_register_command ("reboot", grub_cmd_reboot, - 0, N_("Reboot the computer.")); + reboot_cmd = grub_register_command ("reboot", grub_cmd_reboot, + 0, N_("Reboot the computer.")); + reset_cmd = grub_register_command ("reset", grub_cmd_reboot, + 0, N_("Reboot the computer.")); } GRUB_MOD_FINI(reboot) { - grub_unregister_command (cmd); + grub_unregister_command (reboot_cmd); + grub_unregister_command (reset_cmd); } From fac4ccbb7b0b1d9cf094229a8eff29d57bc5f96e Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 11 Sep 2018 14:20:37 -0400 Subject: [PATCH 095/367] Add a "version" command This adds a command that shows you info about grub's version, the grub target platform, the compiler version, and if you built with --with-rpm-version=, the rpm package version. Signed-off-by: Peter Jones [rharwood: don't say GNU, commit message cleanup] Signed-off-by: Robbie Harwood --- config.h.in | 1 + configure.ac | 13 +++++++++ grub-core/Makefile.core.def | 5 ++++ grub-core/commands/version.c | 56 ++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 grub-core/commands/version.c diff --git a/config.h.in b/config.h.in index 9e8f9911b1..c7e316f0f1 100644 --- a/config.h.in +++ b/config.h.in @@ -59,6 +59,7 @@ #define GRUB_TARGET_CPU "@GRUB_TARGET_CPU@" #define GRUB_PLATFORM "@GRUB_PLATFORM@" +#define GRUB_RPM_VERSION "@GRUB_RPM_VERSION@" #define RE_ENABLE_I18N 1 diff --git a/configure.ac b/configure.ac index 54462e0892..7b4e1854d3 100644 --- a/configure.ac +++ b/configure.ac @@ -284,6 +284,19 @@ AC_SUBST(target_cpu) AC_SUBST(platform) # Define default variables +have_with_rpm_version=n +AC_ARG_WITH([rpm_version], + AS_HELP_STRING([--with-rpm-version=VERSION], + [set the rpm package version [[guessed]]]), + [have_with_rpm_version=y], + [have_with_rpm_version=n]) +if test x$have_with_rpm_version = xy; then + rpm_version="$with_rpm_version" +else + rpm_version="" +fi +GRUB_RPM_VERSION="$rpm_version" +AC_SUBST(GRUB_RPM_VERSION) have_with_bootdir=n AC_ARG_WITH([bootdir], diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 4e7d90da76..4f203533f5 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -579,6 +579,11 @@ image = { enable = mips_loongson; }; +module = { + name = version; + common = commands/version.c; +}; + module = { name = disk; common = lib/disk.c; diff --git a/grub-core/commands/version.c b/grub-core/commands/version.c new file mode 100644 index 0000000000..de0acb07ba --- /dev/null +++ b/grub-core/commands/version.c @@ -0,0 +1,56 @@ +/* version.c - Command to print the grub version and build info. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +grub_cmd_version (grub_command_t cmd UNUSED, int argc, char **args UNUSED) +{ + if (argc != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("no arguments expected")); + + grub_printf (_("GRUB version %s\n"), PACKAGE_VERSION); + grub_printf (_("Platform %s-%s\n"), GRUB_TARGET_CPU, GRUB_PLATFORM); + if (grub_strlen(GRUB_RPM_VERSION) != 0) + grub_printf (_("RPM package version %s\n"), GRUB_RPM_VERSION); + grub_printf (_("Compiler version %s\n"), __VERSION__); + + return 0; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(version) +{ + cmd = grub_register_command ("version", grub_cmd_version, NULL, + N_("Print version and build information.")); +} + +GRUB_MOD_FINI(version) +{ + grub_unregister_command (cmd); +} From 5e3b032459a345c18906ef5e848a97fe91c8d65c Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 11 Sep 2018 15:58:29 -0400 Subject: [PATCH 096/367] Add more dprintf, and nerf dprintf in script.c Signed-off-by: Peter Jones --- grub-core/disk/diskfilter.c | 3 +++ grub-core/disk/efi/efidisk.c | 1 + grub-core/kern/device.c | 1 + grub-core/script/script.c | 5 +++++ 4 files changed, 10 insertions(+) diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c index 0320115662..7cdffe3ebd 100644 --- a/grub-core/disk/diskfilter.c +++ b/grub-core/disk/diskfilter.c @@ -188,6 +188,8 @@ scan_disk (const char *name, int accept_diskfilter) grub_disk_t disk; static int scan_depth = 0; + grub_dprintf ("diskfilter", "scanning %s\n", name); + if (!accept_diskfilter && is_valid_diskfilter_name (name)) return 0; @@ -1212,6 +1214,7 @@ insert_array (grub_disk_t disk, const struct grub_diskfilter_pv_id *id, the same. */ if (pv->disk && grub_disk_native_sectors (disk) >= pv->part_size) return GRUB_ERR_NONE; + grub_dprintf ("diskfilter", "checking %s\n", disk->name); pv->disk = grub_disk_open (disk->name); if (!pv->disk) return grub_errno; diff --git a/grub-core/disk/efi/efidisk.c b/grub-core/disk/efi/efidisk.c index f077b5f553..fe8ba6e6c9 100644 --- a/grub-core/disk/efi/efidisk.c +++ b/grub-core/disk/efi/efidisk.c @@ -855,6 +855,7 @@ grub_efidisk_get_device_name (grub_efi_handle_t *handle) return 0; } + grub_dprintf ("efidisk", "getting disk for %s\n", device_name); parent = grub_disk_open (device_name); grub_free (dup_dp); diff --git a/grub-core/kern/device.c b/grub-core/kern/device.c index 73b8ecc0c0..f58b58c89d 100644 --- a/grub-core/kern/device.c +++ b/grub-core/kern/device.c @@ -34,6 +34,7 @@ grub_device_open (const char *name) { grub_device_t dev = 0; + grub_dprintf ("device", "opening device %s\n", name); if (! name) { name = grub_env_get ("root"); diff --git a/grub-core/script/script.c b/grub-core/script/script.c index ec4d4337c6..844e8343ca 100644 --- a/grub-core/script/script.c +++ b/grub-core/script/script.c @@ -22,6 +22,11 @@ #include #include +#ifdef grub_dprintf +#undef grub_dprintf +#endif +#define grub_dprintf(no, fmt, ...) + /* It is not possible to deallocate the memory when a syntax error was found. Because of that it is required to keep track of all memory allocations. The memory is freed in case of an error, or assigned From c62f6312619c5776611a6d1f899bafebad05608e Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 11 Jul 2019 14:38:57 +0200 Subject: [PATCH 097/367] arm/arm64 loader: Better memory allocation and error messages. On mustang, our memory map looks like: Type Physical start - end #Pages Size Attributes reserved 0000004000000000-00000040001fffff 00000200 2MiB UC WC WT WB conv-mem 0000004000200000-0000004393ffffff 00393e00 14654MiB UC WC WT WB ldr-code 0000004394000000-00000043f7ffffff 00064000 1600MiB UC WC WT WB BS-data 00000043f8000000-00000043f801ffff 00000020 128KiB UC WC WT WB conv-mem 00000043f8020000-00000043fa15bfff 0000213c 34032KiB UC WC WT WB ldr-code 00000043fa15c000-00000043fa2a1fff 00000146 1304KiB UC WC WT WB ldr-data 00000043fa2a2000-00000043fa3e8fff 00000147 1308KiB UC WC WT WB conv-mem 00000043fa3e9000-00000043fa3e9fff 00000001 4KiB UC WC WT WB ldr-data 00000043fa3ea000-00000043fa3eafff 00000001 4KiB UC WC WT WB ldr-code 00000043fa3eb000-00000043fa4affff 000000c5 788KiB UC WC WT WB BS-code 00000043fa4b0000-00000043fa59ffff 000000f0 960KiB UC WC WT WB RT-code 00000043fa5a0000-00000043fa5affff 00000010 64KiB RT UC WC WT WB RT-data 00000043fa5b0000-00000043fa5bffff 00000010 64KiB RT UC WC WT WB RT-code 00000043fa5c0000-00000043fa5cffff 00000010 64KiB RT UC WC WT WB ldr-data 00000043fa5d0000-00000043fa5d0fff 00000001 4KiB UC WC WT WB BS-code 00000043fa5d1000-00000043fa5ddfff 0000000d 52KiB UC WC WT WB reserved 00000043fa5de000-00000043fa60ffff 00000032 200KiB UC WC WT WB ACPI-rec 00000043fa610000-00000043fa6affff 000000a0 640KiB UC WC WT WB ACPI-nvs 00000043fa6b0000-00000043fa6bffff 00000010 64KiB UC WC WT WB ACPI-rec 00000043fa6c0000-00000043fa70ffff 00000050 320KiB UC WC WT WB RT-code 00000043fa710000-00000043fa72ffff 00000020 128KiB RT UC WC WT WB RT-data 00000043fa730000-00000043fa78ffff 00000060 384KiB RT UC WC WT WB RT-code 00000043fa790000-00000043fa79ffff 00000010 64KiB RT UC WC WT WB RT-data 00000043fa7a0000-00000043fa99ffff 00000200 2MiB RT UC WC WT WB RT-code 00000043fa9a0000-00000043fa9affff 00000010 64KiB RT UC WC WT WB RT-data 00000043fa9b0000-00000043fa9cffff 00000020 128KiB RT UC WC WT WB BS-code 00000043fa9d0000-00000043fa9d9fff 0000000a 40KiB UC WC WT WB reserved 00000043fa9da000-00000043fa9dbfff 00000002 8KiB UC WC WT WB conv-mem 00000043fa9dc000-00000043fc29dfff 000018c2 25352KiB UC WC WT WB BS-data 00000043fc29e000-00000043fc78afff 000004ed 5044KiB UC WC WT WB conv-mem 00000043fc78b000-00000043fca01fff 00000277 2524KiB UC WC WT WB BS-data 00000043fca02000-00000043fcea3fff 000004a2 4744KiB UC WC WT WB conv-mem 00000043fcea4000-00000043fcea4fff 00000001 4KiB UC WC WT WB BS-data 00000043fcea5000-00000043fd192fff 000002ee 3000KiB UC WC WT WB conv-mem 00000043fd193000-00000043fd2b0fff 0000011e 1144KiB UC WC WT WB BS-data 00000043fd2b1000-00000043ff80ffff 0000255f 38268KiB UC WC WT WB BS-code 00000043ff810000-00000043ff99ffff 00000190 1600KiB UC WC WT WB RT-code 00000043ff9a0000-00000043ff9affff 00000010 64KiB RT UC WC WT WB conv-mem 00000043ff9b0000-00000043ff9bffff 00000010 64KiB UC WC WT WB RT-data 00000043ff9c0000-00000043ff9effff 00000030 192KiB RT UC WC WT WB conv-mem 00000043ff9f0000-00000043ffa05fff 00000016 88KiB UC WC WT WB BS-data 00000043ffa06000-00000043ffffffff 000005fa 6120KiB UC WC WT WB MMIO 0000000010510000-0000000010510fff 00000001 4KiB RT MMIO 0000000010548000-0000000010549fff 00000002 8KiB RT MMIO 0000000017000000-0000000017001fff 00000002 8KiB RT MMIO 000000001c025000-000000001c025fff 00000001 4KiB RT This patch adds a requirement when we're trying to find the base of ram, that the memory we choose is actually /allocatable/ conventional memory, not merely write-combining. On this machine that means we wind up with an allocation around 0x4392XXXXXX, which is a reasonable address. This also changes grub_efi_allocate_pages_real() so that if 0 is allocated, it tries to allocate again starting with the same max address it did the first time, rather than interposing GRUB_EFI_MAX_USABLE_ADDRESS there, so that any per-platform constraints on its given address are maintained. Signed-off-by: Peter Jones --- grub-core/kern/efi/mm.c | 33 +++++++++++++---- grub-core/loader/arm64/linux.c | 68 +++++++++++++++++++++++++--------- 2 files changed, 76 insertions(+), 25 deletions(-) diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index f6aef0ef64..85ad4b4494 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -154,6 +154,7 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address, { grub_efi_status_t status; grub_efi_boot_services_t *b; + grub_efi_physical_address_t ret = address; /* Limit the memory access to less than 4GB for 32-bit platforms. */ if (address > GRUB_EFI_MAX_USABLE_ADDRESS) @@ -170,19 +171,22 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address, } b = grub_efi_system_table->boot_services; - status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &address); + status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &ret); if (status != GRUB_EFI_SUCCESS) { + grub_dprintf ("efi", + "allocate_pages(%d, %d, 0x%0lx, 0x%016lx) = 0x%016lx\n", + alloctype, memtype, pages, address, status); grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); return NULL; } - if (address == 0) + if (ret == 0) { /* Uggh, the address 0 was allocated... This is too annoying, so reallocate another one. */ - address = GRUB_EFI_MAX_USABLE_ADDRESS; - status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &address); + ret = address; + status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &ret); grub_efi_free_pages (0, pages); if (status != GRUB_EFI_SUCCESS) { @@ -191,9 +195,9 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address, } } - grub_efi_store_alloc (address, pages); + grub_efi_store_alloc (ret, pages); - return (void *) ((grub_addr_t) address); + return (void *) ((grub_addr_t) ret); } void * @@ -713,8 +717,21 @@ grub_efi_get_ram_base(grub_addr_t *base_addr) for (desc = memory_map, *base_addr = GRUB_EFI_MAX_USABLE_ADDRESS; (grub_addr_t) desc < ((grub_addr_t) memory_map + memory_map_size); desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size)) - if (desc->attribute & GRUB_EFI_MEMORY_WB) - *base_addr = grub_min (*base_addr, desc->physical_start); + { + if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY && + (desc->attribute & GRUB_EFI_MEMORY_WB)) + { + *base_addr = grub_min (*base_addr, desc->physical_start); + grub_dprintf ("efi", "setting base_addr=0x%016lx\n", *base_addr); + } + else + { + grub_dprintf ("efi", "ignoring address 0x%016lx\n", desc->physical_start); + } + } + + if (*base_addr == GRUB_EFI_MAX_USABLE_ADDRESS) + grub_dprintf ("efi", "base_addr 0x%016lx is probably wrong.\n", *base_addr); grub_free(memory_map); diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c index 04994d5c67..70a0075ec5 100644 --- a/grub-core/loader/arm64/linux.c +++ b/grub-core/loader/arm64/linux.c @@ -71,20 +71,25 @@ finalize_params_linux (void) { grub_efi_loaded_image_t *loaded_image = NULL; int node, retval, len; - + grub_err_t err = GRUB_ERR_NONE; void *fdt; fdt = grub_fdt_load (GRUB_EFI_LINUX_FDT_EXTRA_SPACE); - if (!fdt) - goto failure; + { + err = grub_error(GRUB_ERR_BAD_OS, "failed to load FDT"); + goto failure; + } node = grub_fdt_find_subnode (fdt, 0, "chosen"); if (node < 0) node = grub_fdt_add_subnode (fdt, 0, "chosen"); if (node < 1) - goto failure; + { + err = grub_error(grub_errno, "failed to load chosen fdt node."); + goto failure; + } /* Set initrd info */ if (initrd_start && initrd_end > initrd_start) @@ -95,15 +100,26 @@ finalize_params_linux (void) retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-start", initrd_start); if (retval) - goto failure; + { + err = grub_error(retval, "Failed to set linux,initrd-start property"); + goto failure; + } + retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-end", initrd_end); if (retval) - goto failure; + { + err = grub_error(retval, "Failed to set linux,initrd-end property"); + goto failure; + } } - if (grub_fdt_install() != GRUB_ERR_NONE) - goto failure; + retval = grub_fdt_install(); + if (retval != GRUB_ERR_NONE) + { + err = grub_error(retval, "Failed to install fdt"); + goto failure; + } grub_dprintf ("linux", "Installed/updated FDT configuration table @ %p\n", fdt); @@ -111,14 +127,20 @@ finalize_params_linux (void) /* Convert command line to UCS-2 */ loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle); if (!loaded_image) - goto failure; + { + err = grub_error(grub_errno, "Failed to install fdt"); + goto failure; + } loaded_image->load_options_size = len = (grub_strlen (linux_args) + 1) * sizeof (grub_efi_char16_t); loaded_image->load_options = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size)); if (!loaded_image->load_options) - return grub_error(GRUB_ERR_BAD_OS, "failed to create kernel parameters"); + { + err = grub_error(GRUB_ERR_BAD_OS, "failed to create kernel parameters"); + goto failure; + } loaded_image->load_options_size = 2 * grub_utf8_to_utf16 (loaded_image->load_options, len, @@ -128,7 +150,7 @@ finalize_params_linux (void) failure: grub_fdt_unload(); - return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT"); + return err; } static void @@ -212,16 +234,28 @@ grub_linux_unload (void) static void * allocate_initrd_mem (int initrd_pages) { - grub_addr_t max_addr; + grub_addr_t max_addr = 0; + grub_err_t err; + void *ret; + + err = grub_efi_get_ram_base (&max_addr); + if (err != GRUB_ERR_NONE) + { + grub_error (err, "grub_efi_get_ram_base() failed"); + return NULL; + } - if (grub_efi_get_ram_base (&max_addr) != GRUB_ERR_NONE) - return NULL; + grub_dprintf ("linux", "max_addr: 0x%016lx, INITRD_MAX_ADDRESS_OFFSET: 0x%016llx\n", + max_addr, INITRD_MAX_ADDRESS_OFFSET); max_addr += INITRD_MAX_ADDRESS_OFFSET - 1; + grub_dprintf ("linux", "calling grub_efi_allocate_pages_real (0x%016lx, 0x%08x, EFI_ALLOCATE_MAX_ADDRESS, EFI_LOADER_DATA)", max_addr, initrd_pages); - return grub_efi_allocate_pages_real (max_addr, initrd_pages, - GRUB_EFI_ALLOCATE_MAX_ADDRESS, - GRUB_EFI_LOADER_DATA); + ret = grub_efi_allocate_pages_real (max_addr, initrd_pages, + GRUB_EFI_ALLOCATE_MAX_ADDRESS, + GRUB_EFI_LOADER_DATA); + grub_dprintf ("linux", "got 0x%016llx\n", (unsigned long long)ret); + return ret; } static grub_err_t From f65947f4496efa4542569d6e3e4e7597223086e0 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 11 Jul 2019 17:17:02 +0200 Subject: [PATCH 098/367] Try to pick better locations for kernel and initrd - Don't limit allocations on 64-bit platforms to < 0x[37f]fffffff if we're using the "large" code model ; use __UINTPTR_MAX__. - Get the comparison right to check the address we've allocated. - Fix the allocation for the command line as well. *But*, when we did this some systems started failing badly; coudln't parse partition tables, etc. What's going on here is the disk controller is silently failing DMAs to addresses above 4GB, so we're trying to parse uninitialized (or HW zeroed) ram when looking for the partition table, etc. So to limit this, we make grub_malloc() pick addresses below 4GB on x86_64, but the direct EFI page allocation functions can get addresses above that. Additionally, we now try to locate kernel+initrd+cmdline+etc below 0x7fffffff, and if they're too big to fit any memory window there, then we try a higher address. Signed-off-by: Peter Jones [david.abdurachmanov: fix macro for riscv64] Signed-off-by: David Abdurachmanov Signed-off-by: Robbie Harwood --- grub-core/kern/efi/mm.c | 8 ++++---- grub-core/loader/i386/efi/linux.c | 24 +++++++++++++++++------- include/grub/arm/efi/memory.h | 1 + include/grub/arm64/efi/memory.h | 1 + include/grub/i386/efi/memory.h | 1 + include/grub/ia64/efi/memory.h | 1 + include/grub/riscv64/efi/memory.h | 1 + include/grub/x86_64/efi/memory.h | 4 +++- 8 files changed, 29 insertions(+), 12 deletions(-) diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index 85ad4b4494..e84961d078 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -122,7 +122,7 @@ grub_efi_allocate_pages_max (grub_efi_physical_address_t max, grub_efi_boot_services_t *b; grub_efi_physical_address_t address = max; - if (max > 0xffffffff) + if (max > GRUB_EFI_MAX_USABLE_ADDRESS) return 0; b = grub_efi_system_table->boot_services; @@ -480,7 +480,7 @@ filter_memory_map (grub_efi_memory_descriptor_t *memory_map, { if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY #if 1 - && desc->physical_start <= GRUB_EFI_MAX_USABLE_ADDRESS + && desc->physical_start <= GRUB_EFI_MAX_ALLOCATION_ADDRESS #endif && desc->physical_start + PAGES_TO_BYTES (desc->num_pages) > 0x100000 && desc->num_pages != 0) @@ -498,9 +498,9 @@ filter_memory_map (grub_efi_memory_descriptor_t *memory_map, #if 1 if (BYTES_TO_PAGES (filtered_desc->physical_start) + filtered_desc->num_pages - > BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_USABLE_ADDRESS)) + > BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_ALLOCATION_ADDRESS)) filtered_desc->num_pages - = (BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_USABLE_ADDRESS) + = (BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_ALLOCATION_ADDRESS) - BYTES_TO_PAGES (filtered_desc->physical_start)); #endif diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 3017d0f3e5..33e981e76e 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -27,6 +27,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -106,7 +107,9 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), size += ALIGN_UP (grub_file_size (files[i]), 4); } - initrd_mem = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(size)); + initrd_mem = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS, BYTES_TO_PAGES(size)); + if (!initrd_mem) + initrd_mem = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS, BYTES_TO_PAGES(size)); if (!initrd_mem) { grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate initrd")); @@ -202,8 +205,11 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } - params = grub_efi_allocate_pages_max (0x3fffffff, + params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS, BYTES_TO_PAGES(sizeof(*params))); + if (!params) + params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS, + BYTES_TO_PAGES(sizeof(*params))); if (! params) { grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters"); @@ -273,8 +279,11 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), #endif grub_dprintf ("linux", "setting up cmdline\n"); - linux_cmdline = grub_efi_allocate_pages_max(0x3fffffff, - BYTES_TO_PAGES(lh->cmdline_size + 1)); + linux_cmdline = grub_efi_allocate_pages_max(GRUB_EFI_MAX_ALLOCATION_ADDRESS, + BYTES_TO_PAGES(lh->cmdline_size + 1)); + if (!linux_cmdline) + linux_cmdline = grub_efi_allocate_pages_max(GRUB_EFI_MAX_USABLE_ADDRESS, + BYTES_TO_PAGES(lh->cmdline_size + 1)); if (!linux_cmdline) { grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate cmdline")); @@ -301,11 +310,12 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), kernel_mem = grub_efi_allocate_pages_max(lh->pref_address, BYTES_TO_PAGES(lh->init_size)); - if (!kernel_mem) - kernel_mem = grub_efi_allocate_pages_max(0x3fffffff, + kernel_mem = grub_efi_allocate_pages_max(GRUB_EFI_MAX_ALLOCATION_ADDRESS, + BYTES_TO_PAGES(lh->init_size)); + if (!kernel_mem) + kernel_mem = grub_efi_allocate_pages_max(GRUB_EFI_MAX_USABLE_ADDRESS, BYTES_TO_PAGES(lh->init_size)); - if (!kernel_mem) { grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate kernel")); diff --git a/include/grub/arm/efi/memory.h b/include/grub/arm/efi/memory.h index 2c64918e3f..a4c2ec8350 100644 --- a/include/grub/arm/efi/memory.h +++ b/include/grub/arm/efi/memory.h @@ -2,5 +2,6 @@ #include #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffff +#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS #endif /* ! GRUB_MEMORY_CPU_HEADER */ diff --git a/include/grub/arm64/efi/memory.h b/include/grub/arm64/efi/memory.h index c6cb324171..acb61dca44 100644 --- a/include/grub/arm64/efi/memory.h +++ b/include/grub/arm64/efi/memory.h @@ -2,5 +2,6 @@ #include #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffffffffULL +#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS #endif /* ! GRUB_MEMORY_CPU_HEADER */ diff --git a/include/grub/i386/efi/memory.h b/include/grub/i386/efi/memory.h index 2c64918e3f..a4c2ec8350 100644 --- a/include/grub/i386/efi/memory.h +++ b/include/grub/i386/efi/memory.h @@ -2,5 +2,6 @@ #include #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffff +#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS #endif /* ! GRUB_MEMORY_CPU_HEADER */ diff --git a/include/grub/ia64/efi/memory.h b/include/grub/ia64/efi/memory.h index 2c64918e3f..a4c2ec8350 100644 --- a/include/grub/ia64/efi/memory.h +++ b/include/grub/ia64/efi/memory.h @@ -2,5 +2,6 @@ #include #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffff +#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS #endif /* ! GRUB_MEMORY_CPU_HEADER */ diff --git a/include/grub/riscv64/efi/memory.h b/include/grub/riscv64/efi/memory.h index c6cb324171..acb61dca44 100644 --- a/include/grub/riscv64/efi/memory.h +++ b/include/grub/riscv64/efi/memory.h @@ -2,5 +2,6 @@ #include #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffffffffULL +#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS #endif /* ! GRUB_MEMORY_CPU_HEADER */ diff --git a/include/grub/x86_64/efi/memory.h b/include/grub/x86_64/efi/memory.h index 46e9145a30..e81cfb3221 100644 --- a/include/grub/x86_64/efi/memory.h +++ b/include/grub/x86_64/efi/memory.h @@ -2,9 +2,11 @@ #include #if defined (__code_model_large__) -#define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffff +#define GRUB_EFI_MAX_USABLE_ADDRESS __UINTPTR_MAX__ +#define GRUB_EFI_MAX_ALLOCATION_ADDRESS 0x7fffffff #else #define GRUB_EFI_MAX_USABLE_ADDRESS 0x7fffffff +#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS #endif #endif /* ! GRUB_MEMORY_CPU_HEADER */ From 64dd90633ce61e9943c1499a6716acf5f9bd929c Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 11 Jul 2019 18:03:25 +0200 Subject: [PATCH 099/367] Attempt to fix up all the places -Wsign-compare=error finds. Signed-off-by: Peter Jones --- grub-core/kern/emu/misc.c | 2 +- grub-core/lib/reed_solomon.c | 4 ++-- grub-core/osdep/linux/blocklist.c | 2 +- grub-core/osdep/linux/getroot.c | 2 +- grub-core/osdep/linux/hostdisk.c | 2 +- util/grub-fstest.c | 2 +- util/grub-menulst2cfg.c | 2 +- util/grub-mkfont.c | 13 +++++++------ util/grub-probe.c | 2 +- util/setup.c | 2 +- 10 files changed, 17 insertions(+), 16 deletions(-) diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c index 0ff13bcaf8..d278c2921f 100644 --- a/grub-core/kern/emu/misc.c +++ b/grub-core/kern/emu/misc.c @@ -185,7 +185,7 @@ grub_util_get_image_size (const char *path) sz = ftello (f); if (sz < 0) grub_util_error (_("cannot open `%s': %s"), path, strerror (errno)); - if (sz != (size_t) sz) + if (sz > (off_t)(GRUB_SIZE_MAX >> 1)) grub_util_error (_("file `%s' is too big"), path); ret = (size_t) sz; diff --git a/grub-core/lib/reed_solomon.c b/grub-core/lib/reed_solomon.c index 467305b46a..79037c093f 100644 --- a/grub-core/lib/reed_solomon.c +++ b/grub-core/lib/reed_solomon.c @@ -157,7 +157,7 @@ static void rs_encode (gf_single_t *data, grub_size_t s, grub_size_t rs) { gf_single_t *rs_polynomial; - int i, j; + unsigned int i, j; gf_single_t *m; m = xcalloc (s + rs, sizeof (gf_single_t)); grub_memcpy (m, data, s * sizeof (gf_single_t)); @@ -324,7 +324,7 @@ static void encode_block (gf_single_t *ptr, grub_size_t s, gf_single_t *rptr, grub_size_t rs) { - int i, j; + unsigned int i, j; for (i = 0; i < SECTOR_SIZE; i++) { grub_size_t ds = (s + SECTOR_SIZE - 1 - i) / SECTOR_SIZE; diff --git a/grub-core/osdep/linux/blocklist.c b/grub-core/osdep/linux/blocklist.c index c77d6085cc..42a315031f 100644 --- a/grub-core/osdep/linux/blocklist.c +++ b/grub-core/osdep/linux/blocklist.c @@ -109,7 +109,7 @@ grub_install_get_blocklist (grub_device_t root_dev, else { struct fiemap *fie2; - int i; + unsigned int i; fie2 = xmalloc (sizeof (*fie2) + fie1.fm_mapped_extents * sizeof (fie1.fm_extents[1])); diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c index 28790307e0..9f730b3518 100644 --- a/grub-core/osdep/linux/getroot.c +++ b/grub-core/osdep/linux/getroot.c @@ -236,7 +236,7 @@ grub_find_root_devices_from_btrfs (const char *dir) { int fd; struct btrfs_ioctl_fs_info_args fsi; - int i, j = 0; + unsigned int i, j = 0; char **ret; fd = open (dir, 0); diff --git a/grub-core/osdep/linux/hostdisk.c b/grub-core/osdep/linux/hostdisk.c index da62f924e3..7bc99ac1c1 100644 --- a/grub-core/osdep/linux/hostdisk.c +++ b/grub-core/osdep/linux/hostdisk.c @@ -83,7 +83,7 @@ grub_util_get_fd_size_os (grub_util_fd_t fd, const char *name, unsigned *log_sec if (sector_size & (sector_size - 1) || !sector_size) return -1; for (log_sector_size = 0; - (1 << log_sector_size) < sector_size; + (1U << log_sector_size) < sector_size; log_sector_size++); if (log_secsize) diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 8386564200..bfcef852d8 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -323,7 +323,7 @@ cmd_cmp (char *src, char *dest) read_file (src, cmp_hook, ff); { - grub_uint64_t pre; + long long pre; pre = ftell (ff); fseek (ff, 0, SEEK_END); if (pre != ftell (ff)) diff --git a/util/grub-menulst2cfg.c b/util/grub-menulst2cfg.c index a39f869394..358d604210 100644 --- a/util/grub-menulst2cfg.c +++ b/util/grub-menulst2cfg.c @@ -34,7 +34,7 @@ main (int argc, char **argv) char *buf = NULL; size_t bufsize = 0; char *suffix = xstrdup (""); - int suffixlen = 0; + size_t suffixlen = 0; const char *out_fname = 0; grub_util_host_init (&argc, &argv); diff --git a/util/grub-mkfont.c b/util/grub-mkfont.c index 0fe45a6103..3e09240b99 100644 --- a/util/grub-mkfont.c +++ b/util/grub-mkfont.c @@ -138,7 +138,8 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face, int width, height; int cuttop, cutbottom, cutleft, cutright; grub_uint8_t *data; - int mask, i, j, bitmap_size; + int mask, i, bitmap_size; + unsigned int j; FT_GlyphSlot glyph; int flag = FT_LOAD_RENDER | FT_LOAD_MONOCHROME; FT_Error err; @@ -183,7 +184,7 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face, cuttop = cutbottom = cutleft = cutright = 0; else { - for (cuttop = 0; cuttop < glyph->bitmap.rows; cuttop++) + for (cuttop = 0; cuttop < (long)glyph->bitmap.rows; cuttop++) { for (j = 0; j < glyph->bitmap.width; j++) if (glyph->bitmap.buffer[j / 8 + cuttop * glyph->bitmap.pitch] @@ -203,10 +204,10 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face, break; } cutbottom = glyph->bitmap.rows - 1 - cutbottom; - if (cutbottom + cuttop >= glyph->bitmap.rows) + if (cutbottom + cuttop >= (long)glyph->bitmap.rows) cutbottom = 0; - for (cutleft = 0; cutleft < glyph->bitmap.width; cutleft++) + for (cutleft = 0; cutleft < (long)glyph->bitmap.width; cutleft++) { for (j = 0; j < glyph->bitmap.rows; j++) if (glyph->bitmap.buffer[cutleft / 8 + j * glyph->bitmap.pitch] @@ -225,7 +226,7 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face, break; } cutright = glyph->bitmap.width - 1 - cutright; - if (cutright + cutleft >= glyph->bitmap.width) + if (cutright + cutleft >= (long)glyph->bitmap.width) cutright = 0; } @@ -262,7 +263,7 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face, mask = 0; data = &glyph_info->bitmap[0] - 1; - for (j = cuttop; j < height + cuttop; j++) + for (j = cuttop; j < (long)height + cuttop; j++) for (i = cutleft; i < width + cutleft; i++) add_pixel (&data, &mask, glyph->bitmap.buffer[i / 8 + j * glyph->bitmap.pitch] & diff --git a/util/grub-probe.c b/util/grub-probe.c index c08e46bbb4..c6fac732b4 100644 --- a/util/grub-probe.c +++ b/util/grub-probe.c @@ -798,7 +798,7 @@ argp_parser (int key, char *arg, struct argp_state *state) case 't': { - int i; + unsigned int i; for (i = PRINT_FS; i < ARRAY_SIZE (targets); i++) if (strcmp (arg, targets[i]) == 0) diff --git a/util/setup.c b/util/setup.c index da5f2c07f5..8b22bb8cca 100644 --- a/util/setup.c +++ b/util/setup.c @@ -406,7 +406,7 @@ SETUP (const char *dir, int is_ldm; grub_err_t err; grub_disk_addr_t *sectors; - int i; + unsigned int i; grub_fs_t fs; unsigned int nsec, maxsec; From 8989b0d3ae78b36de2c8bf2c3cdf4ff6e26fd6ae Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 11 Jul 2019 18:20:37 +0200 Subject: [PATCH 100/367] Don't use -Wno-sign-compare -Wno-conversion -Wno-error, do use -Wextra. Signed-off-by: Peter Jones --- conf/Makefile.common | 2 +- configure.ac | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/conf/Makefile.common b/conf/Makefile.common index 2ff9b39357..35e14ff017 100644 --- a/conf/Makefile.common +++ b/conf/Makefile.common @@ -66,7 +66,7 @@ grubconfdir = $(sysconfdir)/grub.d platformdir = $(pkglibdir)/$(target_cpu)-$(platform) starfielddir = $(pkgdatadir)/themes/starfield -CFLAGS_GNULIB = -Wno-undef -Wno-sign-compare -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code -Wno-conversion +CFLAGS_GNULIB = -Wno-undef -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code CPPFLAGS_GNULIB = -I$(top_builddir)/grub-core/lib/gnulib -I$(top_srcdir)/grub-core/lib/gnulib CFLAGS_POSIX = -fno-builtin diff --git a/configure.ac b/configure.ac index 7b4e1854d3..490353713a 100644 --- a/configure.ac +++ b/configure.ac @@ -1452,11 +1452,11 @@ fi # Set them to their new values for the tests below. CC="$TARGET_CC" if test x"$platform" = xemu ; then -CFLAGS="$TARGET_CFLAGS -Wno-error" +CFLAGS="$TARGET_CFLAGS" elif test "x$TARGET_APPLE_LINKER" = x1 ; then -CFLAGS="$TARGET_CFLAGS -nostdlib -static -Wno-error" +CFLAGS="$TARGET_CFLAGS -nostdlib -static" else -CFLAGS="$TARGET_CFLAGS -nostdlib -Wno-error" +CFLAGS="$TARGET_CFLAGS -nostdlib" fi CPPFLAGS="$TARGET_CPPFLAGS" @@ -1990,6 +1990,14 @@ if test x"$enable_werror" != xno ; then HOST_CFLAGS="$HOST_CFLAGS -Werror" fi +AC_ARG_ENABLE([wextra], + [AS_HELP_STRING([--disable-wextra], + [do not use -Wextra when building GRUB])]) +if test x"$enable_wextra" != xno ; then + TARGET_CFLAGS="$TARGET_CFLAGS -Wextra" + HOST_CFLAGS="$HOST_CFLAGS -Wextra" +fi + TARGET_CPP="$TARGET_CC -E" TARGET_CCAS=$TARGET_CC From 7f3c9b0e9d8bb94411b34b849cb984169fff130b Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 12 Jul 2019 09:53:32 +0200 Subject: [PATCH 101/367] x86-efi: Use bounce buffers for reading to addresses > 4GB Lots of machines apparently can't DMA correctly above 4GB during UEFI, so use bounce buffers for the initramfs read. Signed-off-by: Peter Jones --- grub-core/loader/i386/efi/linux.c | 52 ++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 33e981e76e..2f0336809e 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -35,11 +35,16 @@ static grub_dl_t my_mod; static int loaded; static void *kernel_mem; static grub_uint64_t kernel_size; -static grub_uint8_t *initrd_mem; +static void *initrd_mem; static grub_uint32_t handover_offset; struct linux_kernel_params *params; static char *linux_cmdline; +#define MIN(a, b) \ + ({ typeof (a) _a = (a); \ + typeof (b) _b = (b); \ + _a < _b ? _a : _b; }) + #define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12) static grub_err_t @@ -73,6 +78,44 @@ grub_linuxefi_unload (void) return GRUB_ERR_NONE; } +#define BOUNCE_BUFFER_MAX 0x10000000ull + +static grub_ssize_t +read(grub_file_t file, grub_uint8_t *bufp, grub_size_t len) +{ + grub_ssize_t bufpos = 0; + static grub_size_t bbufsz = 0; + static char *bbuf = NULL; + + if (bbufsz == 0) + bbufsz = MIN(BOUNCE_BUFFER_MAX, len); + + while (!bbuf && bbufsz) + { + bbuf = grub_malloc(bbufsz); + if (!bbuf) + bbufsz >>= 1; + } + if (!bbuf) + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate bounce buffer")); + + while (bufpos < (long long)len) + { + grub_ssize_t sz; + + sz = grub_file_read (file, bbuf, MIN(bbufsz, len - bufpos)); + if (sz < 0) + return sz; + if (sz == 0) + break; + + grub_memcpy(bufp + bufpos, bbuf, sz); + bufpos += sz; + } + + return bufpos; +} + static grub_err_t grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) @@ -126,7 +169,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), for (i = 0; i < nfiles; i++) { grub_ssize_t cursize = grub_file_size (files[i]); - if (grub_file_read (files[i], ptr, cursize) != cursize) + if (read (files[i], ptr, cursize) != cursize) { if (!grub_errno) grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), @@ -152,11 +195,6 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), return grub_errno; } -#define MIN(a, b) \ - ({ typeof (a) _a = (a); \ - typeof (b) _b = (b); \ - _a < _b ? _a : _b; }) - static grub_err_t grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) From 3dc9c3382001676e266c1cdca524504713f5e641 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 13 Sep 2018 14:42:34 -0400 Subject: [PATCH 102/367] x86-efi: Re-arrange grub_cmd_linux() a little bit. This just helps the next patch be easier to read. Signed-off-by: Peter Jones --- grub-core/loader/i386/efi/linux.c | 75 +++++++++++++++++-------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 2f0336809e..5f48fa5561 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -243,32 +243,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } - params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS, - BYTES_TO_PAGES(sizeof(*params))); - if (!params) - params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS, - BYTES_TO_PAGES(sizeof(*params))); - if (! params) - { - grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters"); - goto fail; - } - - grub_dprintf ("linux", "params = %p\n", params); - - grub_memset (params, 0, sizeof(*params)); + lh = (struct linux_i386_kernel_header *)kernel; + grub_dprintf ("linux", "original lh is at %p\n", kernel); - setup_header_end_offset = *((grub_uint8_t *)kernel + 0x201); - grub_dprintf ("linux", "copying %lu bytes from %p to %p\n", - MIN((grub_size_t)0x202+setup_header_end_offset, - sizeof (*params)) - 0x1f1, - (grub_uint8_t *)kernel + 0x1f1, - (grub_uint8_t *)params + 0x1f1); - grub_memcpy ((grub_uint8_t *)params + 0x1f1, - (grub_uint8_t *)kernel + 0x1f1, - MIN((grub_size_t)0x202+setup_header_end_offset,sizeof (*params)) - 0x1f1); - lh = (struct linux_i386_kernel_header *)params; - grub_dprintf ("linux", "lh is at %p\n", lh); grub_dprintf ("linux", "checking lh->boot_flag\n"); if (lh->boot_flag != grub_cpu_to_le16 (0xaa55)) { @@ -316,6 +293,34 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } #endif + params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS, + BYTES_TO_PAGES(sizeof(*params))); + if (!params) + params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS, + BYTES_TO_PAGES(sizeof(*params))); + if (! params) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters"); + goto fail; + } + + grub_dprintf ("linux", "params = %p\n", params); + + grub_memset (params, 0, sizeof(*params)); + + setup_header_end_offset = *((grub_uint8_t *)kernel + 0x201); + grub_dprintf ("linux", "copying %lu bytes from %p to %p\n", + MIN((grub_size_t)0x202+setup_header_end_offset, + sizeof (*params)) - 0x1f1, + (grub_uint8_t *)kernel + 0x1f1, + (grub_uint8_t *)params + 0x1f1); + grub_memcpy ((grub_uint8_t *)params + 0x1f1, + (grub_uint8_t *)kernel + 0x1f1, + MIN((grub_size_t)0x202+setup_header_end_offset,sizeof (*params)) - 0x1f1); + + lh = (struct linux_i386_kernel_header *)params; + grub_dprintf ("linux", "new lh is at %p\n", lh); + grub_dprintf ("linux", "setting up cmdline\n"); linux_cmdline = grub_efi_allocate_pages_max(GRUB_EFI_MAX_ALLOCATION_ADDRESS, BYTES_TO_PAGES(lh->cmdline_size + 1)); @@ -341,8 +346,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_dprintf ("linux", "setting lh->cmd_line_ptr\n"); lh->cmd_line_ptr = (grub_uint32_t)(grub_addr_t)linux_cmdline; - grub_dprintf ("linux", "computing handover offset\n"); handover_offset = lh->handover_offset; + grub_dprintf("linux", "handover_offset: %08x\n", handover_offset); start = (lh->setup_sects + 1) * 512; @@ -359,26 +364,28 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate kernel")); goto fail; } - - grub_dprintf ("linux", "kernel_mem = %lx\n", (unsigned long) kernel_mem); + grub_dprintf("linux", "kernel_mem = %p\n", kernel_mem); grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0); - loaded=1; + + loaded = 1; + grub_dprintf ("linux", "setting lh->code32_start to %p\n", kernel_mem); lh->code32_start = (grub_uint32_t)(grub_addr_t) kernel_mem; grub_memcpy (kernel_mem, (char *)kernel + start, filelen - start); - grub_dprintf ("linux", "setting lh->type_of_loader\n"); lh->type_of_loader = 0x6; + grub_dprintf ("linux", "setting lh->type_of_loader = 0x%02x\n", + lh->type_of_loader); - grub_dprintf ("linux", "setting lh->ext_loader_{type,ver}\n"); params->ext_loader_type = 0; params->ext_loader_ver = 2; - grub_dprintf("linux", "kernel_mem: %p handover_offset: %08x\n", - kernel_mem, handover_offset); + grub_dprintf ("linux", + "setting lh->ext_loader_{type,ver} = {0x%02x,0x%02x}\n", + params->ext_loader_type, params->ext_loader_ver); - fail: +fail: if (file) grub_file_close (file); From 116a666ccc5a9023df42bfb6a0c7ada1dbb3f902 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 12 Sep 2018 16:03:55 -0400 Subject: [PATCH 103/367] x86-efi: Make our own allocator for kernel stuff This helps enable allocations above 4GB. Signed-off-by: Peter Jones --- grub-core/loader/i386/efi/linux.c | 167 +++++++++++++++++------------- 1 file changed, 94 insertions(+), 73 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 5f48fa5561..3e4f7ef39f 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -47,6 +47,65 @@ static char *linux_cmdline; #define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12) +struct allocation_choice { + grub_efi_physical_address_t addr; + grub_efi_allocate_type_t alloc_type; +}; + +static struct allocation_choice max_addresses[] = + { + { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, + { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, + { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, + { 0, 0 } + }; + +static inline void +kernel_free(void *addr, grub_efi_uintn_t size) +{ + if (addr && size) + grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)addr, + BYTES_TO_PAGES(size)); +} + +static void * +kernel_alloc(grub_efi_uintn_t size, const char * const errmsg) +{ + void *addr = 0; + unsigned int i; + grub_efi_physical_address_t prev_max = 0; + + for (i = 0; max_addresses[i].addr != 0 && addr == 0; i++) + { + grub_uint64_t max = max_addresses[i].addr; + grub_efi_uintn_t pages; + + if (max == prev_max) + continue; + + pages = BYTES_TO_PAGES(size); + grub_dprintf ("linux", "Trying to allocate %lu pages from %p\n", + pages, (void *)max); + + prev_max = max; + addr = grub_efi_allocate_pages_real (max, pages, + max_addresses[i].alloc_type, + GRUB_EFI_LOADER_DATA); + if (addr) + grub_dprintf ("linux", "Allocated at %p\n", addr); + } + + while (grub_error_pop ()) + { + ; + } + + if (addr == NULL) + grub_error (GRUB_ERR_OUT_OF_MEMORY, "%s", errmsg); + + return addr; +} + static grub_err_t grub_linuxefi_boot (void) { @@ -62,19 +121,12 @@ grub_linuxefi_unload (void) { grub_dl_unref (my_mod); loaded = 0; - if (initrd_mem) - grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)initrd_mem, - BYTES_TO_PAGES(params->ramdisk_size)); - if (linux_cmdline) - grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t) - linux_cmdline, - BYTES_TO_PAGES(params->cmdline_size + 1)); - if (kernel_mem) - grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)kernel_mem, - BYTES_TO_PAGES(kernel_size)); - if (params) - grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)params, - BYTES_TO_PAGES(16384)); + + kernel_free(initrd_mem, params->ramdisk_size); + kernel_free(linux_cmdline, params->cmdline_size + 1); + kernel_free(kernel_mem, kernel_size); + kernel_free(params, sizeof(*params)); + return GRUB_ERR_NONE; } @@ -150,19 +202,13 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), size += ALIGN_UP (grub_file_size (files[i]), 4); } - initrd_mem = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS, BYTES_TO_PAGES(size)); - if (!initrd_mem) - initrd_mem = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS, BYTES_TO_PAGES(size)); - if (!initrd_mem) - { - grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate initrd")); - goto fail; - } - - grub_dprintf ("linux", "initrd_mem = %lx\n", (unsigned long) initrd_mem); + initrd_mem = kernel_alloc(size, N_("can't allocate initrd")); + if (initrd_mem == NULL) + goto fail; + grub_dprintf ("linux", "initrd_mem = %p\n", initrd_mem); params->ramdisk_size = size; - params->ramdisk_image = (grub_uint32_t)(grub_addr_t) initrd_mem; + params->ramdisk_image = initrd_mem; ptr = initrd_mem; @@ -221,7 +267,6 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), filelen = grub_file_size (file); kernel = grub_malloc(filelen); - if (!kernel) { grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer")); @@ -274,7 +319,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } -#if defined(__x86_64__) || defined(__aarch64__) +#if defined(__x86_64__) grub_dprintf ("linux", "checking lh->xloadflags\n"); if (!(lh->xloadflags & LINUX_XLF_KERNEL_64)) { @@ -293,17 +338,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } #endif - params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_ALLOCATION_ADDRESS, - BYTES_TO_PAGES(sizeof(*params))); + params = kernel_alloc (sizeof(*params), "cannot allocate kernel parameters"); if (!params) - params = grub_efi_allocate_pages_max (GRUB_EFI_MAX_USABLE_ADDRESS, - BYTES_TO_PAGES(sizeof(*params))); - if (! params) - { - grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters"); - goto fail; - } - + goto fail; grub_dprintf ("linux", "params = %p\n", params); grub_memset (params, 0, sizeof(*params)); @@ -322,19 +359,10 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_dprintf ("linux", "new lh is at %p\n", lh); grub_dprintf ("linux", "setting up cmdline\n"); - linux_cmdline = grub_efi_allocate_pages_max(GRUB_EFI_MAX_ALLOCATION_ADDRESS, - BYTES_TO_PAGES(lh->cmdline_size + 1)); + linux_cmdline = kernel_alloc (lh->cmdline_size + 1, N_("can't allocate cmdline")); if (!linux_cmdline) - linux_cmdline = grub_efi_allocate_pages_max(GRUB_EFI_MAX_USABLE_ADDRESS, - BYTES_TO_PAGES(lh->cmdline_size + 1)); - if (!linux_cmdline) - { - grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate cmdline")); - goto fail; - } - - grub_dprintf ("linux", "linux_cmdline = %lx\n", - (unsigned long)linux_cmdline); + goto fail; + grub_dprintf ("linux", "linux_cmdline = %p\n", linux_cmdline); grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE)); grub_create_loader_cmdline (argc, argv, @@ -343,27 +371,24 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), GRUB_VERIFY_KERNEL_CMDLINE); grub_dprintf ("linux", "cmdline:%s\n", linux_cmdline); - grub_dprintf ("linux", "setting lh->cmd_line_ptr\n"); - lh->cmd_line_ptr = (grub_uint32_t)(grub_addr_t)linux_cmdline; + grub_dprintf ("linux", "setting lh->cmd_line_ptr to 0x%08x\n", + linux_cmdline); + lh->cmd_line_ptr = linux_cmdline; handover_offset = lh->handover_offset; - grub_dprintf("linux", "handover_offset: %08x\n", handover_offset); + grub_dprintf("linux", "handover_offset: 0x%08x\n", handover_offset); start = (lh->setup_sects + 1) * 512; - kernel_mem = grub_efi_allocate_pages_max(lh->pref_address, - BYTES_TO_PAGES(lh->init_size)); - if (!kernel_mem) - kernel_mem = grub_efi_allocate_pages_max(GRUB_EFI_MAX_ALLOCATION_ADDRESS, - BYTES_TO_PAGES(lh->init_size)); - if (!kernel_mem) - kernel_mem = grub_efi_allocate_pages_max(GRUB_EFI_MAX_USABLE_ADDRESS, - BYTES_TO_PAGES(lh->init_size)); - if (!kernel_mem) + grub_dprintf ("linux", "lh->pref_address: %p\n", (void *)(grub_addr_t)lh->pref_address); + if (lh->pref_address < (grub_uint64_t)GRUB_EFI_MAX_ALLOCATION_ADDRESS) { - grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate kernel")); - goto fail; + max_addresses[0].addr = lh->pref_address; + max_addresses[0].alloc_type = GRUB_EFI_ALLOCATE_ADDRESS; } + kernel_mem = kernel_alloc (lh->init_size, N_("can't allocate kernel")); + if (!kernel_mem) + goto fail; grub_dprintf("linux", "kernel_mem = %p\n", kernel_mem); grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0); @@ -398,18 +423,14 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), loaded = 0; } - if (linux_cmdline && lh && !loaded) - grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t) - linux_cmdline, - BYTES_TO_PAGES(lh->cmdline_size + 1)); - - if (kernel_mem && !loaded) - grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)kernel_mem, - BYTES_TO_PAGES(kernel_size)); + if (!loaded) + { + if (lh) + kernel_free (linux_cmdline, lh->cmdline_size + 1); - if (params && !loaded) - grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)params, - BYTES_TO_PAGES(16384)); + kernel_free (kernel_mem, kernel_size); + kernel_free (params, sizeof(*params)); + } return grub_errno; } From c463059d5820b409987db0a7301a805be7df27a3 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 12 Sep 2018 16:12:27 -0400 Subject: [PATCH 104/367] x86-efi: Allow initrd+params+cmdline allocations above 4GB. This enables everything except the kernel itself to be above 4GB. Putting the kernel up there still doesn't work, because of the way params->code32_start is used. Signed-off-by: Peter Jones --- grub-core/loader/i386/efi/linux.c | 67 +++++++++++++++++++++++++++---- include/grub/i386/linux.h | 6 ++- 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 3e4f7ef39f..6bc18d5aef 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -52,13 +52,22 @@ struct allocation_choice { grub_efi_allocate_type_t alloc_type; }; -static struct allocation_choice max_addresses[] = +static struct allocation_choice max_addresses[4] = { + /* the kernel overrides this one with pref_address and + * GRUB_EFI_ALLOCATE_ADDRESS */ { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, + /* this one is always below 4GB, which we still *prefer* even if the flag + * is set. */ { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, + /* If the flag in params is set, this one gets changed to be above 4GB. */ { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, { 0, 0 } }; +static struct allocation_choice saved_addresses[4]; + +#define save_addresses() grub_memcpy(saved_addresses, max_addresses, sizeof(max_addresses)) +#define restore_addresses() grub_memcpy(max_addresses, saved_addresses, sizeof(max_addresses)) static inline void kernel_free(void *addr, grub_efi_uintn_t size) @@ -80,6 +89,11 @@ kernel_alloc(grub_efi_uintn_t size, const char * const errmsg) grub_uint64_t max = max_addresses[i].addr; grub_efi_uintn_t pages; + /* + * When we're *not* loading the kernel, or >4GB allocations aren't + * supported, these entries are basically all the same, so don't re-try + * the same parameters. + */ if (max == prev_max) continue; @@ -168,6 +182,9 @@ read(grub_file_t file, grub_uint8_t *bufp, grub_size_t len) return bufpos; } +#define LOW_U32(val) ((grub_uint32_t)(((grub_addr_t)(val)) & 0xffffffffull)) +#define HIGH_U32(val) ((grub_uint32_t)(((grub_addr_t)(val) >> 32) & 0xffffffffull)) + static grub_err_t grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) @@ -207,8 +224,12 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), goto fail; grub_dprintf ("linux", "initrd_mem = %p\n", initrd_mem); - params->ramdisk_size = size; - params->ramdisk_image = initrd_mem; + params->ramdisk_size = LOW_U32(size); + params->ramdisk_image = LOW_U32(initrd_mem); +#if defined(__x86_64__) + params->ext_ramdisk_size = HIGH_U32(size); + params->ext_ramdisk_image = HIGH_U32(initrd_mem); +#endif ptr = initrd_mem; @@ -338,6 +359,18 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } #endif +#if defined(__x86_64__) + if (lh->xloadflags & LINUX_XLF_CAN_BE_LOADED_ABOVE_4G) + { + grub_dprintf ("linux", "Loading kernel above 4GB is supported; enabling.\n"); + max_addresses[2].addr = GRUB_EFI_MAX_USABLE_ADDRESS; + } + else + { + grub_dprintf ("linux", "Loading kernel above 4GB is not supported\n"); + } +#endif + params = kernel_alloc (sizeof(*params), "cannot allocate kernel parameters"); if (!params) goto fail; @@ -372,21 +405,40 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_dprintf ("linux", "cmdline:%s\n", linux_cmdline); grub_dprintf ("linux", "setting lh->cmd_line_ptr to 0x%08x\n", - linux_cmdline); - lh->cmd_line_ptr = linux_cmdline; + LOW_U32(linux_cmdline)); + lh->cmd_line_ptr = LOW_U32(linux_cmdline); +#if defined(__x86_64__) + if ((grub_efi_uintn_t)linux_cmdline > 0xffffffffull) + { + grub_dprintf ("linux", "setting params->ext_cmd_line_ptr to 0x%08x\n", + HIGH_U32(linux_cmdline)); + params->ext_cmd_line_ptr = HIGH_U32(linux_cmdline); + } +#endif handover_offset = lh->handover_offset; grub_dprintf("linux", "handover_offset: 0x%08x\n", handover_offset); start = (lh->setup_sects + 1) * 512; + /* + * AFAICS >4GB for kernel *cannot* work because of params->code32_start being + * 32-bit and getting called unconditionally in head_64.S from either entry + * point. + * + * so nerf that out here... + */ + save_addresses(); grub_dprintf ("linux", "lh->pref_address: %p\n", (void *)(grub_addr_t)lh->pref_address); if (lh->pref_address < (grub_uint64_t)GRUB_EFI_MAX_ALLOCATION_ADDRESS) { max_addresses[0].addr = lh->pref_address; max_addresses[0].alloc_type = GRUB_EFI_ALLOCATE_ADDRESS; } + max_addresses[1].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; + max_addresses[2].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; kernel_mem = kernel_alloc (lh->init_size, N_("can't allocate kernel")); + restore_addresses(); if (!kernel_mem) goto fail; grub_dprintf("linux", "kernel_mem = %p\n", kernel_mem); @@ -395,8 +447,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), loaded = 1; - grub_dprintf ("linux", "setting lh->code32_start to %p\n", kernel_mem); - lh->code32_start = (grub_uint32_t)(grub_addr_t) kernel_mem; + grub_dprintf ("linux", "setting lh->code32_start to 0x%08x\n", + LOW_U32(kernel_mem)); + lh->code32_start = LOW_U32(kernel_mem); grub_memcpy (kernel_mem, (char *)kernel + start, filelen - start); diff --git a/include/grub/i386/linux.h b/include/grub/i386/linux.h index 25ef52c04e..fac22476cc 100644 --- a/include/grub/i386/linux.h +++ b/include/grub/i386/linux.h @@ -236,7 +236,11 @@ struct linux_kernel_params grub_uint32_t ofw_cif_handler; /* b8 */ grub_uint32_t ofw_idt; /* bc */ - grub_uint8_t padding7[0x1b8 - 0xc0]; + grub_uint32_t ext_ramdisk_image; /* 0xc0 */ + grub_uint32_t ext_ramdisk_size; /* 0xc4 */ + grub_uint32_t ext_cmd_line_ptr; /* 0xc8 */ + + grub_uint8_t padding7[0x1b8 - 0xcc]; union { From 89b8cc2da662e85fa37b327d3e46a9418c2ee8df Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 28 Sep 2018 15:42:19 -0400 Subject: [PATCH 105/367] Fix getroot.c's trampolines. This makes the stack executable on most of the grub utilities, which is bad, and rpmdiff complains about it. Signed-off-by: Peter Jones --- grub-core/osdep/linux/getroot.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c index 9f730b3518..f0c503f43d 100644 --- a/grub-core/osdep/linux/getroot.c +++ b/grub-core/osdep/linux/getroot.c @@ -1264,22 +1264,20 @@ grub_util_get_grub_dev_os (const char *os_dev) return grub_dev; } +static void *mp = NULL; +static void +btrfs_mount_path_hook(const char *m) +{ + mp = strdup (m); +} char * grub_util_get_btrfs_subvol (const char *path, char **mount_path) { - char *mp = NULL; - if (mount_path) *mount_path = NULL; - auto void - mount_path_hook (const char *m) - { - mp = strdup (m); - } - - grub_find_root_btrfs_mount_path_hook = mount_path_hook; + grub_find_root_btrfs_mount_path_hook = btrfs_mount_path_hook; grub_free (grub_find_root_devices_from_mountinfo (path, NULL)); grub_find_root_btrfs_mount_path_hook = NULL; From 06bc83a716bb97e3738cd7e0fbfd9a5e1df353b1 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 12 Jul 2019 10:06:50 +0200 Subject: [PATCH 106/367] Do not allow stack trampolines, anywhere. Signed-off-by: Peter Jones --- conf/Makefile.common | 2 +- configure.ac | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/conf/Makefile.common b/conf/Makefile.common index 35e14ff017..0647c53b91 100644 --- a/conf/Makefile.common +++ b/conf/Makefile.common @@ -66,7 +66,7 @@ grubconfdir = $(sysconfdir)/grub.d platformdir = $(pkglibdir)/$(target_cpu)-$(platform) starfielddir = $(pkgdatadir)/themes/starfield -CFLAGS_GNULIB = -Wno-undef -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code +CFLAGS_GNULIB = -Wno-undef -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code -Werror=trampolines -fno-trampolines CPPFLAGS_GNULIB = -I$(top_builddir)/grub-core/lib/gnulib -I$(top_srcdir)/grub-core/lib/gnulib CFLAGS_POSIX = -fno-builtin diff --git a/configure.ac b/configure.ac index 490353713a..a02d40a05b 100644 --- a/configure.ac +++ b/configure.ac @@ -1998,6 +1998,9 @@ if test x"$enable_wextra" != xno ; then HOST_CFLAGS="$HOST_CFLAGS -Wextra" fi +TARGET_CFLAGS="$TARGET_CFLAGS -Werror=trampolines -fno-trampolines" +HOST_CFLAGS="$HOST_CFLAGS -Werror=trampolines -fno-trampolines" + TARGET_CPP="$TARGET_CC -E" TARGET_CCAS=$TARGET_CC From 2bbc26154a4ab915f4cdcdb1bdad2ebaf557a2a6 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 4 Oct 2018 14:22:09 -0400 Subject: [PATCH 107/367] Reimplement boot_counter This adds "increment" and "decrement" commands, and uses them to maintain our variables in 01_fallback_counter. It also simplifies the counter logic, so that there are no nested tests that conflict with each other. Apparently, this *really* wasn't tested well enough. Resolves: rhbz#1614637 Signed-off-by: Peter Jones [lorbus: add comments and revert logic changes in 01_fallback_counting] Signed-off-by: Christian Glombek --- Makefile.util.def | 6 ++ grub-core/Makefile.core.def | 5 ++ grub-core/commands/increment.c | 105 ++++++++++++++++++++++++++++ util/grub.d/01_fallback_counting.in | 22 ++++++ 4 files changed, 138 insertions(+) create mode 100644 grub-core/commands/increment.c create mode 100644 util/grub.d/01_fallback_counting.in diff --git a/Makefile.util.def b/Makefile.util.def index d066652e9b..e10fe766d1 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -458,6 +458,12 @@ script = { installdir = grubconf; }; +script = { + name = '01_fallback_counting'; + common = util/grub.d/01_fallback_counting.in; + installdir = grubconf; +}; + script = { name = '01_menu_auto_hide'; common = util/grub.d/01_menu_auto_hide.in; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 4f203533f5..ea4d59f51b 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -398,6 +398,11 @@ kernel = { extra_dist = kern/mips/cache_flush.S; }; +module = { + name = increment; + common = commands/increment.c; +}; + program = { name = grub-emu; mansection = 1; diff --git a/grub-core/commands/increment.c b/grub-core/commands/increment.c new file mode 100644 index 0000000000..79cf137656 --- /dev/null +++ b/grub-core/commands/increment.c @@ -0,0 +1,105 @@ +/* increment.c - Commands to increment and decrement variables. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +typedef enum { + INCREMENT, + DECREMENT, +} operation; + +static grub_err_t +incr_decr(operation op, int argc, char **args) +{ + const char *old; + char *new; + long value; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_ ("no variable specified")); + if (argc > 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_ ("too many arguments")); + + old = grub_env_get (*args); + if (!old) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("No such variable \"%s\""), + *args); + + value = grub_strtol (old, NULL, 0); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + switch (op) + { + case INCREMENT: + value += 1; + break; + case DECREMENT: + value -= 1; + break; + } + + new = grub_xasprintf ("%ld", value); + if (!new) + return grub_errno; + + grub_env_set (*args, new); + grub_free (new); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_incr(struct grub_command *cmd UNUSED, + int argc, char **args) +{ + return incr_decr(INCREMENT, argc, args); +} + +static grub_err_t +grub_cmd_decr(struct grub_command *cmd UNUSED, + int argc, char **args) +{ + return incr_decr(DECREMENT, argc, args); +} + +static grub_command_t cmd_incr, cmd_decr; + +GRUB_MOD_INIT(increment) +{ + cmd_incr = grub_register_command ("increment", grub_cmd_incr, N_("VARIABLE"), + N_("increment VARIABLE")); + cmd_decr = grub_register_command ("decrement", grub_cmd_decr, N_("VARIABLE"), + N_("decrement VARIABLE")); +} + +GRUB_MOD_FINI(increment) +{ + grub_unregister_command (cmd_incr); + grub_unregister_command (cmd_decr); +} diff --git a/util/grub.d/01_fallback_counting.in b/util/grub.d/01_fallback_counting.in new file mode 100644 index 0000000000..be0e770ea8 --- /dev/null +++ b/util/grub.d/01_fallback_counting.in @@ -0,0 +1,22 @@ +#! /bin/sh -e + +# Boot Counting +# The boot_counter env var can be used to count down boot attempts after an +# OSTree upgrade and choose the rollback deployment when 0 is reached. Both +# boot_counter and boot_success need to be (re-)set from userspace. +cat << EOF +insmod increment +# Check if boot_counter exists and boot_success=0 to activate this behaviour. +if [ -n "\${boot_counter}" -a "\${boot_success}" = "0" ]; then + # if countdown has ended, choose to boot rollback deployment (default=1 on + # OSTree-based systems) + if [ "\${boot_counter}" = "0" -o "\${boot_counter}" = "-1" ]; then + set default=1 + set boot_counter=-1 + # otherwise decrement boot_counter + else + decrement boot_counter + fi + save_env boot_counter +fi +EOF From c6b2588bbdb790f3359c8f62b8aebdd17e29747b Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 19 Oct 2018 10:57:52 -0400 Subject: [PATCH 108/367] Fix menu entry selection based on ID and title Currently if grub_strtoul(saved_entry_value, NULL, 0) does not return an error, we assume the value it has produced is a correct index into our menu entry list, and do not try to interpret the value as the "id" or "title" . In cases where "id" or "title" start with a numeral, this makes them impossible to use as selection criteria. This patch splits the search into three phases - matching id, matching title, and only once those have been exhausted, trying to interpret the ID as a numeral. In that case, we also require that the entire string is numeric, not merely a string with leading numeric characters. Resolves: rhbz#1640979 Signed-off-by: Peter Jones [javierm: fix menu entry selection based on title] Signed-off-by: Javier Martinez Canillas --- grub-core/normal/menu.c | 141 ++++++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 70 deletions(-) diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c index d7a222e681..4a02aadb01 100644 --- a/grub-core/normal/menu.c +++ b/grub-core/normal/menu.c @@ -164,12 +164,12 @@ grub_menu_set_timeout (int timeout) } static int -menuentry_eq (const char *id, const char *spec) +menuentry_eq (const char *id, const char *spec, int limit) { const char *ptr1, *ptr2; ptr1 = id; ptr2 = spec; - while (1) + while (limit == -1 || ptr1 - id <= limit) { if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0) return ptr2 - spec; @@ -178,7 +178,11 @@ menuentry_eq (const char *id, const char *spec) if (*ptr2 == '>') ptr2++; if (*ptr1 != *ptr2) - return 0; + { + if (limit > -1 && ptr1 - id == limit && !*ptr1 && grub_isspace(*ptr2)) + return ptr1 -id -1; + return 0; + } if (*ptr1 == 0) return ptr1 - id; ptr1++; @@ -187,6 +191,58 @@ menuentry_eq (const char *id, const char *spec) return 0; } +static int +get_entry_number_helper(grub_menu_t menu, + const char * const val, const char ** const tail) +{ + /* See if the variable matches the title of a menu entry. */ + int entry = -1; + grub_menu_entry_t e; + int i; + + for (i = 0, e = menu->entry_list; e; i++) + { + int l = 0; + while (val[l] && !grub_isspace(val[l])) + l++; + + if (menuentry_eq (e->id, val, l)) + { + if (tail) + *tail = val + l; + return i; + } + e = e->next; + } + + for (i = 0, e = menu->entry_list; e; i++) + { + + if (menuentry_eq (e->title, val, -1)) + { + if (tail) + *tail = NULL; + return i; + } + e = e->next; + } + + if (tail) + *tail = NULL; + + entry = (int) grub_strtoul (val, tail, 0); + if (grub_errno == GRUB_ERR_BAD_NUMBER || + (*tail && **tail && !grub_isspace(**tail))) + { + entry = -1; + if (tail) + *tail = NULL; + grub_errno = GRUB_ERR_NONE; + } + + return entry; +} + /* Get the first entry number from the value of the environment variable NAME, which is a space-separated list of non-negative integers. The entry number which is returned is stripped from the value of NAME. If no entry number @@ -196,7 +252,6 @@ get_and_remove_first_entry_number (grub_menu_t menu, const char *name) { const char *val, *tail; int entry; - int sz = 0; val = grub_env_get (name); if (! val) @@ -204,50 +259,24 @@ get_and_remove_first_entry_number (grub_menu_t menu, const char *name) grub_error_push (); - entry = (int) grub_strtoul (val, &tail, 0); - - if (grub_errno == GRUB_ERR_BAD_NUMBER) - { - /* See if the variable matches the title of a menu entry. */ - grub_menu_entry_t e = menu->entry_list; - int i; - - for (i = 0; e; i++) - { - sz = menuentry_eq (e->title, val); - if (sz < 1) - sz = menuentry_eq (e->id, val); - - if (sz >= 1) - { - entry = i; - break; - } - e = e->next; - } + entry = get_entry_number_helper(menu, val, &tail); + if (!(*tail == 0 || grub_isspace(*tail))) + entry = -1; - if (sz > 0) - grub_errno = GRUB_ERR_NONE; - - if (! e) - entry = -1; - } - - if (grub_errno == GRUB_ERR_NONE) + if (entry >= 0) { - if (sz > 0) - tail += sz; - /* Skip whitespace to find the next entry. */ while (*tail && grub_isspace (*tail)) tail++; - grub_env_set (name, tail); + if (*tail) + grub_env_set (name, tail); + else + grub_env_unset (name); } else { grub_env_unset (name); grub_errno = GRUB_ERR_NONE; - entry = -1; } grub_error_pop (); @@ -524,6 +553,7 @@ static int get_entry_number (grub_menu_t menu, const char *name) { const char *val; + const char *tail; int entry; val = grub_env_get (name); @@ -531,38 +561,9 @@ get_entry_number (grub_menu_t menu, const char *name) return -1; grub_error_push (); - - entry = (int) grub_strtoul (val, 0, 0); - - if (grub_errno == GRUB_ERR_BAD_NUMBER) - { - /* See if the variable matches the title of a menu entry. */ - grub_menu_entry_t e = menu->entry_list; - int i; - - grub_errno = GRUB_ERR_NONE; - - for (i = 0; e; i++) - { - if (menuentry_eq (e->title, val) - || menuentry_eq (e->id, val)) - { - entry = i; - break; - } - e = e->next; - } - - if (! e) - entry = -1; - } - - if (grub_errno != GRUB_ERR_NONE) - { - grub_errno = GRUB_ERR_NONE; - entry = -1; - } - + entry = get_entry_number_helper(menu, val, &tail); + if (tail && *tail != '\0') + entry = -1; grub_error_pop (); return entry; From 9430ef48e43b4b288f464221b8568276e2e89014 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 26 Nov 2018 10:06:42 +0100 Subject: [PATCH 109/367] Make the menu entry users option argument to be optional The --users option is used to restrict the access to specific menu entries only to a set of users. But the option requires an argument to either be a constant or a variable that has been set. So for example the following: menuentry "May be run by superusers or users in $users" --users $users { linux /vmlinuz } Would fail if $users is not defined and grub would discard the menu entry. Instead, allow the --users option to have an optional argument and ignore the option if the argument was not set. Related: rhbz#1652434 Signed-off-by: Javier Martinez Canillas --- grub-core/commands/menuentry.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c index b194123eb6..b175a1b43b 100644 --- a/grub-core/commands/menuentry.c +++ b/grub-core/commands/menuentry.c @@ -29,7 +29,7 @@ static const struct grub_arg_option options[] = { {"class", 1, GRUB_ARG_OPTION_REPEATABLE, N_("Menu entry type."), N_("STRING"), ARG_TYPE_STRING}, - {"users", 2, 0, + {"users", 2, GRUB_ARG_OPTION_OPTIONAL, N_("List of users allowed to boot this entry."), N_("USERNAME[,USERNAME]"), ARG_TYPE_STRING}, {"hotkey", 3, 0, @@ -281,7 +281,7 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) if (! ctxt->state[3].set && ! ctxt->script) return grub_error (GRUB_ERR_BAD_ARGUMENT, "no menuentry definition"); - if (ctxt->state[1].set) + if (ctxt->state[1].set && ctxt->state[1].arg) users = ctxt->state[1].arg; else if (ctxt->state[5].set) users = NULL; From 994409deb2c0cb8939daf0e5e7af66093d6e22f5 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 16 Jan 2019 13:21:46 -0500 Subject: [PATCH 110/367] Add efi-export-env and efi-load-env commands This adds "efi-export-env VARIABLE" and "efi-load-env", which manipulate the environment block stored in the EFI variable GRUB_ENV-91376aff-cba6-42be-949d-06fde81128e8. Signed-off-by: Peter Jones --- grub-core/Makefile.core.def | 6 ++ grub-core/commands/efi/env.c | 168 +++++++++++++++++++++++++++++++++++ grub-core/kern/efi/efi.c | 3 + grub-core/kern/efi/init.c | 5 -- grub-core/lib/envblk.c | 43 +++++++++ include/grub/efi/efi.h | 5 ++ include/grub/lib/envblk.h | 3 + util/grub-set-bootflag.c | 1 + 8 files changed, 229 insertions(+), 5 deletions(-) create mode 100644 grub-core/commands/efi/env.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index ea4d59f51b..dc9fea6f44 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -820,6 +820,12 @@ module = { enable = efi; }; +module = { + name = efienv; + common = commands/efi/env.c; + enable = efi; +}; + module = { name = efifwsetup; efi = commands/efi/efifwsetup.c; diff --git a/grub-core/commands/efi/env.c b/grub-core/commands/efi/env.c new file mode 100644 index 0000000000..cbd13e03e8 --- /dev/null +++ b/grub-core/commands/efi/env.c @@ -0,0 +1,168 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2012 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static const grub_efi_guid_t grub_env_guid = GRUB_EFI_GRUB_VARIABLE_GUID; + +static grub_err_t +grub_efi_export_env(grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + const char *value; + char *old_value; + struct grub_envblk envblk_s = { NULL, 0 }; + grub_envblk_t envblk = &envblk_s; + grub_err_t err; + int changed = 1; + grub_efi_status_t status; + + grub_dprintf ("efienv", "argc:%d\n", argc); + for (int i = 0; i < argc; i++) + grub_dprintf ("efienv", "argv[%d]: %s\n", i, argv[i]); + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("variable name expected")); + + grub_efi_get_variable ("GRUB_ENV", &grub_env_guid, &envblk_s.size, + (void **) &envblk_s.buf); + if (!envblk_s.buf || envblk_s.size < 1) + { + char *buf = grub_malloc (1025); + if (!buf) + return grub_errno; + + grub_memcpy (buf, GRUB_ENVBLK_SIGNATURE, sizeof (GRUB_ENVBLK_SIGNATURE) - 1); + grub_memset (buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1, '#', + DEFAULT_ENVBLK_SIZE - sizeof (GRUB_ENVBLK_SIGNATURE) + 1); + buf[1024] = '\0'; + + envblk_s.buf = buf; + envblk_s.size = 1024; + } + else + { + char *buf = grub_realloc (envblk_s.buf, envblk_s.size + 1); + if (!buf) + return grub_errno; + + envblk_s.buf = buf; + envblk_s.buf[envblk_s.size] = '\0'; + } + + err = grub_envblk_get(envblk, argv[0], &old_value); + if (err != GRUB_ERR_NONE) + { + grub_dprintf ("efienv", "grub_envblk_get returned %d\n", err); + return err; + } + + value = grub_env_get(argv[0]); + if ((!value && !old_value) || + (value && old_value && !grub_strcmp(old_value, value))) + changed = 0; + + if (old_value) + grub_free(old_value); + + if (changed == 0) + { + grub_dprintf ("efienv", "No changes necessary\n"); + return 0; + } + + if (value) + { + grub_dprintf ("efienv", "setting \"%s\" to \"%s\"\n", argv[0], value); + grub_envblk_set(envblk, argv[0], value); + } + else + { + grub_dprintf ("efienv", "deleting \"%s\" from envblk\n", argv[0]); + grub_envblk_delete(envblk, argv[0]); + } + + grub_dprintf ("efienv", "envblk is %lu bytes:\n\"%s\"\n", envblk_s.size, envblk_s.buf); + + grub_dprintf ("efienv", "removing GRUB_ENV\n"); + status = grub_efi_set_variable ("GRUB_ENV", &grub_env_guid, NULL, 0); + if (status != GRUB_EFI_SUCCESS) + grub_dprintf ("efienv", "removal returned %ld\n", status); + + grub_dprintf ("efienv", "setting GRUB_ENV\n"); + status = grub_efi_set_variable ("GRUB_ENV", &grub_env_guid, + envblk_s.buf, envblk_s.size); + if (status != GRUB_EFI_SUCCESS) + grub_dprintf ("efienv", "setting GRUB_ENV returned %ld\n", status); + + return 0; +} + +static int +set_var (const char *name, const char *value, + void *whitelist __attribute__((__unused__))) +{ + grub_env_set (name, value); + return 0; +} + +static grub_err_t +grub_efi_load_env(grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[] __attribute__((__unused__))) +{ + struct grub_envblk envblk_s = { NULL, 0 }; + grub_envblk_t envblk = &envblk_s; + + grub_efi_get_variable ("GRUB_ENV", &grub_env_guid, &envblk_s.size, + (void **) &envblk_s.buf); + if (!envblk_s.buf || envblk_s.size < 1) + return 0; + + if (argc > 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unexpected argument")); + + grub_envblk_iterate (envblk, NULL, set_var); + grub_free (envblk_s.buf); +} + +static grub_command_t export_cmd, loadenv_cmd; + +GRUB_MOD_INIT(lsefi) +{ + export_cmd = grub_register_command ("efi-export-env", grub_efi_export_env, + N_("VARIABLE_NAME"), N_("Export environment variable to UEFI.")); + loadenv_cmd = grub_register_command ("efi-load-env", grub_efi_load_env, + NULL, N_("Load the grub environment from UEFI.")); +} + +GRUB_MOD_FINI(lsefi) +{ + grub_unregister_command (export_cmd); + grub_unregister_command (loadenv_cmd); +} diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index 2a446f5031..14bc10eb56 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -225,6 +225,9 @@ grub_efi_set_variable(const char *var, const grub_efi_guid_t *guid, if (status == GRUB_EFI_SUCCESS) return GRUB_ERR_NONE; + if (status == GRUB_EFI_NOT_FOUND && datasize == 0) + return GRUB_ERR_NONE; + return grub_error (GRUB_ERR_IO, "could not set EFI variable `%s'", var); } diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c index 2d12e6188f..0574d8d621 100644 --- a/grub-core/kern/efi/init.c +++ b/grub-core/kern/efi/init.c @@ -85,11 +85,6 @@ stack_protector_init (void) grub_addr_t grub_modbase; -#define GRUB_EFI_GRUB_VARIABLE_GUID \ - { 0x91376aff, 0xcba6, 0x42be, \ - { 0x94, 0x9d, 0x06, 0xfd, 0xe8, 0x11, 0x28, 0xe8 } \ - } - /* Helper for grub_efi_env_init */ static int set_var (const char *name, const char *value, diff --git a/grub-core/lib/envblk.c b/grub-core/lib/envblk.c index 2e4e78b132..874506da16 100644 --- a/grub-core/lib/envblk.c +++ b/grub-core/lib/envblk.c @@ -223,6 +223,49 @@ grub_envblk_delete (grub_envblk_t envblk, const char *name) } } +struct get_var_state { + const char * const name; + char * value; + int found; +}; + +static int +get_var (const char * const name, const char * const value, void *statep) +{ + struct get_var_state *state = (struct get_var_state *)statep; + + if (!grub_strcmp(state->name, name)) + { + state->found = 1; + state->value = grub_strdup(value); + if (!state->value) + grub_errno = grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + + return 1; + } + + return 0; +} + +grub_err_t +grub_envblk_get (grub_envblk_t envblk, const char * const name, char ** const value) +{ + struct get_var_state state = { + .name = name, + .value = NULL, + .found = 0, + }; + + grub_envblk_iterate(envblk, (void *)&state, get_var); + + *value = state.value; + + if (state.found && !state.value) + return grub_errno; + + return GRUB_ERR_NONE; +} + void grub_envblk_iterate (grub_envblk_t envblk, void *hook_data, diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index 2e0691454b..8dfc89a33b 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -24,6 +24,11 @@ #include #include +#define GRUB_EFI_GRUB_VARIABLE_GUID \ + { 0x91376aff, 0xcba6, 0x42be, \ + { 0x94, 0x9d, 0x06, 0xfd, 0xe8, 0x11, 0x28, 0xe8 } \ + } + /* Variables. */ extern grub_efi_system_table_t *EXPORT_VAR(grub_efi_system_table); extern grub_efi_handle_t EXPORT_VAR(grub_efi_image_handle); diff --git a/include/grub/lib/envblk.h b/include/grub/lib/envblk.h index c3e6559217..ab969af246 100644 --- a/include/grub/lib/envblk.h +++ b/include/grub/lib/envblk.h @@ -22,6 +22,8 @@ #define GRUB_ENVBLK_SIGNATURE "# GRUB Environment Block\n" #define GRUB_ENVBLK_DEFCFG "grubenv" +#define DEFAULT_ENVBLK_SIZE 1024 + #ifndef ASM_FILE struct grub_envblk @@ -33,6 +35,7 @@ typedef struct grub_envblk *grub_envblk_t; grub_envblk_t grub_envblk_open (char *buf, grub_size_t size); int grub_envblk_set (grub_envblk_t envblk, const char *name, const char *value); +grub_err_t grub_envblk_get (grub_envblk_t envblk, const char * const name, char ** const value); void grub_envblk_delete (grub_envblk_t envblk, const char *name); void grub_envblk_iterate (grub_envblk_t envblk, void *hook_data, diff --git a/util/grub-set-bootflag.c b/util/grub-set-bootflag.c index d506f7e75b..a6ccc11383 100644 --- a/util/grub-set-bootflag.c +++ b/util/grub-set-bootflag.c @@ -25,6 +25,7 @@ #include /* For *_DIR_NAME defines */ #include +#include #include /* For GRUB_ENVBLK_DEFCFG define */ #include #include From d6f99352fe11badbf412101dd5694246848d5d51 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 17 Jan 2019 13:10:39 -0500 Subject: [PATCH 111/367] Make it possible to subtract conditions from debug= This makes it so you can do set debug to "all,-scripting,-lexer" and get the obvious outcome. Any negation present will take preference over that conditional, so "all,-scripting,scripting" is the same thing as "all,-scripting". Signed-off-by: Peter Jones --- grub-core/kern/misc.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index 9a2fae6398..578bf51a5f 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -164,12 +164,24 @@ int grub_debug_enabled (const char * condition) { const char *debug; + char *negcond; + int negated = 0; debug = grub_env_get ("debug"); if (!debug) return 0; - if (grub_strword (debug, "all") || grub_strword (debug, condition)) + negcond = grub_zalloc (grub_strlen (condition) + 2); + if (negcond) + { + grub_strcpy (negcond, "-"); + grub_strcpy (negcond+1, condition); + negated = grub_strword (debug, negcond); + grub_free (negcond); + } + + if (!negated && + (grub_strword (debug, "all") || grub_strword (debug, condition))) return 1; return 0; From 664f245a63936366b2cb63c79d7750a5a92d6127 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 22 Jan 2019 15:40:25 +0100 Subject: [PATCH 112/367] Export all variables from the initial context when creating a submenu When a submenu is created, only the exported variables are copied to the new menu context. But we want the variables to be global, so export lets export all variables to the new created submenu. Also, don't unset the default variable when a new submenu is created. Signed-off-by: Javier Martinez Canillas --- grub-core/normal/context.c | 2 +- grub-core/normal/menu.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/grub-core/normal/context.c b/grub-core/normal/context.c index ee53d4a68e..87edd254c4 100644 --- a/grub-core/normal/context.c +++ b/grub-core/normal/context.c @@ -99,7 +99,7 @@ grub_env_new_context (int export_all) grub_err_t grub_env_context_open (void) { - return grub_env_new_context (0); + return grub_env_new_context (1); } int grub_extractor_level = 0; diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c index 4a02aadb01..fe2e77a43e 100644 --- a/grub-core/normal/menu.c +++ b/grub-core/normal/menu.c @@ -375,8 +375,6 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot) if (ptr && ptr[0] && ptr[1]) grub_env_set ("default", ptr + 1); - else - grub_env_unset ("default"); grub_script_execute_new_scope (entry->sourcecode, entry->argc, entry->args); From bc34953a13c8c582d4aecf103ee87944dd0b9a4d Mon Sep 17 00:00:00 2001 From: Christian Glombek Date: Tue, 2 Apr 2019 16:22:21 +0200 Subject: [PATCH 113/367] grub.d: Split out boot success reset from menu auto hide script Also rename fallback and menu auto hide script to be executed before and after boot success reset script. In menu auto hide script, rename last_boot_ok var to menu_hide_ok Signed-off-by: Christian Glombek Signed-off-by: Robbie Harwood --- Makefile.util.def | 14 ++++++++--- ...ck_counting.in => 08_fallback_counting.in} | 14 ++++++----- util/grub.d/10_reset_boot_success.in | 25 +++++++++++++++++++ ...menu_auto_hide.in => 12_menu_auto_hide.in} | 23 ++++------------- 4 files changed, 48 insertions(+), 28 deletions(-) rename util/grub.d/{01_fallback_counting.in => 08_fallback_counting.in} (65%) create mode 100644 util/grub.d/10_reset_boot_success.in rename util/grub.d/{01_menu_auto_hide.in => 12_menu_auto_hide.in} (58%) diff --git a/Makefile.util.def b/Makefile.util.def index e10fe766d1..b4ce5383b7 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -459,14 +459,14 @@ script = { }; script = { - name = '01_fallback_counting'; - common = util/grub.d/01_fallback_counting.in; + name = '08_fallback_counting'; + common = util/grub.d/08_fallback_counting.in; installdir = grubconf; }; script = { - name = '01_menu_auto_hide'; - common = util/grub.d/01_menu_auto_hide.in; + name = '12_menu_auto_hide'; + common = util/grub.d/12_menu_auto_hide.in; installdir = grubconf; }; @@ -518,6 +518,12 @@ script = { condition = COND_HOST_LINUX; }; +script = { + name = '10_reset_boot_success'; + common = util/grub.d/10_reset_boot_success.in; + installdir = grubconf; +}; + script = { name = '10_xnu'; common = util/grub.d/10_xnu.in; diff --git a/util/grub.d/01_fallback_counting.in b/util/grub.d/08_fallback_counting.in similarity index 65% rename from util/grub.d/01_fallback_counting.in rename to util/grub.d/08_fallback_counting.in index be0e770ea8..2e2c3ff7d3 100644 --- a/util/grub.d/01_fallback_counting.in +++ b/util/grub.d/08_fallback_counting.in @@ -1,15 +1,17 @@ #! /bin/sh -e - -# Boot Counting +# Fallback Countdown +# +# This snippet depends on 10_reset_boot_success and needs to be kept in sync. +# # The boot_counter env var can be used to count down boot attempts after an -# OSTree upgrade and choose the rollback deployment when 0 is reached. Both -# boot_counter and boot_success need to be (re-)set from userspace. +# OSTree upgrade and choose the rollback deployment when 0 is reached. +# Both boot_counter=X and boot_success=1 need to be set from userspace. cat << EOF insmod increment # Check if boot_counter exists and boot_success=0 to activate this behaviour. if [ -n "\${boot_counter}" -a "\${boot_success}" = "0" ]; then - # if countdown has ended, choose to boot rollback deployment (default=1 on - # OSTree-based systems) + # if countdown has ended, choose to boot rollback deployment, + # i.e. default=1 on OSTree-based systems. if [ "\${boot_counter}" = "0" -o "\${boot_counter}" = "-1" ]; then set default=1 set boot_counter=-1 diff --git a/util/grub.d/10_reset_boot_success.in b/util/grub.d/10_reset_boot_success.in new file mode 100644 index 0000000000..6c88d933dd --- /dev/null +++ b/util/grub.d/10_reset_boot_success.in @@ -0,0 +1,25 @@ +#! /bin/sh -e +# Reset Boot Success +# +# The 08_fallback_counting and 12_menu_auto_hide snippets rely on this one +# and need to be kept in sync. +# +# The boot_success var needs to be set to 1 from userspace to mark a boot successful. +cat << EOF +insmod increment +# Hiding the menu is ok if last boot was ok or if this is a first boot attempt to boot the entry +if [ "\${boot_success}" = "1" -o "\${boot_indeterminate}" = "1" ]; then + set menu_hide_ok=1 +else + set menu_hide_ok=0 +fi +# Reset boot_indeterminate after a successful boot, increment otherwise +if [ "\${boot_success}" = "1" ] ; then + set boot_indeterminate=0 +else + increment boot_indeterminate +fi +# Reset boot_success for current boot +set boot_success=0 +save_env boot_success boot_indeterminate +EOF diff --git a/util/grub.d/01_menu_auto_hide.in b/util/grub.d/12_menu_auto_hide.in similarity index 58% rename from util/grub.d/01_menu_auto_hide.in rename to util/grub.d/12_menu_auto_hide.in index ad175870a5..6a7c0fa0d4 100644 --- a/util/grub.d/01_menu_auto_hide.in +++ b/util/grub.d/12_menu_auto_hide.in @@ -1,5 +1,8 @@ #! /bin/sh - +# Menu Auto Hide +# +# This snippet depends on 10_reset_boot_success and needs to be kept in sync. +# # Disable / skip generating menu-auto-hide config parts on serial terminals for x in ${GRUB_TERMINAL_INPUT} ${GRUB_TERMINAL_OUTPUT}; do case "$x" in @@ -10,29 +13,13 @@ for x in ${GRUB_TERMINAL_INPUT} ${GRUB_TERMINAL_OUTPUT}; do done cat << EOF -if [ "\${boot_success}" = "1" -o "\${boot_indeterminate}" = "1" ]; then - set last_boot_ok=1 -else - set last_boot_ok=0 -fi - -# Reset boot_indeterminate after a successful boot -if [ "\${boot_success}" = "1" ] ; then - set boot_indeterminate=0 -# Avoid boot_indeterminate causing the menu to be hidden more then once -elif [ "\${boot_indeterminate}" = "1" ]; then - set boot_indeterminate=2 -fi -set boot_success=0 -save_env boot_success boot_indeterminate - if [ x\$feature_timeout_style = xy ] ; then if [ "\${menu_show_once}" ]; then unset menu_show_once save_env menu_show_once set timeout_style=menu set timeout=60 - elif [ "\${menu_auto_hide}" -a "\${last_boot_ok}" = "1" ]; then + elif [ "\${menu_auto_hide}" -a "\${menu_hide_ok}" = "1" ]; then set orig_timeout_style=\${timeout_style} set orig_timeout=\${timeout} if [ "\${fastboot}" = "1" ]; then From 011ea950d534fbb1f421326fcb241f09cb0273b4 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 9 Apr 2019 13:12:40 +0200 Subject: [PATCH 114/367] Don't assume that boot commands will only return on fail While it's true that for most loaders the boot command never returns, it may be the case that it does. For example the GRUB emulator boot command calls to systemctl kexec which in turn does an asynchonous call to kexec. So in this case GRUB will wrongly assume that the boot command fails and print a "Failed to boot both default and fallback entries" even when the kexec call later succeeds. Signed-off-by: Javier Martinez Canillas --- grub-core/normal/menu.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c index fe2e77a43e..ec0c92bade 100644 --- a/grub-core/normal/menu.c +++ b/grub-core/normal/menu.c @@ -285,7 +285,7 @@ get_and_remove_first_entry_number (grub_menu_t menu, const char *name) } /* Run a menu entry. */ -static void +static grub_err_t grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot) { grub_err_t err = GRUB_ERR_NONE; @@ -302,7 +302,7 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot) { grub_print_error (); grub_errno = GRUB_ERR_NONE; - return; + return grub_errno; } errs_before = grub_err_printed_errors; @@ -315,7 +315,7 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot) grub_env_context_open (); menu = grub_zalloc (sizeof (*menu)); if (! menu) - return; + return grub_errno; grub_env_set_menu (menu); if (auto_boot) grub_env_set ("timeout", "0"); @@ -385,7 +385,7 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot) if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ()) /* Implicit execution of boot, only if something is loaded. */ - grub_command_execute ("boot", 0, 0); + err = grub_command_execute ("boot", 0, 0); if (errs_before != grub_err_printed_errors) grub_wait_after_message (); @@ -408,6 +408,8 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot) else grub_env_unset ("default"); grub_env_unset ("timeout"); + + return err; } /* Execute ENTRY from the menu MENU, falling back to entries specified @@ -422,10 +424,13 @@ grub_menu_execute_with_fallback (grub_menu_t menu, void *callback_data) { int fallback_entry; + grub_err_t err; callback->notify_booting (entry, callback_data); - grub_menu_execute_entry (entry, 1); + err = grub_menu_execute_entry (entry, 1); + if (err == GRUB_ERR_NONE) + return; /* Deal with fallback entries. */ while ((fallback_entry = get_and_remove_first_entry_number (menu, "fallback")) @@ -436,11 +441,9 @@ grub_menu_execute_with_fallback (grub_menu_t menu, entry = grub_menu_get_entry (menu, fallback_entry); callback->notify_fallback (entry, callback_data); - grub_menu_execute_entry (entry, 1); - /* If the function call to execute the entry returns at all, then this is - taken to indicate a boot failure. For menu entries that do something - other than actually boot an operating system, this could assume - incorrectly that something failed. */ + err = grub_menu_execute_entry (entry, 1); + if (err == GRUB_ERR_NONE) + return; } if (!autobooted) From eeb1259b25005eb9e18e828b30e05ffb41560682 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 13 Nov 2019 12:15:43 +0100 Subject: [PATCH 115/367] grub-set-bootflag: Update comment about running as root through pkexec We have stopped using pkexec for grub-set-bootflag, instead it is now installed suid root, update the comment accordingly. Signed-off-by: Hans de Goede --- util/grub-set-bootflag.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/grub-set-bootflag.c b/util/grub-set-bootflag.c index a6ccc11383..3eb04beb5e 100644 --- a/util/grub-set-bootflag.c +++ b/util/grub-set-bootflag.c @@ -18,7 +18,7 @@ */ /* - * NOTE this gets run by users as root (through pkexec), so this does not + * NOTE this gets run by users as root (its suid root), so this does not * use any grub library / util functions to allow for easy auditing. * The grub headers are only included to get certain defines. */ From cbf85d591485012432f26d5cecb03023f39c041c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 13 Nov 2019 13:02:01 +0100 Subject: [PATCH 116/367] grub-set-bootflag: Write new env to tmpfile and then rename Make the grubenv writing code in grub-set-bootflag more robust by writing the modified grubenv to a tmpfile first and then renaming the tmpfile over the old grubenv (following symlinks). Signed-off-by: Hans de Goede --- util/grub-set-bootflag.c | 87 +++++++++++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 9 deletions(-) diff --git a/util/grub-set-bootflag.c b/util/grub-set-bootflag.c index 3eb04beb5e..3b4c25ca2a 100644 --- a/util/grub-set-bootflag.c +++ b/util/grub-set-bootflag.c @@ -28,7 +28,9 @@ #include #include /* For GRUB_ENVBLK_DEFCFG define */ #include +#include #include +#include #include #include @@ -56,8 +58,10 @@ int main(int argc, char *argv[]) { /* NOTE buf must be at least the longest bootflag length + 4 bytes */ char env[GRUBENV_SIZE + 1], buf[64], *s; + /* +1 for 0 termination, +6 for "XXXXXX" in tmp filename */ + char env_filename[PATH_MAX + 1], tmp_filename[PATH_MAX + 6 + 1]; const char *bootflag; - int i, len, ret; + int i, fd, len, ret; FILE *f; if (argc != 2) @@ -89,7 +93,32 @@ int main(int argc, char *argv[]) bootflag = bootflags[i]; len = strlen (bootflag); - f = fopen (GRUBENV, "r"); + /* + * Really become root. setuid avoids an user killing us, possibly leaking + * the tmpfile. setgid avoids the new grubenv's gid being that of the user. + */ + ret = setuid(0); + if (ret) + { + perror ("Error setuid(0) failed"); + return 1; + } + + ret = setgid(0); + if (ret) + { + perror ("Error setgid(0) failed"); + return 1; + } + + /* Canonicalize GRUBENV filename, resolving symlinks, etc. */ + if (!realpath(GRUBENV, env_filename)) + { + perror ("Error canonicalizing " GRUBENV " filename"); + return 1; + } + + f = fopen (env_filename, "r"); if (!f) { perror ("Error opening " GRUBENV " for reading"); @@ -144,30 +173,70 @@ int main(int argc, char *argv[]) snprintf(buf, sizeof(buf), "%s=1\n", bootflag); memcpy(s, buf, len + 3); - /* "r+", don't truncate so that the diskspace stays reserved */ - f = fopen (GRUBENV, "r+"); + + /* + * Create a tempfile for writing the new env. Use the canonicalized filename + * for the template so that the tmpfile is in the same dir / on same fs. + */ + snprintf(tmp_filename, sizeof(tmp_filename), "%sXXXXXX", env_filename); + fd = mkstemp(tmp_filename); + if (fd == -1) + { + perror ("Creating tmpfile failed"); + return 1; + } + + f = fdopen (fd, "w"); if (!f) { - perror ("Error opening " GRUBENV " for writing"); + perror ("Error fdopen of tmpfile failed"); + unlink(tmp_filename); return 1; } ret = fwrite (env, 1, GRUBENV_SIZE, f); if (ret != GRUBENV_SIZE) { - perror ("Error writing to " GRUBENV); + perror ("Error writing tmpfile"); + unlink(tmp_filename); return 1; } ret = fflush (f); if (ret) { - perror ("Error flushing " GRUBENV); + perror ("Error flushing tmpfile"); + unlink(tmp_filename); return 1; } - fsync (fileno (f)); - fclose (f); + ret = fsync (fileno (f)); + if (ret) + { + perror ("Error syncing tmpfile"); + unlink(tmp_filename); + return 1; + } + + ret = fclose (f); + if (ret) + { + perror ("Error closing tmpfile"); + unlink(tmp_filename); + return 1; + } + + /* + * And finally rename the tmpfile with the new env over the old env, the + * linux kernel guarantees that this is atomic (from a syscall pov). + */ + ret = rename(tmp_filename, env_filename); + if (ret) + { + perror ("Error renaming tmpfile to " GRUBENV " failed"); + unlink(tmp_filename); + return 1; + } return 0; } From f6e74af0ee1e4cef492d8a504a925ad35e3100c0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 Nov 2019 09:51:41 +0100 Subject: [PATCH 117/367] grub.d: Fix boot_indeterminate getting set on boot_success=0 boot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "grub.d: Split out boot success reset from menu auto hide script" not only moved the code to clear boot_success and boot_indeterminate but for some reason also mixed in some broken changes to the boot_indeterminate handling. The boot_indeterminate var is meant to suppress the boot menu after a reboot from either a selinux-relabel or offline-updates. These 2 special boot scenarios do not set boot_success since there is no successfull interaction with the user. Instead they increment boot_indeterminate, and if it is 1 and only when it is 1, so the first reboot after a "special" boot we suppress the menu. To ensure that we do show the menu if we somehow get stuck in a "special" boot loop where we do special-boots without them incrementing boot_indeterminate, the code before the "grub.d: Split out boot success reset from menu auto hide script" commit would increment boot_indeterminate once when it is 1, so that even if the "special" boot reboot-loop immediately we would show the menu on the next boot. That commit broke this however, because it not only moves the code, it also changes it from only "incrementing" boot_indeterminate once to always incrementing it, except when boot_success == 1 (and we reset it). This broken behavior causes the following problem: 1. Boot a broken kernel, system hangs, power-cycle 2. boot_success now != 1, so we increment boot_indeterminate from 0 (unset!) to 1. User either simply tries again, or makes some changes but the end-result still is a system hang, power-cycle 3. Now boot_indeterminate==1 so we do not show the menu even though the previous boot failed -> BAD This commit fixes this by restoring the behavior of setting boot_indeterminate to 2 when it was 1 before. Fixes: "grub.d: Split out boot success reset from menu auto hide script" Signed-off-by: Hans de Goede [jpokorny: 01_menu_auto_hide.in: fix a then/than typo] Signed-off-by: Jan Pokorný Signed-off-by: Robbie Harwood --- util/grub.d/10_reset_boot_success.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/util/grub.d/10_reset_boot_success.in b/util/grub.d/10_reset_boot_success.in index 6c88d933dd..e73f4137b3 100644 --- a/util/grub.d/10_reset_boot_success.in +++ b/util/grub.d/10_reset_boot_success.in @@ -6,18 +6,18 @@ # # The boot_success var needs to be set to 1 from userspace to mark a boot successful. cat << EOF -insmod increment # Hiding the menu is ok if last boot was ok or if this is a first boot attempt to boot the entry if [ "\${boot_success}" = "1" -o "\${boot_indeterminate}" = "1" ]; then set menu_hide_ok=1 else set menu_hide_ok=0 fi -# Reset boot_indeterminate after a successful boot, increment otherwise +# Reset boot_indeterminate after a successful boot if [ "\${boot_success}" = "1" ] ; then set boot_indeterminate=0 -else - increment boot_indeterminate +# Avoid boot_indeterminate causing the menu to be hidden more than once +elif [ "\${boot_indeterminate}" = "1" ]; then + set boot_indeterminate=2 fi # Reset boot_success for current boot set boot_success=0 From d673d42bee71c8d181a2ac251d94abb05c5fab27 Mon Sep 17 00:00:00 2001 From: David Abdurachmanov Date: Sat, 9 Nov 2019 19:51:57 +0000 Subject: [PATCH 118/367] Add start symbol for RISC-V All other architectures have start symbol. Hopefully this resolves: BUILDSTDERR: ././grub-mkimage: error: undefined symbol start. Signed-off-by: David Abdurachmanov --- grub-core/kern/riscv/efi/startup.S | 1 + 1 file changed, 1 insertion(+) diff --git a/grub-core/kern/riscv/efi/startup.S b/grub-core/kern/riscv/efi/startup.S index f2a7b2b1ed..781773136e 100644 --- a/grub-core/kern/riscv/efi/startup.S +++ b/grub-core/kern/riscv/efi/startup.S @@ -29,6 +29,7 @@ .file "startup.S" .text +FUNCTION(start) FUNCTION(_start) /* * EFI_SYSTEM_TABLE and EFI_HANDLE are passed in a1/a0. From 2ab6c45c00f068ad1f6f32c7b1d62f9ae58b48e4 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 15 Jan 2020 12:47:46 +0100 Subject: [PATCH 119/367] bootstrap.conf: Force autogen.sh to use python3 The python-unversioned-command package is not installed in the buildroot, but the bootstrap script expects the python command to be present if one is not defined. So building the package leads to the following error: ./autogen.sh: line 20: python: command not found This is harmless since gnulib is included as a source anyways, because the builders can't download. But still the issue should be fixed by forcing to use python3 that's the default in Fedora now. Signed-off-by: Javier Martinez Canillas --- bootstrap.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap.conf b/bootstrap.conf index 6b043fc354..52d4af44be 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -92,7 +92,7 @@ bootstrap_post_import_hook () { patch -d po -p3 \ < "po/gettext-patches/$patchname.patch" done - FROM_BOOTSTRAP=1 ./autogen.sh + PYTHON=python3 FROM_BOOTSTRAP=1 ./autogen.sh set +e # bootstrap expects this } From 38475f565856077feee56df328e1a7448cb0b41c Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 5 Mar 2020 16:21:47 +0100 Subject: [PATCH 120/367] efi/http: Export {fw,http}_path variables to make them global The fw_path environment variable is used by http_configure() function to determine the HTTP path that should be used as prefix when using relative HTTP paths. And this is stored in the http_path environment variable. Later, that variable is looked up by grub_efihttp_open() to generate the complete path to be used in the HTTP request. But these variables are not exported, which means that are not global and so are only found in the initial context. This can cause commands like configfile that create a new context to fail because the fw_path and http_path variables will not be found. Resolves: rhbz#1616395 Signed-off-by: Javier Martinez Canillas --- grub-core/kern/main.c | 1 + grub-core/net/efi/http.c | 1 + 2 files changed, 2 insertions(+) diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c index 1c540fc8c2..b573be6650 100644 --- a/grub-core/kern/main.c +++ b/grub-core/kern/main.c @@ -143,6 +143,7 @@ grub_set_prefix_and_root (void) if (fw_path) { grub_env_set ("fw_path", fw_path); + grub_env_export ("fw_path"); grub_dprintf ("fw_path", "fw_path:\"%s\"\n", fw_path); grub_free (fw_path); } diff --git a/grub-core/net/efi/http.c b/grub-core/net/efi/http.c index de351b2cd0..755b7a6d05 100644 --- a/grub-core/net/efi/http.c +++ b/grub-core/net/efi/http.c @@ -39,6 +39,7 @@ http_configure (struct grub_efi_net_device *dev, int prefer_ip6) http_path++; grub_env_unset ("http_path"); grub_env_set ("http_path", http_path); + grub_env_export ("http_path"); } } From c49a33a326f774453b7fa13964455a16c0c6cd25 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 5 Mar 2020 16:21:58 +0100 Subject: [PATCH 121/367] efi/http: Enclose literal IPv6 addresses in square brackets According to RFC 2732 (https://www.ietf.org/rfc/rfc2732.txt), literal IPv6 addresses must be enclosed in square brackets. But GRUB currently does not do this and is causing HTTP servers to send Bad Request (400) responses. For example, the following is the HTTP stream when fetching a config file: HEAD /EFI/BOOT/grub.cfg HTTP/1.1 Host: 2000:dead:beef:a::1 Accept: */* User-Agent: UefiHttpBoot/1.0 HTTP/1.1 400 Bad Request Date: Thu, 05 Mar 2020 14:46:02 GMT Server: Apache/2.4.41 (Fedora) OpenSSL/1.1.1d Connection: close Content-Type: text/html; charset=iso-8859-1 and after enclosing the IPv6 address the HTTP request is successful: HEAD /EFI/BOOT/grub.cfg HTTP/1.1 Host: [2000:dead:beef:a::1] Accept: */* User-Agent: UefiHttpBoot/1.0 HTTP/1.1 200 OK Date: Thu, 05 Mar 2020 14:48:04 GMT Server: Apache/2.4.41 (Fedora) OpenSSL/1.1.1d Last-Modified: Thu, 27 Feb 2020 17:45:58 GMT ETag: "206-59f924b24b1da" Accept-Ranges: bytes Content-Length: 518 Resolves: rhbz#1732765 Signed-off-by: Javier Martinez Canillas --- grub-core/net/efi/http.c | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/grub-core/net/efi/http.c b/grub-core/net/efi/http.c index 755b7a6d05..fc8cb25ae0 100644 --- a/grub-core/net/efi/http.c +++ b/grub-core/net/efi/http.c @@ -158,13 +158,7 @@ efihttp_request (grub_efi_http_t *http, char *server, char *name, int use_https, grub_efi_status_t status; grub_efi_boot_services_t *b = grub_efi_system_table->boot_services; char *url = NULL; - - request_headers[0].field_name = (grub_efi_char8_t *)"Host"; - request_headers[0].field_value = (grub_efi_char8_t *)server; - request_headers[1].field_name = (grub_efi_char8_t *)"Accept"; - request_headers[1].field_value = (grub_efi_char8_t *)"*/*"; - request_headers[2].field_name = (grub_efi_char8_t *)"User-Agent"; - request_headers[2].field_value = (grub_efi_char8_t *)"UefiHttpBoot/1.0"; + char *hostname = NULL; { grub_efi_ipv6_address_t address; @@ -174,9 +168,24 @@ efihttp_request (grub_efi_http_t *http, char *server, char *name, int use_https, const char *protocol = (use_https == 1) ? "https" : "http"; if (grub_efi_string_to_ip6_address (server, &address, &rest) && *rest == 0) - url = grub_xasprintf ("%s://[%s]%s", protocol, server, name); + { + hostname = grub_xasprintf ("[%s]", server); + if (!hostname) + return GRUB_ERR_OUT_OF_MEMORY; + + server = hostname; + + url = grub_xasprintf ("%s://%s%s", protocol, server, name); + if (!url) + { + grub_free (hostname); + return GRUB_ERR_OUT_OF_MEMORY; + } + } else - url = grub_xasprintf ("%s://%s%s", protocol, server, name); + { + url = grub_xasprintf ("%s://%s%s", protocol, server, name); + } if (!url) { @@ -199,6 +208,13 @@ efihttp_request (grub_efi_http_t *http, char *server, char *name, int use_https, request_data.url = ucs2_url; } + request_headers[0].field_name = (grub_efi_char8_t *)"Host"; + request_headers[0].field_value = (grub_efi_char8_t *)server; + request_headers[1].field_name = (grub_efi_char8_t *)"Accept"; + request_headers[1].field_value = (grub_efi_char8_t *)"*/*"; + request_headers[2].field_name = (grub_efi_char8_t *)"User-Agent"; + request_headers[2].field_value = (grub_efi_char8_t *)"UefiHttpBoot/1.0"; + request_data.method = (headeronly > 0) ? GRUB_EFI_HTTPMETHODHEAD : GRUB_EFI_HTTPMETHODGET; request_message.data.request = &request_data; @@ -228,6 +244,9 @@ efihttp_request (grub_efi_http_t *http, char *server, char *name, int use_https, status = efi_call_2 (http->request, http, &request_token); + if (hostname) + grub_free (hostname); + if (status != GRUB_EFI_SUCCESS) { efi_call_1 (b->close_event, request_token.event); From 1d5a347529f68093545e3fcb6de5c14139f41b15 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 9 Mar 2020 15:29:45 +0100 Subject: [PATCH 122/367] efi/net: Allow to specify a port number in addresses The grub_efi_net_parse_address() function is not covering the case where a port number is specified in an IPv4 or IPv6 address, so will fail to parse the network address. For most cases the issue is harmless, because the function is only used to match an address with a network interface and if fails the default is used. But still is a bug that has to be fixed and it causes error messages to be printed like the following: error: net/efi/net.c:782:unrecognised network address '192.168.122.1:8080' error: net/efi/net.c:781:unrecognised network address '[2000:dead:beef:a::1]:8080' Resolves: rhbz#1732765 Signed-off-by: Javier Martinez Canillas --- grub-core/net/efi/net.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/net/efi/net.c b/grub-core/net/efi/net.c index 6603cd83ed..84573937b1 100644 --- a/grub-core/net/efi/net.c +++ b/grub-core/net/efi/net.c @@ -742,7 +742,7 @@ grub_efi_net_parse_address (const char *address, return GRUB_ERR_NONE; } } - else if (*rest == 0) + else if (*rest == 0 || *rest == ':') { grub_uint32_t subnet_mask = 0xffffffffU; grub_memcpy (ip4->subnet_mask, &subnet_mask, sizeof (ip4->subnet_mask)); @@ -768,7 +768,7 @@ grub_efi_net_parse_address (const char *address, return GRUB_ERR_NONE; } } - else if (*rest == 0) + else if (*rest == 0 || *rest == ':') { ip6->prefix_length = 128; ip6->is_anycast = 0; From d38bbe21d166d7a7b2c98b19565b9b25c4152566 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 9 Mar 2020 15:30:05 +0100 Subject: [PATCH 123/367] efi/ip4_config: Improve check to detect literal IPv6 addresses The grub_efi_string_to_ip4_address() function wrongly assumes that an IPv6 address is an IPv4 address, because it doesn't take into account the case of a caller passing an IPv6 address as a string. This leads to the grub_efi_net_parse_address() function to fail and print the following error message: error: net/efi/net.c:785:unrecognised network address '2000:dead:beef:a::1' Resolves: rhbz#1732765 Signed-off-by: Javier Martinez Canillas --- grub-core/net/efi/ip4_config.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/grub-core/net/efi/ip4_config.c b/grub-core/net/efi/ip4_config.c index b711a5d945..313c818b18 100644 --- a/grub-core/net/efi/ip4_config.c +++ b/grub-core/net/efi/ip4_config.c @@ -56,9 +56,20 @@ int grub_efi_string_to_ip4_address (const char *val, grub_efi_ipv4_address_t *address, const char **rest) { grub_uint32_t newip = 0; - int i; + int i, ncolon = 0; const char *ptr = val; + /* Check that is not an IPv6 address */ + for (i = 0; i < grub_strlen(ptr); i++) + { + if (ptr[i] == '[' && i == 0) + return 0; + + if (ptr[i] == ':') + if (i == 0 || ++ncolon == 2) + return 0; + } + for (i = 0; i < 4; i++) { unsigned long t; From 5f331e88144922ba13bcbf3397e26a8928162707 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 10 Mar 2020 11:23:49 +0100 Subject: [PATCH 124/367] efi/net: Print a debug message if parsing the address fails Currently if parsing the address fails an error message is printed. But in most cases this isn't a fatal error since the grub_efi_net_parse_address() function is only used to match an address with a network interface to use. And if this fails, the default interface is used which is good enough for most cases. So instead of printing an error that would pollute the console just print a debug message if the address is not parsed correctly. A user can enable debug messages for the efinet driver to have information about the failure and the fact that the default interface is being used. Related: rhbz#1732765 Signed-off-by: Javier Martinez Canillas --- grub-core/net/efi/net.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/grub-core/net/efi/net.c b/grub-core/net/efi/net.c index 84573937b1..a3f0535d43 100644 --- a/grub-core/net/efi/net.c +++ b/grub-core/net/efi/net.c @@ -778,9 +778,9 @@ grub_efi_net_parse_address (const char *address, } } - return grub_error (GRUB_ERR_NET_BAD_ADDRESS, - N_("unrecognised network address `%s'"), - address); + grub_dprintf ("efinet", "unrecognised network address '%s'\n", address); + + return GRUB_ERR_NET_BAD_ADDRESS; } static grub_efi_net_interface_t * @@ -795,10 +795,7 @@ match_route (const char *server) err = grub_efi_net_parse_address (server, &ip4, &ip6, &is_ip6, 0); if (err) - { - grub_print_error (); return NULL; - } if (is_ip6) { @@ -1233,8 +1230,15 @@ grub_net_open_real (const char *name __attribute__ ((unused))) /*FIXME: Use DNS translate name to address */ net_interface = match_route (server); + if (!net_interface && net_default_interface) + { + net_interface = net_default_interface; + grub_dprintf ("efinet", "interface lookup failed, using default '%s'\n", + net_interface->name); + } + /*XXX: should we check device with default gateway ? */ - if (!net_interface && !(net_interface = net_default_interface)) + if (!net_interface) { grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' no route found"), name); From efea4aa3a7c8f80c9ed38509919d8388fbf717ec Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 22 Apr 2020 12:41:52 +0200 Subject: [PATCH 125/367] kern/term: Also accept F8 as a user interrupt key Make F8, which used to be the hotkey to show the Windows boot menu during boot for a long long time, also interrupt sleeps / stop the menu countdown. Signed-off-by: Javier Martinez Canillas --- grub-core/kern/term.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/grub-core/kern/term.c b/grub-core/kern/term.c index 14d5964983..4d61f4e979 100644 --- a/grub-core/kern/term.c +++ b/grub-core/kern/term.c @@ -144,9 +144,10 @@ grub_key_is_interrupt (int key) /* * ESC sometimes is the BIOS setup hotkey and may be hard to discover, also * check F4, which was chosen because is not used as a hotkey to enter the - * BIOS setup by any vendor. + * BIOS setup by any vendor. Also, F8 which was the key to get the Windows + * bootmenu for a long time. */ - if (key == GRUB_TERM_ESC || key == GRUB_TERM_KEY_F4) + if (key == GRUB_TERM_ESC || key == GRUB_TERM_KEY_F4 || key == GRUB_TERM_KEY_F8) return 1; /* From c003e1bdaacad7c6b9b76eb8ec8a4b3dd7b0755e Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 23 Apr 2020 15:06:46 +0200 Subject: [PATCH 126/367] efi: Set image base address before jumping to the PE/COFF entry point Upstream GRUB uses the EFI LoadImage() and StartImage() to boot the Linux kernel. But our custom EFI loader that supports Secure Boot instead uses the EFI handover protocol (for x86) or jumping directly to the PE/COFF entry point (for aarch64). This is done to allow the bootloader to verify the images using the shim lock protocol to avoid booting untrusted binaries. Since the bootloader loads the kernel from the boot media instead of using LoadImage(), it is responsible to set the Loaded Image base address before booting the kernel. Otherwise the kernel EFI stub will complain that it was not set correctly and print the following warning message: EFI stub: ERROR: FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value Resolves: rhbz#1814690 Signed-off-by: Javier Martinez Canillas --- grub-core/loader/efi/linux.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c index 0622dfa48d..e8b9ecb17f 100644 --- a/grub-core/loader/efi/linux.c +++ b/grub-core/loader/efi/linux.c @@ -72,6 +72,7 @@ grub_err_t grub_efi_linux_boot (void *kernel_addr, grub_off_t handover_offset, void *kernel_params) { + grub_efi_loaded_image_t *loaded_image = NULL; handover_func hf; int offset = 0; @@ -79,6 +80,19 @@ grub_efi_linux_boot (void *kernel_addr, grub_off_t handover_offset, offset = 512; #endif + /* + * Since the EFI loader is not calling the LoadImage() and StartImage() + * services for loading the kernel and booting respectively, it has to + * set the Loaded Image base address. + */ + loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle); + if (loaded_image) + loaded_image->image_base = kernel_addr; + else + grub_dprintf ("linux", "Loaded Image base address could not be set\n"); + + grub_dprintf ("linux", "kernel_addr: %p handover_offset: %p params: %p\n", + kernel_addr, (void *)(grub_efi_uintn_t)handover_offset, kernel_params); hf = (handover_func)((char *)kernel_addr + handover_offset + offset); hf (grub_efi_image_handle, grub_efi_system_table, kernel_params); From 2b21b645b8f3459f3cd96be981c80ac9310bb201 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Sat, 16 May 2020 11:33:18 +0200 Subject: [PATCH 127/367] tpm: Don't propagate TPM measurement errors to the verifiers layer Currently if the EFI firmware fails to do a TPM measurement for a file, the error will be propagated to the verifiers framework and so opening the file will not succeed. This mean that buggy firmwares will prevent the system to boot since the loader won't be able to open any file. But failing to do TPM measurements shouldn't be a fatal error and the system should still be able to boot. Signed-off-by: Javier Martinez Canillas --- grub-core/commands/tpm.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/grub-core/commands/tpm.c b/grub-core/commands/tpm.c index 2052c36eab..e287d042e6 100644 --- a/grub-core/commands/tpm.c +++ b/grub-core/commands/tpm.c @@ -42,7 +42,8 @@ grub_tpm_verify_init (grub_file_t io, static grub_err_t grub_tpm_verify_write (void *context, void *buf, grub_size_t size) { - return grub_tpm_measure (buf, size, GRUB_BINARY_PCR, context); + grub_tpm_measure (buf, size, GRUB_BINARY_PCR, context); + return GRUB_ERR_NONE; } static grub_err_t @@ -50,7 +51,6 @@ grub_tpm_verify_string (char *str, enum grub_verify_string_type type) { const char *prefix = NULL; char *description; - grub_err_t status; switch (type) { @@ -66,15 +66,15 @@ grub_tpm_verify_string (char *str, enum grub_verify_string_type type) } description = grub_malloc (grub_strlen (str) + grub_strlen (prefix) + 1); if (!description) - return grub_errno; + return GRUB_ERR_NONE; grub_memcpy (description, prefix, grub_strlen (prefix)); grub_memcpy (description + grub_strlen (prefix), str, grub_strlen (str) + 1); - status = - grub_tpm_measure ((unsigned char *) str, grub_strlen (str), - GRUB_STRING_PCR, description); + + grub_tpm_measure ((unsigned char *) str, grub_strlen (str), GRUB_STRING_PCR, + description); grub_free (description); - return status; + return GRUB_ERR_NONE; } struct grub_file_verifier grub_tpm_verifier = { From b6da5b71a55d93b34124f593f5d489e9220e2d66 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 26 May 2020 16:59:28 +0200 Subject: [PATCH 128/367] x86-efi: Reduce maximum bounce buffer size to 16 MiB The EFI linux loader allocates a bounce buffer to copy the initrd since in some machines doing DMA on addresses above 4GB is not possible during EFI. But the verifiers framework also allocates a buffer to copy the initrd in its grub_file_open() handler. It does this since the data to verify has to be passed as a single chunk to modules that use the verifiers framework. If the initrd image size is big there may not be enough memory in the heap to allocate two buffers of that size. This causes an allocation failure in the verifiers framework and leads to the initrd not being read. To prevent these allocation failures, let's reduce the maximum size of the bounce buffer used in the EFI loader. Since the data read can be copied to the actual initrd address in multilple chunks. Resolves: rhbz#1838633 Signed-off-by: Javier Martinez Canillas --- grub-core/loader/i386/efi/linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 6bc18d5aef..15d40d6e35 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -144,7 +144,7 @@ grub_linuxefi_unload (void) return GRUB_ERR_NONE; } -#define BOUNCE_BUFFER_MAX 0x10000000ull +#define BOUNCE_BUFFER_MAX 0x1000000ull static grub_ssize_t read(grub_file_t file, grub_uint8_t *bufp, grub_size_t len) From f0e14433e585c4e8d6e6a99ab41c26ad3eb532ae Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 2 Jun 2020 13:25:01 +0200 Subject: [PATCH 129/367] http: Prepend prefix when the HTTP path is relative as done in efi/http There are two different HTTP drivers that can be used when requesting an HTTP resource: the efi/http that uses the EFI_HTTP_PROTOCOL and the http that uses GRUB's HTTP and TCP/IP implementation. The efi/http driver appends a prefix that is defined in the variable http_path, but the http driver doesn't. So using this driver and attempting to fetch a resource using a relative path fails. Signed-off-by: Javier Martinez Canillas --- grub-core/net/http.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/grub-core/net/http.c b/grub-core/net/http.c index b52b558d63..7f878b5615 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -501,13 +501,20 @@ http_open (struct grub_file *file, const char *filename) { grub_err_t err; struct http_data *data; + const char *http_path; data = grub_zalloc (sizeof (*data)); if (!data) return grub_errno; file->size = GRUB_FILE_SIZE_UNKNOWN; - data->filename = grub_strdup (filename); + /* If path is relative, prepend http_path */ + http_path = grub_env_get ("http_path"); + if (http_path && filename[0] != '/') + data->filename = grub_xasprintf ("%s/%s", http_path, filename); + else + data->filename = grub_strdup (filename); + if (!data->filename) { grub_free (data); From 0974ee58169a4ac8d3bc0d85718449e0605dc0dc Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 16 Jan 2019 13:21:46 -0500 Subject: [PATCH 130/367] Fix a missing return in efi-export-env and efi-load-env commands Somewhere along the way this got mis-merged to include a return without a value. Fix it up. Signed-off-by: Peter Jones --- grub-core/commands/efi/env.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/commands/efi/env.c b/grub-core/commands/efi/env.c index cbd13e03e8..977edb6b06 100644 --- a/grub-core/commands/efi/env.c +++ b/grub-core/commands/efi/env.c @@ -149,6 +149,8 @@ grub_efi_load_env(grub_command_t cmd __attribute__ ((unused)), grub_envblk_iterate (envblk, NULL, set_var); grub_free (envblk_s.buf); + + return GRUB_ERR_NONE; } static grub_command_t export_cmd, loadenv_cmd; From 8c89092762ae4b752b53eec5b48939f427a744e8 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Sun, 19 Jul 2020 17:11:06 -0400 Subject: [PATCH 131/367] efi+dhcp: fix some allocation error checking. Signed-off-by: Peter Jones --- grub-core/net/efi/dhcp.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/grub-core/net/efi/dhcp.c b/grub-core/net/efi/dhcp.c index dbef63d8c0..e5c79b748b 100644 --- a/grub-core/net/efi/dhcp.c +++ b/grub-core/net/efi/dhcp.c @@ -80,7 +80,7 @@ grub_efi_dhcp4_parse_dns (grub_efi_dhcp4_protocol_t *dhcp4, grub_efi_dhcp4_packe if (status != GRUB_EFI_BUFFER_TOO_SMALL) return NULL; - option_list = grub_malloc (option_count * sizeof(*option_list)); + option_list = grub_calloc (option_count, sizeof(*option_list)); if (!option_list) return NULL; @@ -360,8 +360,11 @@ grub_cmd_efi_bootp6 (struct grub_command *cmd __attribute__ ((unused)), if (status == GRUB_EFI_BUFFER_TOO_SMALL && count) { - options = grub_malloc (count * sizeof(*options)); - status = efi_call_4 (dev->dhcp6->parse, dev->dhcp6, mode.ia->reply_packet, &count, options); + options = grub_calloc (count, sizeof(*options)); + if (options) + status = efi_call_4 (dev->dhcp6->parse, dev->dhcp6, mode.ia->reply_packet, &count, options); + else + status = GRUB_EFI_OUT_OF_RESOURCES; } if (status != GRUB_EFI_SUCCESS) From 8a8faa76d63afc3feeeee3d6ad27286a9048543a Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Sun, 19 Jul 2020 17:14:15 -0400 Subject: [PATCH 132/367] efi+http: fix some allocation error checking. Signed-off-by: Peter Jones --- grub-core/net/efi/http.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/grub-core/net/efi/http.c b/grub-core/net/efi/http.c index fc8cb25ae0..26647a50fa 100644 --- a/grub-core/net/efi/http.c +++ b/grub-core/net/efi/http.c @@ -412,8 +412,8 @@ grub_efihttp_open (struct grub_efi_net_device *dev, int type) { grub_err_t err; - grub_off_t size; - char *buf; + grub_off_t size = 0; + char *buf = NULL; char *file_name = NULL; const char *http_path; @@ -441,8 +441,11 @@ grub_efihttp_open (struct grub_efi_net_device *dev, return err; } - buf = grub_malloc (size); - efihttp_read (dev, buf, size); + if (size) + { + buf = grub_malloc (size); + efihttp_read (dev, buf, size); + } file->size = size; file->data = buf; From 8368fef9254d403ca160f77d9df6b9f5142c1025 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Sun, 19 Jul 2020 17:27:00 -0400 Subject: [PATCH 133/367] efi/ip[46]_config.c: fix some potential allocation overflows In theory all of this data comes from the firmware stack and it should be safe, but it's better to be paranoid. Signed-off-by: Peter Jones --- grub-core/net/efi/ip4_config.c | 25 ++++++++++++++++++------- grub-core/net/efi/ip6_config.c | 13 ++++++++++--- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/grub-core/net/efi/ip4_config.c b/grub-core/net/efi/ip4_config.c index 313c818b18..9725e928f7 100644 --- a/grub-core/net/efi/ip4_config.c +++ b/grub-core/net/efi/ip4_config.c @@ -4,15 +4,20 @@ #include #include #include +#include char * grub_efi_hw_address_to_string (grub_efi_uint32_t hw_address_size, grub_efi_mac_address_t hw_address) { char *hw_addr, *p; - int sz, s; - int i; + grub_size_t sz, s, i; - sz = (int)hw_address_size * (sizeof ("XX:") - 1) + 1; + if (grub_mul (hw_address_size, sizeof ("XX:") - 1, &sz) || + grub_add (sz, 1, &sz)) + { + grub_errno = GRUB_ERR_OUT_OF_RANGE; + return NULL; + } hw_addr = grub_malloc (sz); if (!hw_addr) @@ -20,7 +25,7 @@ grub_efi_hw_address_to_string (grub_efi_uint32_t hw_address_size, grub_efi_mac_a p = hw_addr; s = sz; - for (i = 0; i < (int)hw_address_size; i++) + for (i = 0; i < hw_address_size; i++) { grub_snprintf (p, sz, "%02x:", hw_address[i]); p += sizeof ("XX:") - 1; @@ -238,14 +243,20 @@ grub_efi_ip4_interface_route_table (struct grub_efi_net_device *dev) { grub_efi_ip4_config2_interface_info_t *interface_info; char **ret; - int i, id; + int id; + grub_size_t i, nmemb; interface_info = efi_ip4_config_interface_info (dev->ip4_config); if (!interface_info) return NULL; - ret = grub_malloc (sizeof (*ret) * (interface_info->route_table_size + 1)); + if (grub_add (interface_info->route_table_size, 1, &nmemb)) + { + grub_errno = GRUB_ERR_OUT_OF_RANGE; + return NULL; + } + ret = grub_calloc (nmemb, sizeof (*ret)); if (!ret) { grub_free (interface_info); @@ -253,7 +264,7 @@ grub_efi_ip4_interface_route_table (struct grub_efi_net_device *dev) } id = 0; - for (i = 0; i < (int)interface_info->route_table_size; i++) + for (i = 0; i < interface_info->route_table_size; i++) { char *subnet, *gateway, *mask; grub_uint32_t u32_subnet, u32_gateway; diff --git a/grub-core/net/efi/ip6_config.c b/grub-core/net/efi/ip6_config.c index 017c4d05bc..a46f6f9b68 100644 --- a/grub-core/net/efi/ip6_config.c +++ b/grub-core/net/efi/ip6_config.c @@ -3,6 +3,7 @@ #include #include #include +#include char * grub_efi_ip6_address_to_string (grub_efi_pxe_ipv6_address_t *address) @@ -228,14 +229,20 @@ grub_efi_ip6_interface_route_table (struct grub_efi_net_device *dev) { grub_efi_ip6_config_interface_info_t *interface_info; char **ret; - int i, id; + int id; + grub_size_t i, nmemb; interface_info = efi_ip6_config_interface_info (dev->ip6_config); if (!interface_info) return NULL; - ret = grub_malloc (sizeof (*ret) * (interface_info->route_count + 1)); + if (grub_add (interface_info->route_count, 1, &nmemb)) + { + grub_errno = GRUB_ERR_OUT_OF_RANGE; + return NULL; + } + ret = grub_calloc (nmemb, sizeof (*ret)); if (!ret) { grub_free (interface_info); @@ -243,7 +250,7 @@ grub_efi_ip6_interface_route_table (struct grub_efi_net_device *dev) } id = 0; - for (i = 0; i < (int)interface_info->route_count ; i++) + for (i = 0; i < interface_info->route_count ; i++) { char *gateway, *destination; grub_uint64_t u64_gateway[2]; From 2ad53432adbdeff39fcdaab54414b5ae3b084c78 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Fri, 24 Jul 2020 17:18:09 +0100 Subject: [PATCH 134/367] efilinux: Fix integer overflows in grub_cmd_initrd These could be triggered by an extremely large number of arguments to the initrd command on 32-bit architectures, or a crafted filesystem with very large files on any architecture. Signed-off-by: Colin Watson --- grub-core/loader/i386/efi/linux.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 15d40d6e35..f992ceeef2 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -206,7 +208,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), goto fail; } - files = grub_zalloc (argc * sizeof (files[0])); + files = grub_calloc (argc, sizeof (files[0])); if (!files) goto fail; @@ -216,7 +218,11 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), if (! files[i]) goto fail; nfiles++; - size += ALIGN_UP (grub_file_size (files[i]), 4); + if (grub_add (size, ALIGN_UP (grub_file_size (files[i]), 4), &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + goto fail; + } } initrd_mem = kernel_alloc(size, N_("can't allocate initrd")); From 717c8293a7e3af264070b5ffbf3c6d38e76f8b5f Mon Sep 17 00:00:00 2001 From: Dimitri John Ledkov Date: Wed, 22 Jul 2020 11:31:43 +0100 Subject: [PATCH 135/367] linuxefi: fail kernel validation without shim protocol. If certificates that signed grub are installed into db, grub can be booted directly. It will then boot any kernel without signature validation. The booted kernel will think it was booted in secureboot mode and will implement lockdown, yet it could have been tampered. This version of the patch skips calling verification, when booted without secureboot. And is indented with gnu ident. CVE-2020-15705 Reported-by: Mathieu Trudel-Lapierre Signed-off-by: Dimitri John Ledkov --- grub-core/loader/arm64/linux.c | 13 +++++++++---- grub-core/loader/efi/chainloader.c | 1 + grub-core/loader/efi/linux.c | 1 + grub-core/loader/i386/efi/linux.c | 17 +++++++++++------ 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c index 70a0075ec5..47f8cf0d84 100644 --- a/grub-core/loader/arm64/linux.c +++ b/grub-core/loader/arm64/linux.c @@ -34,6 +34,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -363,11 +364,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_dprintf ("linux", "kernel @ %p\n", kernel_addr); - rc = grub_linuxefi_secure_validate (kernel_addr, kernel_size); - if (rc < 0) + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) { - grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), argv[0]); - goto fail; + rc = grub_linuxefi_secure_validate (kernel_addr, kernel_size); + if (rc <= 0) + { + grub_error (GRUB_ERR_INVALID_COMMAND, + N_("%s has invalid signature"), argv[0]); + goto fail; + } } pe = (void *)((unsigned long)kernel_addr + lh.hdr_offset); diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index ac8dfd40c6..d41e8ea14a 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -1084,6 +1084,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), return 0; } + // -1 fall-through to fail fail: if (dev) diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c index e8b9ecb17f..9260731c10 100644 --- a/grub-core/loader/efi/linux.c +++ b/grub-core/loader/efi/linux.c @@ -33,6 +33,7 @@ struct grub_efi_shim_lock }; typedef struct grub_efi_shim_lock grub_efi_shim_lock_t; +// Returns 1 on success, -1 on error, 0 when not available int grub_linuxefi_secure_validate (void *data, grub_uint32_t size) { diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index f992ceeef2..3cf0f9b330 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -30,6 +30,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -101,7 +102,7 @@ kernel_alloc(grub_efi_uintn_t size, const char * const errmsg) pages = BYTES_TO_PAGES(size); grub_dprintf ("linux", "Trying to allocate %lu pages from %p\n", - pages, (void *)max); + (unsigned long)pages, (void *)(unsigned long)max); prev_max = max; addr = grub_efi_allocate_pages_real (max, pages, @@ -307,12 +308,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } - rc = grub_linuxefi_secure_validate (kernel, filelen); - if (rc < 0) + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) { - grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), - argv[0]); - goto fail; + rc = grub_linuxefi_secure_validate (kernel, filelen); + if (rc <= 0) + { + grub_error (GRUB_ERR_INVALID_COMMAND, + N_("%s has invalid signature"), argv[0]); + goto fail; + } } lh = (struct linux_i386_kernel_header *)kernel; @@ -386,6 +390,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), setup_header_end_offset = *((grub_uint8_t *)kernel + 0x201); grub_dprintf ("linux", "copying %lu bytes from %p to %p\n", + (unsigned long) MIN((grub_size_t)0x202+setup_header_end_offset, sizeof (*params)) - 0x1f1, (grub_uint8_t *)kernel + 0x1f1, From ffff971788e7982228cbb46a172849a52661438c Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 20 Jul 2020 12:24:02 -0400 Subject: [PATCH 136/367] Fix const char ** pointers in grub-core/net/bootp.c This will need to get folded back in the right place on the next rebase, but it's before "Make grub_strtol() "end" pointers have safer const qualifiers" currently, so for now I'm leaving it here instead of merging it back with the original patch. Signed-off-by: Peter Jones --- grub-core/net/bootp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index 8fb8918ae7..7baf3540c8 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -329,7 +329,7 @@ grub_net_configure_by_dhcp_ack (const char *name, struct grub_net_network_level_interface *inter; int mask = -1; char server_ip[sizeof ("xxx.xxx.xxx.xxx")]; - const grub_uint8_t *opt; + const char *opt; grub_uint8_t opt_len, overload = 0; const char *boot_file = 0, *server_name = 0; grub_size_t boot_file_len, server_name_len; @@ -505,7 +505,7 @@ grub_net_configure_by_dhcp_ack (const char *name, if (opt && opt_len) { grub_env_set_net_property (name, "vendor_class_identifier", (const char *) opt, opt_len); - if (opt && grub_strcmp (opt, "HTTPClient") == 0) + if (opt && grub_strcmp ((char *)opt, "HTTPClient") == 0) { char *proto, *ip, *pa; From f7a79899340deafbfca2abd57eef90ef24a3e7cc Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 20 Jul 2020 12:24:02 -0400 Subject: [PATCH 137/367] Fix const char ** pointers in grub-core/net/efi/ip4_config.c This will need to get folded back in the right place on the next rebase, but it's before "Make grub_strtol() "end" pointers have safer const qualifiers" currently, so for now I'm leaving it here instead of merging it back with the original patch. Signed-off-by: Peter Jones --- grub-core/net/efi/ip4_config.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/grub-core/net/efi/ip4_config.c b/grub-core/net/efi/ip4_config.c index 9725e928f7..cb880fc3e8 100644 --- a/grub-core/net/efi/ip4_config.c +++ b/grub-core/net/efi/ip4_config.c @@ -61,7 +61,8 @@ int grub_efi_string_to_ip4_address (const char *val, grub_efi_ipv4_address_t *address, const char **rest) { grub_uint32_t newip = 0; - int i, ncolon = 0; + grub_size_t i; + int ncolon = 0; const char *ptr = val; /* Check that is not an IPv6 address */ @@ -78,7 +79,7 @@ grub_efi_string_to_ip4_address (const char *val, grub_efi_ipv4_address_t *addres for (i = 0; i < 4; i++) { unsigned long t; - t = grub_strtoul (ptr, (char **) &ptr, 0); + t = grub_strtoul (ptr, &ptr, 0); if (grub_errno) { grub_errno = GRUB_ERR_NONE; From 3e2b6b23dea6fe60dce7f9e7d01728b3d86330ae Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 20 Jul 2020 12:24:02 -0400 Subject: [PATCH 138/367] Fix const char ** pointers in grub-core/net/efi/ip6_config.c This will need to get folded back in the right place on the next rebase, but it's before "Make grub_strtol() "end" pointers have safer const qualifiers" currently, so for now I'm leaving it here instead of merging it back with the original patch. Signed-off-by: Peter Jones --- grub-core/net/efi/ip6_config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/net/efi/ip6_config.c b/grub-core/net/efi/ip6_config.c index a46f6f9b68..1c5415d718 100644 --- a/grub-core/net/efi/ip6_config.c +++ b/grub-core/net/efi/ip6_config.c @@ -85,7 +85,7 @@ grub_efi_string_to_ip6_address (const char *val, grub_efi_ipv6_address_t *addres ptr++; continue; } - t = grub_strtoul (ptr, (char **) &ptr, 16); + t = grub_strtoul (ptr, &ptr, 16); if (grub_errno) { grub_errno = GRUB_ERR_NONE; From 7ea499ce2a3362adc2fc27384a0330435f2fd6dd Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 20 Jul 2020 12:24:02 -0400 Subject: [PATCH 139/367] Fix const char ** pointers in grub-core/net/efi/net.c This will need to get folded back in the right place on the next rebase, but it's before "Make grub_strtol() "end" pointers have safer const qualifiers" currently, so for now I'm leaving it here instead of merging it back with the original patch. Signed-off-by: Peter Jones --- grub-core/net/efi/net.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/net/efi/net.c b/grub-core/net/efi/net.c index a3f0535d43..78e5442fc5 100644 --- a/grub-core/net/efi/net.c +++ b/grub-core/net/efi/net.c @@ -729,7 +729,7 @@ grub_efi_net_parse_address (const char *address, { grub_uint32_t subnet_mask_size; - subnet_mask_size = grub_strtoul (rest + 1, (char **) &rest, 0); + subnet_mask_size = grub_strtoul (rest + 1, &rest, 0); if (!grub_errno && subnet_mask_size <= 32 && *rest == 0) { @@ -758,7 +758,7 @@ grub_efi_net_parse_address (const char *address, { grub_efi_uint8_t prefix_length; - prefix_length = grub_strtoul (rest + 1, (char **) &rest, 0); + prefix_length = grub_strtoul (rest + 1, &rest, 0); if (!grub_errno && prefix_length <= 128 && *rest == 0) { ip6->prefix_length = prefix_length; From 58b7b115ebe58a3adbd13d4a20ea11b059f76edf Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 20 Jul 2020 12:24:02 -0400 Subject: [PATCH 140/367] Fix const char ** pointers in grub-core/net/efi/pxe.c This will need to get folded back in the right place on the next rebase, but it's before "Make grub_strtol() "end" pointers have safer const qualifiers" currently, so for now I'm leaving it here instead of merging it back with the original patch. Signed-off-by: Peter Jones --- grub-core/net/efi/pxe.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grub-core/net/efi/pxe.c b/grub-core/net/efi/pxe.c index 531949cba5..73e2bb01c1 100644 --- a/grub-core/net/efi/pxe.c +++ b/grub-core/net/efi/pxe.c @@ -187,7 +187,7 @@ parse_ip6 (const char *val, grub_uint64_t *ip, const char **rest) ptr++; continue; } - t = grub_strtoul (ptr, (char **) &ptr, 16); + t = grub_strtoul (ptr, &ptr, 16); if (grub_errno) { grub_errno = GRUB_ERR_NONE; @@ -225,7 +225,7 @@ pxe_open (struct grub_efi_net_device *dev, int type __attribute__((unused))) { int i; - char *p; + const char *p; grub_efi_status_t status; grub_efi_pxe_ip_address_t server_ip; grub_efi_uint64_t file_size = 0; @@ -313,7 +313,7 @@ pxe_read (struct grub_efi_net_device *dev, grub_size_t len) { int i; - char *p; + const char *p; grub_efi_status_t status; grub_efi_pxe_t *pxe = (prefer_ip6) ? dev->ip6_pxe : dev->ip4_pxe; grub_efi_uint64_t bufsz = len; From 39698498b51d9f4486add7f43be1a059a96bcb0a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 22 Jul 2020 14:03:42 +0200 Subject: [PATCH 141/367] Add systemd integration scripts to make "systemctl reboot --boot-loader-menu=xxx" work with grub This commit adds a number of scripts / config files to make "systemctl reboot --boot-loader-menu=xxx" work with grub: 1. /lib/systemd/system/systemd-logind.service.d/10-grub.conf This sets SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU in the env. for logind, indicating that the boot-loader which is used supports this feature, see: https://github.com/systemd/systemd/blob/master/docs/ENVIRONMENT.md 2. /lib/systemd/system/grub-systemd-integration.service /lib/systemd/system/reboot.target.wants/grub-systemd-integration.service -> ../grub-systemd-integration.service /usr/libexec/grub/grub-systemd-integration.sh The symlink in the .wants dir causes the added service file to be started by systemd just before rebooting the system. If /run/systemd/reboot-to-boot-loader-menu exist then the service will run the grub-systemd-integration.sh script. This script sets the new menu_show_once_timeout grubenv variable to the requested timeout in seconds. 3. /etc/grub.d/14_menu_show_once This new grub-mkconfig snippet adds the necessary code to the generated grub.conf to honor the new menu_show_once_timeout variable, and to automatically clear it after consuming it. Note the service and libexec script use grub-systemd-integration as name because in the future they may be used to add further integration with systemctl reboot --foo options, e.g. support for --boot-loader-entry=NAME. A few notes about upstreaming this patch from the rhboot grub2 fork: 1. I have deliberately put the grub.conf bits for this in a new / separate grub-mkconfig snippet generator for easy upstreaming 2. Even though the commit message mentions the .wants symlink for the .service I have been unable to come up with a clean way to do this at "make install" time, this should be fixed before upstreaming. Downstream notes: 1. Since make install does not add the .wants symlink, this needs to be done in grub2.spec %install 2. This is keeping support for the "old" Fedora specific menu_show_once env variable, which has a hardcoded timeout of 60 sec in 12_menu_auto_hide in place for now. This can be dropped (eventually) in a follow-up patch once GNOME has been converted to use the systemd dbus API equivalent of "systemctl reboot --boot-loader-menu=xxx". Signed-off-by: Hans de Goede --- Makefile.util.def | 27 +++++++++++++++++++ conf/Makefile.common | 6 +++++ util/grub.d/14_menu_show_once.in | 13 +++++++++ util/systemd/10-grub-logind-service.conf.in | 2 ++ .../grub-systemd-integration.service.in | 8 ++++++ util/systemd/systemd-integration.sh.in | 6 +++++ 6 files changed, 62 insertions(+) create mode 100755 util/grub.d/14_menu_show_once.in create mode 100644 util/systemd/10-grub-logind-service.conf.in create mode 100644 util/systemd/grub-systemd-integration.service.in create mode 100644 util/systemd/systemd-integration.sh.in diff --git a/Makefile.util.def b/Makefile.util.def index b4ce5383b7..6366442129 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -470,6 +470,12 @@ script = { installdir = grubconf; }; +script = { + name = '14_menu_show_once'; + common = util/grub.d/14_menu_show_once.in; + installdir = grubconf; +}; + script = { name = '01_users'; common = util/grub.d/01_users.in; @@ -569,6 +575,27 @@ script = { installdir = grubconf; }; +script = { + name = 'grub-systemd-integration.service'; + common = util/systemd/grub-systemd-integration.service.in; + installdir = systemdunit; + condition = COND_HOST_LINUX; +}; + +script = { + name = 'systemd-integration.sh'; + common = util/systemd/systemd-integration.sh.in; + installdir = grublibexec; + condition = COND_HOST_LINUX; +}; + +script = { + name = '10-grub-logind-service.conf'; + common = util/systemd/10-grub-logind-service.conf.in; + installdir = systemd_logind_service_d; + condition = COND_HOST_LINUX; +}; + program = { mansection = 1; name = grub-mkrescue; diff --git a/conf/Makefile.common b/conf/Makefile.common index 0647c53b91..9fe5863b2d 100644 --- a/conf/Makefile.common +++ b/conf/Makefile.common @@ -63,8 +63,11 @@ CCASFLAGS_LIBRARY = $(UTILS_CCASFLAGS) # Other variables grubconfdir = $(sysconfdir)/grub.d +grublibexecdir = $(libexecdir)/$(grubdirname) platformdir = $(pkglibdir)/$(target_cpu)-$(platform) starfielddir = $(pkgdatadir)/themes/starfield +systemdunitdir = ${prefix}/lib/systemd/system +systemd_logind_service_ddir = $(systemdunitdir)/systemd-logind.service.d CFLAGS_GNULIB = -Wno-undef -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code -Werror=trampolines -fno-trampolines CPPFLAGS_GNULIB = -I$(top_builddir)/grub-core/lib/gnulib -I$(top_srcdir)/grub-core/lib/gnulib @@ -121,6 +124,9 @@ noinst_LIBRARIES = dist_noinst_DATA = platform_SCRIPTS = platform_PROGRAMS = +grublibexec_SCRIPTS = +systemdunit_SCRIPTS = +systemd_logind_service_d_SCRIPTS = TESTS = EXTRA_DIST = diff --git a/util/grub.d/14_menu_show_once.in b/util/grub.d/14_menu_show_once.in new file mode 100755 index 0000000000..1cd7f36142 --- /dev/null +++ b/util/grub.d/14_menu_show_once.in @@ -0,0 +1,13 @@ +#! /bin/sh +# Force the menu to be shown once, with a timeout of ${menu_show_once_timeout} +# if requested by ${menu_show_once_timeout} being set in the env. +cat << EOF +if [ x\$feature_timeout_style = xy ]; then + if [ "\${menu_show_once_timeout}" ]; then + set timeout_style=menu + set timeout="\${menu_show_once_timeout}" + unset menu_show_once_timeout + save_env menu_show_once_timeout + fi +fi +EOF diff --git a/util/systemd/10-grub-logind-service.conf.in b/util/systemd/10-grub-logind-service.conf.in new file mode 100644 index 0000000000..f2d4ac0073 --- /dev/null +++ b/util/systemd/10-grub-logind-service.conf.in @@ -0,0 +1,2 @@ +[Service] +Environment=SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU=true diff --git a/util/systemd/grub-systemd-integration.service.in b/util/systemd/grub-systemd-integration.service.in new file mode 100644 index 0000000000..c81fb594ce --- /dev/null +++ b/util/systemd/grub-systemd-integration.service.in @@ -0,0 +1,8 @@ +[Unit] +Description=Grub2 systemctl reboot --boot-loader-menu=... support +Before=umount.target systemd-reboot.service +DefaultDependencies=no +ConditionPathExists=/run/systemd/reboot-to-boot-loader-menu + +[Service] +ExecStart=@libexecdir@/@grubdirname@/systemd-integration.sh diff --git a/util/systemd/systemd-integration.sh.in b/util/systemd/systemd-integration.sh.in new file mode 100644 index 0000000000..dc1218597b --- /dev/null +++ b/util/systemd/systemd-integration.sh.in @@ -0,0 +1,6 @@ +#!/bin/sh + +TIMEOUT_USEC=$(cat /run/systemd/reboot-to-boot-loader-menu) +TIMEOUT=$(((TIMEOUT_USEC + 500000) / 1000000)) + +@grub_editenv@ - set menu_show_once_timeout=$TIMEOUT From a409d434e2deb57b8890790dbdeca9bc978a879c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 23 Jul 2020 09:27:36 +0200 Subject: [PATCH 142/367] systemd-integration.sh: Also set old menu_show_once grubenv var Downstream RH / Fedora patch for compatibility with old, not (yet) regenerated grub.cfg files which miss the menu_show_once_timeout check. This older grubenv variable leads to a fixed timeout of 60 seconds. Note that the new menu_show_once_timeout will overrule these 60 seconds if both are set and the grub.cfg does have the menu_show_once_timeout check. Signed-off-by: Hans de Goede --- util/systemd/systemd-integration.sh.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/util/systemd/systemd-integration.sh.in b/util/systemd/systemd-integration.sh.in index dc1218597b..a4c071c5b0 100644 --- a/util/systemd/systemd-integration.sh.in +++ b/util/systemd/systemd-integration.sh.in @@ -4,3 +4,8 @@ TIMEOUT_USEC=$(cat /run/systemd/reboot-to-boot-loader-menu) TIMEOUT=$(((TIMEOUT_USEC + 500000) / 1000000)) @grub_editenv@ - set menu_show_once_timeout=$TIMEOUT + +# Downstream RH / Fedora patch for compatibility with old, not (yet) +# regenerated grub.cfg files which miss the menu_show_once_timeout check +# this older grubenv variable leads to a fixed timeout of 60 seconds +@grub_editenv@ - set menu_show_once=1 From 0ca09078a51e698d15022d51209e6c6f2162201a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Thu, 3 Dec 2020 09:13:24 +0100 Subject: [PATCH 143/367] at_keyboard: use set 1 when keyboard is in Translate mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When keyboard controller acts in Translate mode (0x40 mask), then use set 1 since translation is done. Otherwise use the mode queried from the controller (usually set 2). Added "atkeyb" debugging messages in at_keyboard module as well. Resolves: rhbz#1897587 Tested on: - Asus N53SN (set 1 used) - Dell Precision (set 1 used) - HP Elitebook (set 2 used) - HP G5430 (set 1 used, keyboard in XT mode!) - Lenovo P71 & Lenovo T460s (set 2 used) - QEMU/KVM (set 1 used) Signed-off-by: Renaud Métrich --- grub-core/term/at_keyboard.c | 29 ++++++++++++++++++++++++----- include/grub/at_keyboard.h | 4 ++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/grub-core/term/at_keyboard.c b/grub-core/term/at_keyboard.c index 597111077b..2601438260 100644 --- a/grub-core/term/at_keyboard.c +++ b/grub-core/term/at_keyboard.c @@ -135,20 +135,28 @@ query_mode (void) int e; e = write_mode (0); - if (!e) + if (!e) { + grub_dprintf("atkeyb", "query_mode: write_mode(0) failed\n"); return 0; + } do { keyboard_controller_wait_until_ready (); ret = grub_inb (KEYBOARD_REG_DATA); } while (ret == GRUB_AT_ACK); /* QEMU translates the set even in no-translate mode. */ - if (ret == 0x43 || ret == 1) + if (ret == 0x43 || ret == 1) { + grub_dprintf("atkeyb", "query_mode: returning 1 (ret=0x%x)\n", ret); return 1; - if (ret == 0x41 || ret == 2) + } + if (ret == 0x41 || ret == 2) { + grub_dprintf("atkeyb", "query_mode: returning 2 (ret=0x%x)\n", ret); return 2; - if (ret == 0x3f || ret == 3) + } + if (ret == 0x3f || ret == 3) { + grub_dprintf("atkeyb", "query_mode: returning 3 (ret=0x%x)\n", ret); return 3; + } return 0; } @@ -165,7 +173,13 @@ set_scancodes (void) } #if !USE_SCANCODE_SET - ps2_state.current_set = 1; + if ((grub_keyboard_controller_orig & KEYBOARD_AT_TRANSLATE) == KEYBOARD_AT_TRANSLATE) { + grub_dprintf ("atkeyb", "queried set is %d but keyboard in Translate mode, so actually in set 1\n", grub_keyboard_orig_set); + ps2_state.current_set = 1; + } else { + grub_dprintf ("atkeyb", "using queried set %d\n", grub_keyboard_orig_set); + ps2_state.current_set = grub_keyboard_orig_set; + } return; #else @@ -266,6 +280,7 @@ grub_keyboard_controller_init (void) grub_keyboard_orig_set = 2; #else grub_keyboard_controller_orig = grub_keyboard_controller_read (); + grub_dprintf ("atkeyb", "grub_keyboard_controller_orig = 0x%x\n", grub_keyboard_controller_orig); grub_keyboard_orig_set = query_mode (); #endif set_scancodes (); @@ -275,11 +290,15 @@ grub_keyboard_controller_init (void) static grub_err_t grub_keyboard_controller_fini (struct grub_term_input *term __attribute__ ((unused))) { +/* In !USE_SCANCODE_SET mode, we didn't change anything, so nothing to restore */ +#if USE_SCANCODE_SET if (ps2_state.current_set == 0) return GRUB_ERR_NONE; + grub_dprintf ("atkeyb", "restoring set %d, controller 0x%x\n", grub_keyboard_orig_set, grub_keyboard_controller_orig); if (grub_keyboard_orig_set) write_mode (grub_keyboard_orig_set); grub_keyboard_controller_write (grub_keyboard_controller_orig); +#endif return GRUB_ERR_NONE; } diff --git a/include/grub/at_keyboard.h b/include/grub/at_keyboard.h index bcb4d9ba78..9414dc1b99 100644 --- a/include/grub/at_keyboard.h +++ b/include/grub/at_keyboard.h @@ -19,6 +19,10 @@ #ifndef GRUB_AT_KEYBOARD_HEADER #define GRUB_AT_KEYBOARD_HEADER 1 +/* + * Refer to https://wiki.osdev.org/%228042%22_PS/2_Controller for details. + */ + /* Used for sending commands to the controller. */ #define KEYBOARD_COMMAND_ISREADY(x) !((x) & 0x02) #define KEYBOARD_COMMAND_READ 0x20 From 8a95071e7233d74996de867e3cbb11600a6f95e8 Mon Sep 17 00:00:00 2001 From: Jan Hlavac Date: Fri, 20 Nov 2020 23:51:47 +0100 Subject: [PATCH 144/367] grub-install: disable support for EFI platforms For each platform, GRUB is shipped as a kernel image and a set of modules. These files are then used by the grub-install utility to install GRUB on a specific device. However, in order to support UEFI Secure Boot, the resulting EFI binary must be signed by a recognized private key. For this reason, for EFI platforms, most distributions also ship prebuilt EFI binaries signed by a distribution-specific private key. In this case, however, the grub-install utility should not be used because it would overwrite the signed EFI binary. The current fix is suboptimal because it preserves all EFI-related code. A better solution could be to modularize the code and provide a build-time option. Resolves: rhbz#1737444 Signed-off-by: Jan Hlavac [rharwood: drop man page] --- docs/grub.texi | 7 +++++++ util/grub-install.c | 37 ++++++++++++++++--------------------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index 04ed6ac1f0..4870faaa00 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -6509,6 +6509,13 @@ grub2-install @var{install_device} The device name @var{install_device} is an OS device name or a GRUB device name. +In order to support UEFI Secure Boot, the resulting GRUB EFI binary must +be signed by a recognized private key. For this reason, for EFI +platforms, most distributions also ship prebuilt GRUB EFI binaries +signed by a distribution-specific private key. In this case, however, +@command{grub2-install} should not be used because it would overwrite +the signed EFI binary. + @command{grub2-install} accepts the following options: @table @option diff --git a/util/grub-install.c b/util/grub-install.c index a2bec7446c..5babc7af55 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -899,6 +899,22 @@ main (int argc, char *argv[]) platform = grub_install_get_target (grub_install_source_directory); + switch (platform) + { + case GRUB_INSTALL_PLATFORM_ARM_EFI: + case GRUB_INSTALL_PLATFORM_ARM64_EFI: + case GRUB_INSTALL_PLATFORM_I386_EFI: + case GRUB_INSTALL_PLATFORM_IA64_EFI: + case GRUB_INSTALL_PLATFORM_X86_64_EFI: + is_efi = 1; + grub_util_error (_("this utility cannot be used for EFI platforms" + " because it does not support UEFI Secure Boot")); + break; + default: + is_efi = 0; + break; + } + { char *platname = grub_install_get_platform_name (platform); fprintf (stderr, _("Installing for %s platform.\n"), platname); @@ -1011,28 +1027,7 @@ main (int argc, char *argv[]) grub_hostfs_init (); grub_host_init (); - switch (platform) - { - case GRUB_INSTALL_PLATFORM_I386_EFI: - case GRUB_INSTALL_PLATFORM_X86_64_EFI: - case GRUB_INSTALL_PLATFORM_ARM_EFI: - case GRUB_INSTALL_PLATFORM_ARM64_EFI: - case GRUB_INSTALL_PLATFORM_RISCV32_EFI: - case GRUB_INSTALL_PLATFORM_RISCV64_EFI: - case GRUB_INSTALL_PLATFORM_IA64_EFI: - is_efi = 1; - break; - default: - is_efi = 0; - break; - - /* pacify warning. */ - case GRUB_INSTALL_PLATFORM_MAX: - break; - } - /* Find the EFI System Partition. */ - if (is_efi) { grub_fs_t fs; From ac68b2356f64a09d53def9067d24af9d8717d041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Sat, 23 Nov 2019 14:57:41 +0100 Subject: [PATCH 145/367] New --with-debug-timestamps configure flag to prepend debug traces with absolute and relative timestamp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Renaud Métrich --- config.h.in | 1 + configure.ac | 18 ++++++++++++++++++ grub-core/kern/misc.c | 20 ++++++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/config.h.in b/config.h.in index c7e316f0f1..c80e3e0aba 100644 --- a/config.h.in +++ b/config.h.in @@ -12,6 +12,7 @@ /* Define to 1 to enable disk cache statistics. */ #define DISK_CACHE_STATS @DISK_CACHE_STATS@ #define BOOT_TIME_STATS @BOOT_TIME_STATS@ +#define DEBUG_WITH_TIMESTAMPS @DEBUG_WITH_TIMESTAMPS@ /* We don't need those. */ #define MINILZO_CFG_SKIP_LZO_PTR 1 diff --git a/configure.ac b/configure.ac index a02d40a05b..ab0d326f00 100644 --- a/configure.ac +++ b/configure.ac @@ -1585,6 +1585,17 @@ else fi AC_SUBST([BOOT_TIME_STATS]) +AC_ARG_WITH([debug-timestamps], + AS_HELP_STRING([--with-debug-timestamps], + [prepend debug traces with absolute and relative timestamps])) + +if test x$with_debug_timestamps = xyes; then + DEBUG_WITH_TIMESTAMPS=1 +else + DEBUG_WITH_TIMESTAMPS=0 +fi +AC_SUBST([DEBUG_WITH_TIMESTAMPS]) + AC_ARG_ENABLE([grub-emu-sdl], [AS_HELP_STRING([--enable-grub-emu-sdl], [build and install the `grub-emu' debugging utility with SDL support (default=guessed)])]) @@ -2136,6 +2147,7 @@ AM_CONDITIONAL([COND_APPLE_LINKER], [test x$TARGET_APPLE_LINKER = x1]) AM_CONDITIONAL([COND_ENABLE_EFIEMU], [test x$enable_efiemu = xyes]) AM_CONDITIONAL([COND_ENABLE_CACHE_STATS], [test x$DISK_CACHE_STATS = x1]) AM_CONDITIONAL([COND_ENABLE_BOOT_TIME_STATS], [test x$BOOT_TIME_STATS = x1]) +AM_CONDITIONAL([COND_DEBUG_WITH_TIMESTAMPS], [test x$DEBUG_WITH_TIMESTAMPS = x1]) AM_CONDITIONAL([COND_HAVE_CXX], [test x$HAVE_CXX = xyes]) @@ -2231,6 +2243,12 @@ else echo With boot time statistics: No fi +if [ x"$with_debug_timestamps" = xyes ]; then +echo Debug traces with timestamps: Yes +else +echo Debug traces with timestamps: No +fi + if [ x"$efiemu_excuse" = x ]; then echo efiemu runtime: Yes else diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index 578bf51a5f..9f54b6b7d2 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -25,6 +25,9 @@ #include #include #include +#if DEBUG_WITH_TIMESTAMPS +#include +#endif union printf_arg { @@ -192,9 +195,26 @@ grub_real_dprintf (const char *file, const int line, const char *condition, const char *fmt, ...) { va_list args; +#if DEBUG_WITH_TIMESTAMPS + static long unsigned int last_time = 0; + static int last_had_cr = 1; +#endif if (grub_debug_enabled (condition)) { +#if DEBUG_WITH_TIMESTAMPS + /* Don't print timestamp if last printed message isn't terminated yet */ + if (last_had_cr) { + long unsigned int tmabs = (long unsigned int) grub_get_time_ms(); + long unsigned int tmrel = tmabs - last_time; + last_time = tmabs; + grub_printf ("%3lu.%03lus +%2lu.%03lus ", tmabs / 1000, tmabs % 1000, tmrel / 1000, tmrel % 1000); + } + if (fmt[grub_strlen(fmt)-1] == '\n') + last_had_cr = 1; + else + last_had_cr = 0; +#endif grub_printf ("%s:%d: ", file, line); va_start (args, fmt); grub_vprintf (fmt, args); From f9b717627c4817596192c3b3d77beebd5bc59dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Sat, 23 Nov 2019 15:22:16 +0100 Subject: [PATCH 146/367] Added debug statements to grub_disk_open() and grub_disk_close() on success MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Renaud Métrich --- grub-core/kern/disk.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/disk.c b/grub-core/kern/disk.c index e1b0e073e0..05a28ab142 100644 --- a/grub-core/kern/disk.c +++ b/grub-core/kern/disk.c @@ -285,6 +285,8 @@ grub_disk_open (const char *name) return 0; } + grub_dprintf ("disk", "Opening `%s' succeeded.\n", name); + return disk; } @@ -292,7 +294,7 @@ void grub_disk_close (grub_disk_t disk) { grub_partition_t part; - grub_dprintf ("disk", "Closing `%s'.\n", disk->name); + grub_dprintf ("disk", "Closing `%s'...\n", disk->name); if (disk->dev && disk->dev->disk_close) (disk->dev->disk_close) (disk); @@ -306,8 +308,10 @@ grub_disk_close (grub_disk_t disk) grub_free (disk->partition); disk->partition = part; } + grub_dprintf ("disk", "Closing `%s' succeeded.\n", disk->name); grub_free ((void *) disk->name); grub_free (disk); + } /* Small read (less than cache size and not pass across cache unit boundaries). From b84b54c6511f0d80868e65419298517a39eab71f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Mon, 25 Nov 2019 09:29:53 +0100 Subject: [PATCH 147/367] Introduce function grub_debug_is_enabled(void) returning 1 if 'debug' is in the environment and not empty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Renaud Métrich --- grub-core/kern/misc.c | 13 +++++++++++++ include/grub/misc.h | 1 + 2 files changed, 14 insertions(+) diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index 9f54b6b7d2..a186ad3dd4 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -163,6 +163,19 @@ int grub_err_printf (const char *fmt, ...) __attribute__ ((alias("grub_printf"))); #endif +/* Return 1 if 'debug' is set and not empty */ +int +grub_debug_is_enabled (void) +{ + const char *debug; + + debug = grub_env_get ("debug"); + if (!debug || debug[0] == '\0') + return 0; + + return 1; +} + int grub_debug_enabled (const char * condition) { diff --git a/include/grub/misc.h b/include/grub/misc.h index 3adc4036e3..6c4aa85ac5 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -340,6 +340,7 @@ grub_puts (const char *s) } int EXPORT_FUNC(grub_puts_) (const char *s); +int EXPORT_FUNC(grub_debug_is_enabled) (void); int EXPORT_FUNC(grub_debug_enabled) (const char *condition); void EXPORT_FUNC(grub_real_dprintf) (const char *file, const int line, From cfe9f9b2deee5eb4f009c3154a86d7f5c10df5c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Sat, 23 Nov 2019 16:23:54 +0100 Subject: [PATCH 148/367] Don't clear screen when debugging is enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Renaud Métrich [rharwood@redhat.com: rebase fuzz] Signed-off-by: Robbie Harwood --- grub-core/normal/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index af9792c963..7de9e4c36d 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -215,8 +215,9 @@ grub_normal_init_page (struct grub_term_output *term, char *msg_formatted; grub_uint32_t *unicode_msg; grub_uint32_t *last_position; - - grub_term_cls (term); + + if (! grub_debug_is_enabled ()) + grub_term_cls (term); msg_formatted = grub_xasprintf (_("GRUB version %s"), PACKAGE_VERSION); if (!msg_formatted) From 3594ccd988d7ff6d04739005f804d0e644541b5b Mon Sep 17 00:00:00 2001 From: Steve McIntyre Date: Tue, 6 Dec 2022 01:45:11 +0000 Subject: [PATCH 149/367] kern/file: Fix error handling in grub_file_open() grub_file_open() calls grub_file_get_device_name(), but doesn't check the return. Instead, it checks if grub_errno is set. However, nothing initialises grub_errno here when grub_file_open() starts. This means that trying to open one file that doesn't exist and then trying to open another file that does will (incorrectly) also fail to open that second file. Let's fix that. Signed-off-by: Steve McIntyre --- grub-core/kern/file.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c index 58454458c4..5b58f45cfd 100644 --- a/grub-core/kern/file.c +++ b/grub-core/kern/file.c @@ -66,6 +66,9 @@ grub_file_open (const char *name, enum grub_file_type type) const char *file_name; grub_file_filter_id_t filter; + /* Reset grub_errno before we start */ + grub_errno = GRUB_ERR_NONE; + device_name = grub_file_get_device_name (name); if (grub_errno) goto fail; From a780b6ecf7233065209dc9a9ddc971cad2876996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Fri, 29 Nov 2019 11:02:00 +0100 Subject: [PATCH 150/367] grub_file_* instrumentation (new 'file' debug tag) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Renaud Métrich --- grub-core/kern/file.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c index 5b58f45cfd..ec10e54fc0 100644 --- a/grub-core/kern/file.c +++ b/grub-core/kern/file.c @@ -66,6 +66,8 @@ grub_file_open (const char *name, enum grub_file_type type) const char *file_name; grub_file_filter_id_t filter; + grub_dprintf ("file", "Opening `%s' ...\n", name); + /* Reset grub_errno before we start */ grub_errno = GRUB_ERR_NONE; @@ -131,6 +133,8 @@ grub_file_open (const char *name, enum grub_file_type type) if (!file) grub_file_close (last_file); + grub_dprintf ("file", "Opening `%s' succeeded.\n", name); + return file; fail: @@ -141,6 +145,8 @@ grub_file_open (const char *name, enum grub_file_type type) grub_free (file); + grub_dprintf ("file", "Opening `%s' failed.\n", name); + return 0; } @@ -172,6 +178,7 @@ grub_file_read (grub_file_t file, void *buf, grub_size_t len) if (len == 0) return 0; + read_hook = file->read_hook; read_hook_data = file->read_hook_data; if (!file->read_hook) @@ -192,11 +199,18 @@ grub_file_read (grub_file_t file, void *buf, grub_size_t len) grub_err_t grub_file_close (grub_file_t file) { + grub_dprintf ("file", "Closing `%s' ...\n", file->name); if (file->fs->fs_close) (file->fs->fs_close) (file); if (file->device) grub_device_close (file->device); + + if (grub_errno == GRUB_ERR_NONE) + grub_dprintf ("file", "Closing `%s' succeeded.\n", file->name); + else + grub_dprintf ("file", "Closing `%s' failed with %d.\n", file->name, grub_errno); + grub_free (file->name); grub_free (file); return grub_errno; From cee8c5d730af30d7f9ec73bf177cce4db5a82614 Mon Sep 17 00:00:00 2001 From: Diego Domingos Date: Mon, 14 Dec 2020 17:42:45 +0100 Subject: [PATCH 151/367] ieee1275: Avoiding many unecessary open/close Signed-off-by: Diego Domingos --- grub-core/disk/ieee1275/ofdisk.c | 64 +++++++++++++++++--------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/grub-core/disk/ieee1275/ofdisk.c b/grub-core/disk/ieee1275/ofdisk.c index 03674cb477..ea7f78ac7d 100644 --- a/grub-core/disk/ieee1275/ofdisk.c +++ b/grub-core/disk/ieee1275/ofdisk.c @@ -44,7 +44,7 @@ struct ofdisk_hash_ent }; static grub_err_t -grub_ofdisk_get_block_size (const char *device, grub_uint32_t *block_size, +grub_ofdisk_get_block_size (grub_uint32_t *block_size, struct ofdisk_hash_ent *op); #define OFDISK_HASH_SZ 8 @@ -461,6 +461,7 @@ grub_ofdisk_open (const char *name, grub_disk_t disk) grub_ssize_t actual; grub_uint32_t block_size = 0; grub_err_t err; + struct ofdisk_hash_ent *op; if (grub_strncmp (name, "ieee1275/", sizeof ("ieee1275/") - 1) != 0) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, @@ -471,6 +472,35 @@ grub_ofdisk_open (const char *name, grub_disk_t disk) grub_dprintf ("disk", "Opening `%s'.\n", devpath); + op = ofdisk_hash_find (devpath); + if (!op) + op = ofdisk_hash_add (devpath, NULL); + if (!op) + { + grub_free (devpath); + return grub_errno; + } + + /* Check if the call to open is the same to the last disk already opened */ + if (last_devpath && !grub_strcmp(op->open_path,last_devpath)) + { + goto finish; + } + + /* If not, we need to close the previous disk and open the new one */ + else { + if (last_ihandle){ + grub_ieee1275_close (last_ihandle); + } + last_ihandle = 0; + last_devpath = NULL; + + grub_ieee1275_open (op->open_path, &last_ihandle); + if (! last_ihandle) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); + last_devpath = op->open_path; + } + if (grub_ieee1275_finddevice (devpath, &dev)) { grub_free (devpath); @@ -491,25 +521,18 @@ grub_ofdisk_open (const char *name, grub_disk_t disk) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a block device"); } + + finish: /* XXX: There is no property to read the number of blocks. There should be a property `#blocks', but it is not there. Perhaps it is possible to use seek for this. */ disk->total_sectors = GRUB_DISK_SIZE_UNKNOWN; { - struct ofdisk_hash_ent *op; - op = ofdisk_hash_find (devpath); - if (!op) - op = ofdisk_hash_add (devpath, NULL); - if (!op) - { - grub_free (devpath); - return grub_errno; - } disk->id = (unsigned long) op; disk->data = op->open_path; - err = grub_ofdisk_get_block_size (devpath, &block_size, op); + err = grub_ofdisk_get_block_size (&block_size, op); if (err) { grub_free (devpath); @@ -532,13 +555,6 @@ grub_ofdisk_open (const char *name, grub_disk_t disk) static void grub_ofdisk_close (grub_disk_t disk) { - if (disk->data == last_devpath) - { - if (last_ihandle) - grub_ieee1275_close (last_ihandle); - last_ihandle = 0; - last_devpath = NULL; - } disk->data = 0; } @@ -685,7 +701,7 @@ grub_ofdisk_init (void) } static grub_err_t -grub_ofdisk_get_block_size (const char *device, grub_uint32_t *block_size, +grub_ofdisk_get_block_size (grub_uint32_t *block_size, struct ofdisk_hash_ent *op) { struct size_args_ieee1275 @@ -698,16 +714,6 @@ grub_ofdisk_get_block_size (const char *device, grub_uint32_t *block_size, grub_ieee1275_cell_t size2; } args_ieee1275; - if (last_ihandle) - grub_ieee1275_close (last_ihandle); - - last_ihandle = 0; - last_devpath = NULL; - - grub_ieee1275_open (device, &last_ihandle); - if (! last_ihandle) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); - *block_size = 0; if (op->block_size_fails >= 2) From 8506b2fefb3d8730e84c052610cf2ab81b94c64a Mon Sep 17 00:00:00 2001 From: Diego Domingos Date: Mon, 14 Dec 2020 17:45:28 +0100 Subject: [PATCH 152/367] ieee1275/powerpc: implements fibre channel discovery for ofpathname grub-ofpathname doesn't work with fibre channel because there is no function currently implemented for it. This patch enables it by prividing a function that looks for the port name, building the entire path for OF devices. Signed-off-by: Diego Domingos --- grub-core/osdep/linux/ofpath.c | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/grub-core/osdep/linux/ofpath.c b/grub-core/osdep/linux/ofpath.c index a6153d3595..0f5d54e9f2 100644 --- a/grub-core/osdep/linux/ofpath.c +++ b/grub-core/osdep/linux/ofpath.c @@ -350,6 +350,38 @@ of_path_of_ide(const char *sys_devname __attribute__((unused)), const char *devi return ret; } + +static void +of_fc_port_name(const char *path, const char *subpath, char *port_name) +{ + char *bname, *basepath, *p; + int fd; + + bname = xmalloc(sizeof(char)*150); + basepath = xmalloc(strlen(path)); + + /* Generate the path to get port name information from the drive */ + strncpy(basepath,path,subpath-path); + basepath[subpath-path-1] = '\0'; + p = get_basename(basepath); + snprintf(bname,sizeof(char)*150,"%s/fc_transport/%s/port_name",basepath,p); + + /* Read the information from the port name */ + fd = open (bname, O_RDONLY); + if (fd < 0) + grub_util_error (_("cannot open `%s': %s"), bname, strerror (errno)); + + if (read(fd,port_name,sizeof(char)*19) < 0) + grub_util_error (_("cannot read `%s': %s"), bname, strerror (errno)); + + sscanf(port_name,"0x%s",port_name); + + close(fd); + + free(bname); + free(basepath); +} + #ifdef __sparc__ static char * of_path_of_nvme(const char *sys_devname __attribute__((unused)), @@ -577,6 +609,16 @@ of_path_of_scsi(const char *sys_devname __attribute__((unused)), const char *dev digit_string = trailing_digits (device); if (strncmp (of_path, "/vdevice/", sizeof ("/vdevice/") - 1) == 0) { + if(strstr(of_path,"vfc-client")) + { + char * port_name = xmalloc(sizeof(char)*17); + of_fc_port_name(sysfs_path, p, port_name); + + snprintf(disk,sizeof(disk),"/%s@%s", disk_name, port_name); + free(port_name); + } + else + { unsigned long id = 0x8000 | (tgt << 8) | (bus << 5) | lun; if (*digit_string == '\0') { @@ -590,6 +632,13 @@ of_path_of_scsi(const char *sys_devname __attribute__((unused)), const char *dev snprintf(disk, sizeof (disk), "/%s@%04lx000000000000:%c", disk_name, id, 'a' + (part - 1)); } + } + } else if (strstr(of_path,"fibre-channel")||(strstr(of_path,"vfc-client"))){ + char * port_name = xmalloc(sizeof(char)*17); + of_fc_port_name(sysfs_path, p, port_name); + + snprintf(disk,sizeof(disk),"/%s@%s", disk_name, port_name); + free(port_name); } else { From cce9dc566f82bbe885fc989fa21df8b1569ef075 Mon Sep 17 00:00:00 2001 From: Diego Domingos Date: Mon, 14 Dec 2020 17:47:16 +0100 Subject: [PATCH 153/367] ieee1275/powerpc: enables device mapper discovery this patch enables the device mapper discovery on ofpath.c. Currently, when we are dealing with a device like /dev/dm-* the ofpath returns null since there is no function implemented to handle this case. This patch implements a function that will look into /sys/block/dm-* devices and search recursively inside slaves directory to find the root disk. Signed-off-by: Diego Domingos --- grub-core/osdep/linux/ofpath.c | 64 +++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/grub-core/osdep/linux/ofpath.c b/grub-core/osdep/linux/ofpath.c index 0f5d54e9f2..cc849d9c94 100644 --- a/grub-core/osdep/linux/ofpath.c +++ b/grub-core/osdep/linux/ofpath.c @@ -37,6 +37,7 @@ #include #include #include +#include #ifdef __sparc__ typedef enum @@ -755,13 +756,74 @@ strip_trailing_digits (const char *p) return new; } +static char * +get_slave_from_dm(const char * device){ + char *curr_device, *tmp; + char *directory; + char *ret = NULL; + + directory = grub_strdup (device); + tmp = get_basename(directory); + curr_device = grub_strdup (tmp); + *tmp = '\0'; + + /* Recursively check for slaves devices so we can find the root device */ + while ((curr_device[0] == 'd') && (curr_device[1] == 'm') && (curr_device[2] == '-')){ + DIR *dp; + struct dirent *ep; + char* device_path; + + device_path = grub_xasprintf ("/sys/block/%s/slaves", curr_device); + dp = opendir(device_path); + free(device_path); + + if (dp != NULL) + { + ep = readdir (dp); + while (ep != NULL){ + + /* avoid some system directories */ + if (!strcmp(ep->d_name,".")) + goto next_dir; + if (!strcmp(ep->d_name,"..")) + goto next_dir; + + free (curr_device); + free (ret); + curr_device = grub_strdup (ep->d_name); + ret = grub_xasprintf ("%s%s", directory, curr_device); + break; + + next_dir: + ep = readdir (dp); + continue; + } + closedir (dp); + } + else + grub_util_warn (_("cannot open directory `%s'"), device_path); + } + + free (directory); + free (curr_device); + + return ret; +} + char * grub_util_devname_to_ofpath (const char *sys_devname) { - char *name_buf, *device, *devnode, *devicenode, *ofpath; + char *name_buf, *device, *devnode, *devicenode, *ofpath, *realname; name_buf = xrealpath (sys_devname); + realname = get_slave_from_dm (name_buf); + if (realname) + { + free (name_buf); + name_buf = realname; + } + device = get_basename (name_buf); devnode = strip_trailing_digits (name_buf); devicenode = strip_trailing_digits (device); From f94fe889916971904397b7c0367429e71f556a67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Fri, 18 Dec 2020 15:39:26 +0100 Subject: [PATCH 154/367] Add 'at_keyboard_fallback_set' var to force the set manually MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This seems required with HP DL380p Gen 8 systems. Indeed, with this system, we can see the following sequence: 1. controller is queried to get current configuration (returns 0x30 which is quite standard) 2. controller is queried to get the current keyboard set in used, using code 0xf0 (first part) 3. controller answers with 0xfa which means "ACK" (== ok) 4. then we send "0" to tell "we want to know which set your are supporting" 5. controller answers with 0xfa ("ACK") 6. controller should then give us 1, 2, 3 or 0x43, 0x41, 0x3f, but here it gives us 0xfe which means "NACK" Since there seems no way to determine the current set, and in fact the controller expects set2 to be used, we need to rely on an environment variable. Everything has been tested on this system: using 0xFE (resend command), making sure we wait for ACK in the 2 steps "write_mode", etc. Below is litterature I used to come up with "there is no other solution": - https://wiki.osdev.org/%228042%22_PS/2_Controller - http://www-ug.eecg.toronto.edu/msl/nios_devices/datasheets/PS2%20Keyboard%20Protocol.htm - http://www.s100computers.com/My%20System%20Pages/MSDOS%20Board/PC%20Keyboard.pdf Signed-off-by: Renaud Métrich Signed-off-by: Robbie Harwood --- grub-core/term/at_keyboard.c | 121 +++++++++++++++++++++++++++-------- 1 file changed, 96 insertions(+), 25 deletions(-) diff --git a/grub-core/term/at_keyboard.c b/grub-core/term/at_keyboard.c index 2601438260..dac0f946fe 100644 --- a/grub-core/term/at_keyboard.c +++ b/grub-core/term/at_keyboard.c @@ -31,6 +31,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); static grub_uint8_t grub_keyboard_controller_orig; static grub_uint8_t grub_keyboard_orig_set; struct grub_ps2_state ps2_state; +static int fallback_set; static int ping_sent; @@ -76,6 +77,8 @@ at_command (grub_uint8_t data) break; return 0; } + if (i == GRUB_AT_TRIES) + grub_dprintf ("atkeyb", "at_command() timed out! (stopped after %d tries)\n", i); return (i != GRUB_AT_TRIES); } @@ -105,6 +108,21 @@ grub_keyboard_controller_read (void) #endif +static int +resend_last_result (void) +{ + grub_uint8_t ret; + keyboard_controller_wait_until_ready (); + grub_dprintf ("atkeyb", "resend_last_result: sending 0xfe\n"); + grub_outb (0xfe, KEYBOARD_REG_DATA); + ret = wait_ack (); + grub_dprintf ("atkeyb", "resend_last_result: wait_ack() returned 0x%x\n", ret); + keyboard_controller_wait_until_ready (); + ret = grub_inb (KEYBOARD_REG_DATA); + grub_dprintf ("atkeyb", "resend_last_result: read 0x%x from controller\n", ret); + return ret; +} + static int write_mode (int mode) { @@ -113,11 +131,14 @@ write_mode (int mode) { grub_uint8_t ack; keyboard_controller_wait_until_ready (); + grub_dprintf ("atkeyb", "write_mode: sending 0xf0\n"); grub_outb (0xf0, KEYBOARD_REG_DATA); keyboard_controller_wait_until_ready (); + grub_dprintf ("atkeyb", "write_mode: sending mode %d\n", mode); grub_outb (mode, KEYBOARD_REG_DATA); keyboard_controller_wait_until_ready (); ack = wait_ack (); + grub_dprintf ("atkeyb", "write_mode: wait_ack() returned 0x%x\n", ack); if (ack == GRUB_AT_NACK) continue; if (ack == GRUB_AT_ACK) @@ -125,6 +146,9 @@ write_mode (int mode) return 0; } + if (i == GRUB_AT_TRIES) + grub_dprintf ("atkeyb", "write_mode() timed out! (stopped after %d tries)\n", i); + return (i != GRUB_AT_TRIES); } @@ -132,31 +156,66 @@ static int query_mode (void) { grub_uint8_t ret; + grub_uint64_t endtime; + unsigned i; int e; + char *envvar; - e = write_mode (0); - if (!e) { - grub_dprintf("atkeyb", "query_mode: write_mode(0) failed\n"); - return 0; - } + for (i = 0; i < GRUB_AT_TRIES; i++) { + grub_dprintf ("atkeyb", "query_mode: sending command to controller\n"); + e = write_mode (0); + if (!e) { + grub_dprintf ("atkeyb", "query_mode: write_mode(0) failed\n"); + return 0; + } - do { - keyboard_controller_wait_until_ready (); - ret = grub_inb (KEYBOARD_REG_DATA); - } while (ret == GRUB_AT_ACK); - /* QEMU translates the set even in no-translate mode. */ - if (ret == 0x43 || ret == 1) { - grub_dprintf("atkeyb", "query_mode: returning 1 (ret=0x%x)\n", ret); - return 1; - } - if (ret == 0x41 || ret == 2) { - grub_dprintf("atkeyb", "query_mode: returning 2 (ret=0x%x)\n", ret); - return 2; + endtime = grub_get_time_ms () + 20; + do { + keyboard_controller_wait_until_ready (); + ret = grub_inb (KEYBOARD_REG_DATA); + grub_dprintf ("atkeyb", "query_mode/loop: read 0x%x from controller\n", ret); + } while ((ret == GRUB_AT_ACK || ret == GRUB_AT_NACK) && grub_get_time_ms () < endtime); + if (ret == 0xfe) { + grub_dprintf ("atkeyb", "query_mode: asking controller to resend last result\n"); + ret = resend_last_result(); + grub_dprintf ("atkeyb", "query_mode: read 0x%x from controller\n", ret); + } + /* QEMU translates the set even in no-translate mode. */ + if (ret == 0x43 || ret == 1) { + grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 1\n", ret); + return 1; + } + if (ret == 0x41 || ret == 2) { + grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 2\n", ret); + return 2; + } + if (ret == 0x3f || ret == 3) { + grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 3\n", ret); + return 3; + } + grub_dprintf ("atkeyb", "query_mode: controller returned unexpected value 0x%x, retrying\n", ret); } - if (ret == 0x3f || ret == 3) { - grub_dprintf("atkeyb", "query_mode: returning 3 (ret=0x%x)\n", ret); - return 3; + + /* + * Falling here means we tried querying and the controller returned something + * we don't understand, try to use 'at_keyboard_fallback_set' if it exists, + * otherwise return 0. + */ + envvar = grub_env_get ("at_keyboard_fallback_set"); + if (envvar) { + fallback_set = grub_strtoul (envvar, 0, 10); + if ((grub_errno) || (fallback_set < 1) || (fallback_set > 3)) { + grub_dprintf ("atkeyb", "WARNING: ignoring unexpected value '%s' for '%s' variable\n", + envvar, "at_keyboard_fallback_set"); + fallback_set = 0; + } else { + grub_dprintf ("atkeyb", "query_mode: '%s' specified in environment, returning %d\n", + "at_keyboard_fallback_set", fallback_set); + } + return fallback_set; } + grub_dprintf ("atkeyb", "WARNING: no '%s' specified in environment, returning 0\n", + "at_keyboard_fallback_set"); return 0; } @@ -165,14 +224,25 @@ set_scancodes (void) { /* You must have visited computer museum. Keyboard without scancode set knowledge. Assume XT. */ - if (!grub_keyboard_orig_set) - { - grub_dprintf ("atkeyb", "No sets support assumed\n"); - ps2_state.current_set = 1; + if (!grub_keyboard_orig_set) { + if (fallback_set) { + grub_dprintf ("atkeyb", "No sets support assumed but set forced to %d\n", fallback_set); + ps2_state.current_set = fallback_set; return; } + grub_dprintf ("atkeyb", "No sets support assumed, forcing to set 1\n"); + ps2_state.current_set = 1; + return; + } #if !USE_SCANCODE_SET + if (fallback_set) { + grub_dprintf ("atkeyb", "queried set is %d but set forced to %d\n", + grub_keyboard_orig_set, fallback_set); + ps2_state.current_set = fallback_set; + return; + } + if ((grub_keyboard_controller_orig & KEYBOARD_AT_TRANSLATE) == KEYBOARD_AT_TRANSLATE) { grub_dprintf ("atkeyb", "queried set is %d but keyboard in Translate mode, so actually in set 1\n", grub_keyboard_orig_set); ps2_state.current_set = 1; @@ -261,6 +331,7 @@ grub_at_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused))) static void grub_keyboard_controller_init (void) { + grub_dprintf ("atkeyb", "initializing the controller\n"); ps2_state.at_keyboard_status = 0; /* Drain input buffer. */ while (1) @@ -282,6 +353,7 @@ grub_keyboard_controller_init (void) grub_keyboard_controller_orig = grub_keyboard_controller_read (); grub_dprintf ("atkeyb", "grub_keyboard_controller_orig = 0x%x\n", grub_keyboard_controller_orig); grub_keyboard_orig_set = query_mode (); + grub_dprintf ("atkeyb", "grub_keyboard_orig_set = %d\n", grub_keyboard_orig_set); #endif set_scancodes (); keyboard_controller_led (ps2_state.led_status); @@ -329,7 +401,6 @@ grub_at_restore_hw (void) return GRUB_ERR_NONE; } - static struct grub_term_input grub_at_keyboard_term = { .name = "at_keyboard", From b31e0016fe1c47e66cf9f31c3f435d6e631b01b5 Mon Sep 17 00:00:00 2001 From: Rashmica Gupta Date: Thu, 11 Jun 2020 11:26:23 +1000 Subject: [PATCH 155/367] Add suport for signing grub with an appended signature Add infrastructure to allow firmware to verify the integrity of grub by use of a Linux-kernel-module-style appended signature. We initially target powerpc-ieee1275, but the code should be extensible to other platforms. Usually these signatures are appended to a file without modifying the ELF file itself. (This is what the 'sign-file' tool does, for example.) The verifier loads the signed file from the file system and looks at the end of the file for the appended signature. However, on powerpc-ieee1275 platforms, the bootloader is often stored directly in the PReP partition as raw bytes without a file-system. This makes determining the location of an appended signature more difficult. To address this, we add a new ELF note. The name field of shall be the string "Appended-Signature", zero-padded to 4 byte alignment. The type field shall be 0x41536967 (the ASCII values for the string "ASig"). It must be the final section in the ELF binary. The description shall contain the appended signature structure as defined by the Linux kernel. The description will also be padded to be a multiple of 4 bytes. The padding shall be added before the appended signature structure (not at the end) so that the final bytes of a signed ELF file are the appended signature magic. A subsequent patch documents how to create a grub core.img validly signed under this scheme. Signed-off-by: Daniel Axtens Signed-off-by: Rashmica Gupta --- include/grub/util/install.h | 8 ++++++-- include/grub/util/mkimage.h | 4 ++-- util/grub-install-common.c | 18 +++++++++++++---- util/grub-mkimage.c | 15 ++++++++++++-- util/grub-mkimagexx.c | 39 ++++++++++++++++++++++++++++++++++++- util/mkimage.c | 13 +++++++------ 6 files changed, 80 insertions(+), 17 deletions(-) diff --git a/include/grub/util/install.h b/include/grub/util/install.h index 7df3191f47..cf4531e02b 100644 --- a/include/grub/util/install.h +++ b/include/grub/util/install.h @@ -67,6 +67,9 @@ N_("SBAT metadata"), 0 }, \ { "disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, \ N_("disable shim_lock verifier"), 0 }, \ + { "appended-signature-size", GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE,\ + "SIZE", 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), \ + 1}, \ { "verbose", 'v', 0, 0, \ N_("print verbose messages."), 1 } @@ -128,7 +131,8 @@ enum grub_install_options { GRUB_INSTALL_OPTIONS_INSTALL_CORE_COMPRESS, GRUB_INSTALL_OPTIONS_DTB, GRUB_INSTALL_OPTIONS_SBAT, - GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK + GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, + GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE }; extern char *grub_install_source_directory; @@ -188,7 +192,7 @@ grub_install_generate_image (const char *dir, const char *prefix, size_t npubkeys, char *config_path, const struct grub_install_image_target_desc *image_target, - int note, + int note, size_t appsig_size, grub_compression_t comp, const char *dtb_file, const char *sbat_path, const int disable_shim_lock); diff --git a/include/grub/util/mkimage.h b/include/grub/util/mkimage.h index 3819a67441..6f1da89b9b 100644 --- a/include/grub/util/mkimage.h +++ b/include/grub/util/mkimage.h @@ -51,12 +51,12 @@ grub_mkimage_load_image64 (const char *kernel_path, const struct grub_install_image_target_desc *image_target); void grub_mkimage_generate_elf32 (const struct grub_install_image_target_desc *image_target, - int note, char **core_img, size_t *core_size, + int note, size_t appsig_size, char **core_img, size_t *core_size, Elf32_Addr target_addr, struct grub_mkimage_layout *layout); void grub_mkimage_generate_elf64 (const struct grub_install_image_target_desc *image_target, - int note, char **core_img, size_t *core_size, + int note, size_t appsig_size, char **core_img, size_t *core_size, Elf64_Addr target_addr, struct grub_mkimage_layout *layout); diff --git a/util/grub-install-common.c b/util/grub-install-common.c index 4e212e690c..a74fee16e2 100644 --- a/util/grub-install-common.c +++ b/util/grub-install-common.c @@ -461,10 +461,12 @@ static size_t npubkeys; static char *sbat; static int disable_shim_lock; static grub_compression_t compression; +static size_t appsig_size; int grub_install_parse (int key, char *arg) { + const char *end; switch (key) { case 'C': @@ -562,6 +564,12 @@ grub_install_parse (int key, char *arg) grub_util_error (_("Unrecognized compression `%s'"), arg); case GRUB_INSTALL_OPTIONS_GRUB_MKIMAGE: return 1; + case GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE: + grub_errno = 0; + appsig_size = grub_strtol(arg, &end, 10); + if (grub_errno) + return 0; + return 1; default: return 0; } @@ -661,11 +669,13 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, " --output '%s' " " --dtb '%s' " "--sbat '%s' " - "--format '%s' --compression '%s' %s %s %s\n", + "--format '%s' --compression '%s' " + "--appended-signature-size %zu %s %s %s\n", dir, prefix, outname, dtb ? : "", sbat ? : "", mkimage_target, - compnames[compression], note ? "--note" : "", - disable_shim_lock ? "--disable-shim-lock" : "", s); + compnames[compression], appsig_size, + disable_shim_lock ? "--disable-shim-lock" : "", + note ? "--note" : "", s); free (s); tgt = grub_install_get_image_target (mkimage_target); @@ -675,7 +685,7 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, grub_install_generate_image (dir, prefix, fp, outname, modules.entries, memdisk_path, pubkeys, npubkeys, config_path, tgt, - note, compression, dtb, sbat, + note, appsig_size, compression, dtb, sbat, disable_shim_lock); while (dc--) grub_install_pop_module (); diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c index c0d5599370..8a53310548 100644 --- a/util/grub-mkimage.c +++ b/util/grub-mkimage.c @@ -84,6 +84,7 @@ static struct argp_option options[] = { {"sbat", 's', N_("FILE"), 0, N_("SBAT metadata"), 0}, {"disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, N_("disable shim_lock verifier"), 0}, {"verbose", 'v', 0, 0, N_("print verbose messages."), 0}, + {"appended-signature-size", 'S', N_("SIZE"), 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), 0}, { 0, 0, 0, 0, 0, 0 } }; @@ -128,6 +129,7 @@ struct arguments char *sbat; int note; int disable_shim_lock; + size_t appsig_size; const struct grub_install_image_target_desc *image_target; grub_compression_t comp; }; @@ -138,6 +140,7 @@ argp_parser (int key, char *arg, struct argp_state *state) /* Get the input argument from argp_parse, which we know is a pointer to our arguments structure. */ struct arguments *arguments = state->input; + const char* end; switch (key) { @@ -170,6 +173,13 @@ argp_parser (int key, char *arg, struct argp_state *state) arguments->note = 1; break; + case 'S': + grub_errno = 0; + arguments->appsig_size = grub_strtol(arg, &end, 10); + if (grub_errno) + return 0; + break; + case 'm': if (arguments->memdisk) free (arguments->memdisk); @@ -324,8 +334,9 @@ main (int argc, char *argv[]) arguments.memdisk, arguments.pubkeys, arguments.npubkeys, arguments.config, arguments.image_target, arguments.note, - arguments.comp, arguments.dtb, - arguments.sbat, arguments.disable_shim_lock); + arguments.appsig_size, arguments.comp, + arguments.dtb, arguments.sbat, + arguments.disable_shim_lock); if (grub_util_file_sync (fp) < 0) grub_util_error (_("cannot sync `%s': %s"), arguments.output ? : "stdout", diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c index d78fa3e533..393119486d 100644 --- a/util/grub-mkimagexx.c +++ b/util/grub-mkimagexx.c @@ -84,6 +84,15 @@ struct grub_ieee1275_note struct grub_ieee1275_note_desc descriptor; }; +#define GRUB_APPENDED_SIGNATURE_NOTE_NAME "Appended-Signature" +#define GRUB_APPENDED_SIGNATURE_NOTE_TYPE 0x41536967 /* "ASig" */ + +struct grub_appended_signature_note +{ + Elf32_Nhdr header; + char name[ALIGN_UP(sizeof (GRUB_APPENDED_SIGNATURE_NOTE_NAME), 4)]; +}; + #define GRUB_XEN_NOTE_NAME "Xen" struct fixup_block_list @@ -207,7 +216,7 @@ grub_arm_reloc_jump24 (grub_uint32_t *target, Elf32_Addr sym_addr) void SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc *image_target, - int note, char **core_img, size_t *core_size, + int note, size_t appsig_size, char **core_img, size_t *core_size, Elf_Addr target_addr, struct grub_mkimage_layout *layout) { @@ -221,6 +230,12 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc int shnum = 4; int string_size = sizeof (".text") + sizeof ("mods") + 1; + if (appsig_size) + { + phnum++; + footer_size += ALIGN_UP(sizeof (struct grub_appended_signature_note) + appsig_size, 4); + } + if (image_target->id != IMAGE_LOONGSON_ELF) phnum += 2; @@ -484,6 +499,28 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc phdr->p_offset = grub_host_to_target32 (header_size + program_size); } + if (appsig_size) { + int note_size = ALIGN_UP(sizeof (struct grub_appended_signature_note) + appsig_size, 4); + struct grub_appended_signature_note *note_ptr = (struct grub_appended_signature_note *) + (elf_img + program_size + header_size + (note ? sizeof (struct grub_ieee1275_note) : 0)); + + note_ptr->header.n_namesz = grub_host_to_target32 (sizeof (GRUB_APPENDED_SIGNATURE_NOTE_NAME)); + /* needs to sit at the end, so we round this up and sign some zero padding */ + note_ptr->header.n_descsz = grub_host_to_target32 (ALIGN_UP(appsig_size, 4)); + note_ptr->header.n_type = grub_host_to_target32 (GRUB_APPENDED_SIGNATURE_NOTE_TYPE); + strcpy (note_ptr->name, GRUB_APPENDED_SIGNATURE_NOTE_NAME); + + phdr++; + phdr->p_type = grub_host_to_target32 (PT_NOTE); + phdr->p_flags = grub_host_to_target32 (PF_R); + phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof); + phdr->p_vaddr = 0; + phdr->p_paddr = 0; + phdr->p_filesz = grub_host_to_target32 (note_size); + phdr->p_memsz = 0; + phdr->p_offset = grub_host_to_target32 (header_size + program_size + (note ? sizeof (struct grub_ieee1275_note) : 0)); + } + { char *str_start = (elf_img + sizeof (*ehdr) + phnum * sizeof (*phdr) + shnum * sizeof (*shdr)); diff --git a/util/mkimage.c b/util/mkimage.c index a26cf76f72..bab1227601 100644 --- a/util/mkimage.c +++ b/util/mkimage.c @@ -869,8 +869,9 @@ grub_install_generate_image (const char *dir, const char *prefix, char *memdisk_path, char **pubkey_paths, size_t npubkeys, char *config_path, const struct grub_install_image_target_desc *image_target, - int note, grub_compression_t comp, const char *dtb_path, - const char *sbat_path, int disable_shim_lock) + int note, size_t appsig_size, grub_compression_t comp, + const char *dtb_path, const char *sbat_path, + int disable_shim_lock) { char *kernel_img, *core_img; size_t total_module_size, core_size; @@ -1773,11 +1774,11 @@ grub_install_generate_image (const char *dir, const char *prefix, else target_addr = image_target->link_addr; if (image_target->voidp_sizeof == 4) - grub_mkimage_generate_elf32 (image_target, note, &core_img, &core_size, - target_addr, &layout); + grub_mkimage_generate_elf32 (image_target, note, appsig_size, &core_img, + &core_size, target_addr, &layout); else - grub_mkimage_generate_elf64 (image_target, note, &core_img, &core_size, - target_addr, &layout); + grub_mkimage_generate_elf64 (image_target, note, appsig_size, &core_img, + &core_size, target_addr, &layout); } break; } From e99dcf2b82af4fe2854fca87778cc0287522fa36 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Sat, 15 Aug 2020 02:00:57 +1000 Subject: [PATCH 156/367] docs/grub: Document signing grub under UEFI Before adding information about how grub is signed with an appended signature scheme, it's worth adding some information about how it can currently be signed for UEFI. Signed-off-by: Daniel Axtens --- docs/grub.texi | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/grub.texi b/docs/grub.texi index 4870faaa00..365d1d6931 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -5817,6 +5817,7 @@ environment variables and commands are listed in the same order. * Secure Boot Advanced Targeting:: Embedded information for generation number based revocation * Measured Boot:: Measuring boot components * Lockdown:: Lockdown when booting on a secure setup +* Signing GRUB itself:: Ensuring the integrity of the GRUB core image @end menu @node Authentication and authorisation @@ -5895,7 +5896,7 @@ commands. GRUB's @file{core.img} can optionally provide enforcement that all files subsequently read from disk are covered by a valid digital signature. -This document does @strong{not} cover how to ensure that your +This section does @strong{not} cover how to ensure that your platform's firmware (e.g., Coreboot) validates @file{core.img}. If environment variable @code{check_signatures} @@ -6067,6 +6068,25 @@ be restricted and some operations/commands cannot be executed. The @samp{lockdown} variable is set to @samp{y} when the GRUB is locked down. Otherwise it does not exit. +@node Signing GRUB itself +@section Signing GRUB itself + +To ensure a complete secure-boot chain, there must be a way for the code that +loads GRUB to verify the integrity of the core image. + +This is ultimately platform-specific and individual platforms can define their +own mechanisms. However, there are general-purpose mechanisms that can be used +with GRUB. + +@section Signing GRUB for UEFI secure boot + +On UEFI platforms, @file{core.img} is a PE binary. Therefore, it can be signed +with a tool such as @command{pesign} or @command{sbsign}. Refer to the +suggestions in @pxref{UEFI secure boot and shim} to ensure that the final +image works under UEFI secure boot and can maintain the secure-boot chain. It +will also be necessary to enrol the public key used into a relevant firmware +key database. + @node Platform limitations @chapter Platform limitations From 80672b99e623da23c01e63aaf521cf7d73d13587 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Sat, 15 Aug 2020 02:19:36 +1000 Subject: [PATCH 157/367] docs/grub: Document signing grub with an appended signature Signing grub for firmware that verifies an appended signature is a bit fiddly. I don't want people to have to figure it out from scratch so document it here. Signed-off-by: Daniel Axtens --- docs/grub.texi | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/docs/grub.texi b/docs/grub.texi index 365d1d6931..afbde7c1f7 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -6087,6 +6087,48 @@ image works under UEFI secure boot and can maintain the secure-boot chain. It will also be necessary to enrol the public key used into a relevant firmware key database. +@section Signing GRUB with an appended signature + +The @file{core.elf} itself can be signed with a Linux kernel module-style +appended signature. + +To support IEEE1275 platforms where the boot image is often loaded directly +from a disk partition rather than from a file system, the @file{core.elf} +can specify the size and location of the appended signature with an ELF +note added by @command{grub-install}. + +An image can be signed this way using the @command{sign-file} command from +the Linux kernel: + +@example +@group +# grub.key is your private key and certificate.der is your public key + +# Determine the size of the appended signature. It depends on the signing +# certificate and the hash algorithm +touch empty +sign-file SHA256 grub.key certificate.der empty empty.sig +SIG_SIZE=`stat -c '%s' empty.sig` +rm empty empty.sig + +# Build a grub image with $SIG_SIZE reserved for the signature +grub-install --appended-signature-size $SIG_SIZE --modules="..." ... + +# Replace the reserved size with a signature: +# cut off the last $SIG_SIZE bytes with truncate's minus modifier +truncate -s -$SIG_SIZE /boot/grub/powerpc-ieee1275/core.elf core.elf.unsigned +# sign the trimmed file with an appended signature, restoring the correct size +sign-file SHA256 grub.key certificate.der core.elf.unsigned core.elf.signed + +# Don't forget to install the signed image as required +# (e.g. on powerpc-ieee1275, to the PReP partition) +@end group +@end example + +As with UEFI secure boot, it is necessary to build in the required modules, +or sign them separately. + + @node Platform limitations @chapter Platform limitations From 552274c70b3c43fc81be01ce3c7b7c7eb2883768 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Thu, 30 Jul 2020 00:13:21 +1000 Subject: [PATCH 158/367] dl: provide a fake grub_dl_set_persistent for the emu target Trying to start grub-emu with a module that calls grub_dl_set_persistent will crash because grub-emu fakes modules and passes NULL to the module init function. Provide an empty function for the emu case. Fixes: ee7808e2197c (dl: Add support for persistent modules) Signed-off-by: Daniel Axtens --- include/grub/dl.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/grub/dl.h b/include/grub/dl.h index 2f76e6b043..20d870f2a4 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -245,11 +245,22 @@ grub_dl_get (const char *name) return 0; } +#ifdef GRUB_MACHINE_EMU +/* + * Under grub-emu, modules are faked and NULL is passed to GRUB_MOD_INIT. + * So we fake this out to avoid a NULL deref. + */ +static inline void +grub_dl_set_persistent (grub_dl_t mod __attribute__((unused))) +{ +} +#else static inline void grub_dl_set_persistent (grub_dl_t mod) { mod->persistent = 1; } +#endif static inline int grub_dl_is_persistent (grub_dl_t mod) From 8e913d53e36f45e0b807a4b16910efe53d524710 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Thu, 1 Oct 2020 20:23:48 +1000 Subject: [PATCH 159/367] pgp: factor out rsa_pad rsa_pad does the PKCS#1 v1.5 padding for the RSA signature scheme. We want to use it in other RSA signature verification applications. I considered and rejected putting it in lib/crypto.c. That file doesn't currently require any MPI functions, but rsa_pad does. That's not so much of a problem for the grub kernel and modules, but crypto.c also gets built into all the grub utilities. So - despite the utils not using any asymmetric ciphers - we would need to built the entire MPI infrastructure in to them. A better and simpler solution is just to spin rsa_pad out into its own PKCS#1 v1.5 module. Signed-off-by: Daniel Axtens --- grub-core/Makefile.core.def | 8 +++++ grub-core/commands/pgp.c | 28 ++---------------- grub-core/lib/pkcs1_v15.c | 59 +++++++++++++++++++++++++++++++++++++ include/grub/pkcs1_v15.h | 27 +++++++++++++++++ 4 files changed, 96 insertions(+), 26 deletions(-) create mode 100644 grub-core/lib/pkcs1_v15.c create mode 100644 include/grub/pkcs1_v15.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index dc9fea6f44..64cc758835 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2511,6 +2511,14 @@ module = { cppflags = '$(CPPFLAGS_GCRY)'; }; +module = { + name = pkcs1_v15; + common = lib/pkcs1_v15.c; + + cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls -Wno-sign-compare'; + cppflags = '$(CPPFLAGS_GCRY)'; +}; + module = { name = all_video; common = lib/fake_module.c; diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c index 5daa1e9d00..2408db4994 100644 --- a/grub-core/commands/pgp.c +++ b/grub-core/commands/pgp.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -411,32 +412,7 @@ static int rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, const gcry_md_spec_t *hash, struct grub_public_subkey *sk) { - grub_size_t tlen, emlen, fflen; - grub_uint8_t *em, *emptr; - unsigned nbits = gcry_mpi_get_nbits (sk->mpis[0]); - int ret; - tlen = hash->mdlen + hash->asnlen; - emlen = (nbits + 7) / 8; - if (emlen < tlen + 11) - return 1; - - em = grub_malloc (emlen); - if (!em) - return 1; - - em[0] = 0x00; - em[1] = 0x01; - fflen = emlen - tlen - 3; - for (emptr = em + 2; emptr < em + 2 + fflen; emptr++) - *emptr = 0xff; - *emptr++ = 0x00; - grub_memcpy (emptr, hash->asnoid, hash->asnlen); - emptr += hash->asnlen; - grub_memcpy (emptr, hval, hash->mdlen); - - ret = gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, em, emlen, 0); - grub_free (em); - return ret; + return grub_crypto_rsa_pad(hmpi, hval, hash, sk->mpis[0]); } struct grub_pubkey_context diff --git a/grub-core/lib/pkcs1_v15.c b/grub-core/lib/pkcs1_v15.c new file mode 100644 index 0000000000..dbacd563d0 --- /dev/null +++ b/grub-core/lib/pkcs1_v15.c @@ -0,0 +1,59 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* + * Given a hash value 'hval', of hash specification 'hash', perform + * the EMSA-PKCS1-v1_5 padding suitable for a key with modulus 'mod' + * (see RFC 8017 s 9.2) and place the result in 'hmpi'. + */ +gcry_err_code_t +grub_crypto_rsa_pad (gcry_mpi_t * hmpi, grub_uint8_t * hval, + const gcry_md_spec_t * hash, gcry_mpi_t mod) +{ + grub_size_t tlen, emlen, fflen; + grub_uint8_t *em, *emptr; + unsigned nbits = gcry_mpi_get_nbits (mod); + int ret; + tlen = hash->mdlen + hash->asnlen; + emlen = (nbits + 7) / 8; + if (emlen < tlen + 11) + return GPG_ERR_TOO_SHORT; + + em = grub_malloc (emlen); + if (!em) + return 1; + + em[0] = 0x00; + em[1] = 0x01; + fflen = emlen - tlen - 3; + for (emptr = em + 2; emptr < em + 2 + fflen; emptr++) + *emptr = 0xff; + *emptr++ = 0x00; + grub_memcpy (emptr, hash->asnoid, hash->asnlen); + emptr += hash->asnlen; + grub_memcpy (emptr, hval, hash->mdlen); + + ret = gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, em, emlen, 0); + grub_free (em); + return ret; +} diff --git a/include/grub/pkcs1_v15.h b/include/grub/pkcs1_v15.h new file mode 100644 index 0000000000..5c338c84a1 --- /dev/null +++ b/include/grub/pkcs1_v15.h @@ -0,0 +1,27 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +/* + * Given a hash value 'hval', of hash specification 'hash', perform + * the EMSA-PKCS1-v1_5 padding suitable for a key with modulus 'mod' + * (See RFC 8017 s 9.2) + */ +gcry_err_code_t +grub_crypto_rsa_pad (gcry_mpi_t * hmpi, grub_uint8_t * hval, + const gcry_md_spec_t * hash, gcry_mpi_t mod); + From a9b8125bd0fcb5ba8378c75d63eb43a0b650c80c Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 2 Oct 2020 10:49:26 +1000 Subject: [PATCH 160/367] crypto: move storage for grub_crypto_pk_* to crypto.c The way gcry_rsa and friends (the asymmetric ciphers) are loaded for the pgp module is a bit quirky. include/grub/crypto.h contains: extern struct gcry_pk_spec *grub_crypto_pk_rsa; commands/pgp.c contains the actual storage: struct gcry_pk_spec *grub_crypto_pk_rsa; And the module itself saves to the storage in pgp.c: GRUB_MOD_INIT(gcry_rsa) { grub_crypto_pk_rsa = &_gcry_pubkey_spec_rsa; } This is annoying: gcry_rsa now has a dependency on pgp! We want to be able to bring in gcry_rsa without bringing in PGP, so move the storage to crypto.c. Previously, gcry_rsa depended on pgp and mpi. Now it depends on crypto and mpi. As pgp depends on crypto, this doesn't add any new module dependencies using the PGP verfier. [FWIW, the story is different for the symmetric ciphers. cryptodisk and friends (zfs encryption etc) use grub_crypto_lookup_cipher_by_name() to get a cipher handle. That depends on grub_ciphers being populated by people calling grub_cipher_register. import_gcry.py ensures that the symmetric ciphers call it.] Signed-off-by: Daniel Axtens --- grub-core/commands/pgp.c | 4 ---- grub-core/lib/crypto.c | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c index 2408db4994..355a43844a 100644 --- a/grub-core/commands/pgp.c +++ b/grub-core/commands/pgp.c @@ -147,10 +147,6 @@ const char *hashes[] = { [0x0b] = "sha224" }; -struct gcry_pk_spec *grub_crypto_pk_dsa; -struct gcry_pk_spec *grub_crypto_pk_ecdsa; -struct gcry_pk_spec *grub_crypto_pk_rsa; - static int dsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, const gcry_md_spec_t *hash, struct grub_public_subkey *sk); diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c index ca334d5a40..c578128a59 100644 --- a/grub-core/lib/crypto.c +++ b/grub-core/lib/crypto.c @@ -121,6 +121,10 @@ grub_md_unregister (gcry_md_spec_t *cipher) } } +struct gcry_pk_spec *grub_crypto_pk_dsa; +struct gcry_pk_spec *grub_crypto_pk_ecdsa; +struct gcry_pk_spec *grub_crypto_pk_rsa; + void grub_crypto_hash (const gcry_md_spec_t *hash, void *out, const void *in, grub_size_t inlen) From c6712755454c09962d1f7529231b0ec9a5fa7159 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Sat, 2 May 2020 00:27:57 +1000 Subject: [PATCH 161/367] posix_wrap: tweaks in preparation for libtasn1 - Define SIZEOF_UNSIGNED_LONG_INT, it's the same as SIZEOF_UNSIGNED_LONG. - Define WORD_BIT, the size in bits of an int. This is a defined in the Single Unix Specification and in gnulib's limits.h. gnulib assumes it's 32 bits on all our platforms, including 64 bit platforms, so we also use that value. - Provide strto[u]l[l] preprocessor macros that resolve to grub_strto[u]l[l]. To avoid gcrypt redefining strtoul, we also define HAVE_STRTOUL here. Signed-off-by: Daniel Axtens --- grub-core/lib/posix_wrap/limits.h | 1 + grub-core/lib/posix_wrap/stdlib.h | 8 ++++++++ grub-core/lib/posix_wrap/sys/types.h | 1 + 3 files changed, 10 insertions(+) diff --git a/grub-core/lib/posix_wrap/limits.h b/grub-core/lib/posix_wrap/limits.h index 7217138ffd..591dbf3289 100644 --- a/grub-core/lib/posix_wrap/limits.h +++ b/grub-core/lib/posix_wrap/limits.h @@ -37,5 +37,6 @@ #define LONG_MAX GRUB_LONG_MAX #define CHAR_BIT 8 +#define WORD_BIT 32 #endif diff --git a/grub-core/lib/posix_wrap/stdlib.h b/grub-core/lib/posix_wrap/stdlib.h index 7a8d385e97..4634db09f2 100644 --- a/grub-core/lib/posix_wrap/stdlib.h +++ b/grub-core/lib/posix_wrap/stdlib.h @@ -58,4 +58,12 @@ abs (int c) return (c >= 0) ? c : -c; } +#define strtol grub_strtol + +/* for libgcrypt */ +#define HAVE_STRTOUL +#define strtoul grub_strtoul + +#define strtoull grub_strtoull + #endif diff --git a/grub-core/lib/posix_wrap/sys/types.h b/grub-core/lib/posix_wrap/sys/types.h index 854eb0122e..f63412c8da 100644 --- a/grub-core/lib/posix_wrap/sys/types.h +++ b/grub-core/lib/posix_wrap/sys/types.h @@ -51,6 +51,7 @@ typedef grub_uint8_t byte; typedef grub_addr_t uintptr_t; #define SIZEOF_UNSIGNED_LONG GRUB_CPU_SIZEOF_LONG +#define SIZEOF_UNSIGNED_LONG_INT GRUB_CPU_SIZEOF_LONG #define SIZEOF_UNSIGNED_INT 4 #define SIZEOF_UNSIGNED_LONG_LONG 8 #define SIZEOF_UNSIGNED_SHORT 2 From 24714df2607172327bb30137aacbc2801e0d80cb Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Wed, 10 Jun 2020 16:31:22 +1000 Subject: [PATCH 162/367] libtasn1: import libtasn1-4.16.0 Import a very trimmed-down set of libtasn1 files: pushd /tmp wget https://ftp.gnu.org/gnu/libtasn1/libtasn1-4.16.0.tar.gz popd pushd grub-core/lib mkdir libtasn1 cp /tmp/libtasn1-4.16.0/{README.md,LICENSE} libtasn1/ mkdir libtasn1/lib cp /tmp/libtasn1-4.16.0/lib/{coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h} libtasn1/lib cp /tmp/libtasn1-4.16.0/lib/includes/libtasn1.h ../../include/grub/ git add libtasn1/ ../../include/grub/libtasn1.h popd Signed-off-by: Daniel Axtens --- grub-core/lib/libtasn1/LICENSE | 16 + grub-core/lib/libtasn1/README.md | 91 + grub-core/lib/libtasn1/lib/coding.c | 1415 +++++++++++++ grub-core/lib/libtasn1/lib/decoding.c | 2478 +++++++++++++++++++++++ grub-core/lib/libtasn1/lib/element.c | 1111 ++++++++++ grub-core/lib/libtasn1/lib/element.h | 40 + grub-core/lib/libtasn1/lib/errors.c | 100 + grub-core/lib/libtasn1/lib/gstr.c | 74 + grub-core/lib/libtasn1/lib/gstr.h | 47 + grub-core/lib/libtasn1/lib/int.h | 221 ++ grub-core/lib/libtasn1/lib/parser_aux.c | 1173 +++++++++++ grub-core/lib/libtasn1/lib/parser_aux.h | 172 ++ grub-core/lib/libtasn1/lib/structure.c | 1220 +++++++++++ grub-core/lib/libtasn1/lib/structure.h | 45 + include/grub/libtasn1.h | 588 ++++++ 15 files changed, 8791 insertions(+) create mode 100644 grub-core/lib/libtasn1/LICENSE create mode 100644 grub-core/lib/libtasn1/README.md create mode 100644 grub-core/lib/libtasn1/lib/coding.c create mode 100644 grub-core/lib/libtasn1/lib/decoding.c create mode 100644 grub-core/lib/libtasn1/lib/element.c create mode 100644 grub-core/lib/libtasn1/lib/element.h create mode 100644 grub-core/lib/libtasn1/lib/errors.c create mode 100644 grub-core/lib/libtasn1/lib/gstr.c create mode 100644 grub-core/lib/libtasn1/lib/gstr.h create mode 100644 grub-core/lib/libtasn1/lib/int.h create mode 100644 grub-core/lib/libtasn1/lib/parser_aux.c create mode 100644 grub-core/lib/libtasn1/lib/parser_aux.h create mode 100644 grub-core/lib/libtasn1/lib/structure.c create mode 100644 grub-core/lib/libtasn1/lib/structure.h create mode 100644 include/grub/libtasn1.h diff --git a/grub-core/lib/libtasn1/LICENSE b/grub-core/lib/libtasn1/LICENSE new file mode 100644 index 0000000000..e8b3628db9 --- /dev/null +++ b/grub-core/lib/libtasn1/LICENSE @@ -0,0 +1,16 @@ +LICENSING +========= + +The libtasn1 library is released under the GNU Lesser General Public +License (LGPL) version 2.1 or later; see [COPYING.LESSER](doc/COPYING.LESSER) +for the license terms. + +The GNU LGPL applies to the main libtasn1 library, while the +included applications library are under the GNU GPL version 3. +The libtasn1 library is located in the lib directory, while the applications +in src/. + +The documentation in doc/ is under the GNU FDL license 1.3. + +For any copyright year range specified as YYYY-ZZZZ in this package +note that the range specifies every single year in that closed interval. diff --git a/grub-core/lib/libtasn1/README.md b/grub-core/lib/libtasn1/README.md new file mode 100644 index 0000000000..50a8642296 --- /dev/null +++ b/grub-core/lib/libtasn1/README.md @@ -0,0 +1,91 @@ +|Branch|CI system|Status| +|:----:|:-------:|-----:| +|Master|Gitlab|[![build status](https://gitlab.com/gnutls/libtasn1/badges/master/pipeline.svg)](https://gitlab.com/gnutls/libtasn1/commits/master)[![coverage report](https://gitlab.com/gnutls/libtasn1/badges/master/coverage.svg)](https://gnutls.gitlab.io/libtasn1/coverage)| + +# libtasn1 + +This is GNU Libtasn1, a small ASN.1 library. + +The C library (libtasn1.*) is licensed under the GNU Lesser General +Public License version 2.1 or later. See the file COPYING.LIB. + +The command line tool, self tests, examples, and other auxilliary +files, are licensed under the GNU General Public License version 3.0 +or later. See the file COPYING. + +## Building the library + +We require several tools to build the software, including: + +* [Make](https://www.gnu.org/software/make/) +* [Automake](https://www.gnu.org/software/automake/) (use 1.11.3 or later) +* [Autoconf](https://www.gnu.org/software/autoconf/) +* [Libtool](https://www.gnu.org/software/libtool/) +* [Texinfo](https://www.gnu.org/software/texinfo/) +* [help2man](http://www.gnu.org/software/help2man/) +* [Tar](https://www.gnu.org/software/tar/) +* [Gzip](https://www.gnu.org/software/gzip/) +* [bison](https://www.gnu.org/software/bison/) +* [Texlive & epsf](https://www.tug.org/texlive/) (for PDF manual) +* [GTK-DOC](https://www.gtk.org/gtk-doc/) (for API manual) +* [Git](https://git-scm.com/) +* [libabigail](https://pagure.io/libabigail/) (for abi comparison in make dist) +* [Valgrind](https://valgrind.org/) (optional) + +The required software is typically distributed with your operating +system, and the instructions for installing them differ. Here are +some hints: + +gNewSense/Debian/Ubuntu: +``` +sudo apt-get install make git-core autoconf automake libtool +sudo apt-get install texinfo texlive texlive-generic-recommended texlive-extra-utils +sudo apt-get install help2man gtk-doc-tools valgrind abigail-tools +``` + +The next step is to run autoreconf, ./configure, etc: + +``` +$ ./bootstrap +``` + +Then build the project normally: + +``` +$ make +$ make check +``` + +Happy hacking! + + +## Manual + +The manual is in the `doc/` directory of the release. You can also browse +the manual online at: + + - https://gnutls.gitlab.io/libtasn1/ + + +## Code coverage report + +The coverage report is at: + + - https://gnutls.gitlab.io/libtasn1/coverage + + +## Issue trackers + + - [Main issue tracker](https://gitlab.com/gnutls/libtasn1/issues) + - [oss-fuzz found issues](https://bugs.chromium.org/p/oss-fuzz/issues/list?q=libtasn1&can=2) + + +## Homepage + +The project homepage at the gnu site is at: + +http://www.gnu.org/software/libtasn1/ + + +For any copyright year range specified as YYYY-ZZZZ in this package +note that the range specifies every single year in that closed interval. diff --git a/grub-core/lib/libtasn1/lib/coding.c b/grub-core/lib/libtasn1/lib/coding.c new file mode 100644 index 0000000000..245ea64cf0 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/coding.c @@ -0,0 +1,1415 @@ +/* + * Copyright (C) 2002-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/*****************************************************/ +/* File: coding.c */ +/* Description: Functions to create a DER coding of */ +/* an ASN1 type. */ +/*****************************************************/ + +#include +#include "parser_aux.h" +#include +#include "element.h" +#include "minmax.h" +#include + +#define MAX_TAG_LEN 16 + +/******************************************************/ +/* Function : _asn1_error_description_value_not_found */ +/* Description: creates the ErrorDescription string */ +/* for the ASN1_VALUE_NOT_FOUND error. */ +/* Parameters: */ +/* node: node of the tree where the value is NULL. */ +/* ErrorDescription: string returned. */ +/* Return: */ +/******************************************************/ +static void +_asn1_error_description_value_not_found (asn1_node node, + char *ErrorDescription) +{ + + if (ErrorDescription == NULL) + return; + + Estrcpy (ErrorDescription, ":: value of element '"); + _asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription), + ASN1_MAX_ERROR_DESCRIPTION_SIZE - 40); + Estrcat (ErrorDescription, "' not found"); + +} + +/** + * asn1_length_der: + * @len: value to convert. + * @der: buffer to hold the returned encoding (may be %NULL). + * @der_len: number of meaningful bytes of ANS (der[0]..der[der_len-1]). + * + * Creates the DER encoding of the provided length value. + * The @der buffer must have enough room for the output. The maximum + * length this function will encode is %ASN1_MAX_LENGTH_SIZE. + * + * To know the size of the DER encoding use a %NULL value for @der. + **/ +void +asn1_length_der (unsigned long int len, unsigned char *der, int *der_len) +{ + int k; + unsigned char temp[ASN1_MAX_LENGTH_SIZE]; +#if SIZEOF_UNSIGNED_LONG_INT > 8 + len &= 0xFFFFFFFFFFFFFFFF; +#endif + + if (len < 128) + { + /* short form */ + if (der != NULL) + der[0] = (unsigned char) len; + *der_len = 1; + } + else + { + /* Long form */ + k = 0; + while (len) + { + temp[k++] = len & 0xFF; + len = len >> 8; + } + *der_len = k + 1; + if (der != NULL) + { + der[0] = ((unsigned char) k & 0x7F) + 128; + while (k--) + der[*der_len - 1 - k] = temp[k]; + } + } +} + +/******************************************************/ +/* Function : _asn1_tag_der */ +/* Description: creates the DER coding for the CLASS */ +/* and TAG parameters. */ +/* It is limited by the ASN1_MAX_TAG_SIZE variable */ +/* Parameters: */ +/* class: value to convert. */ +/* tag_value: value to convert. */ +/* ans: string returned. */ +/* ans_len: number of meaningful bytes of ANS */ +/* (ans[0]..ans[ans_len-1]). */ +/* Return: */ +/******************************************************/ +static void +_asn1_tag_der (unsigned char class, unsigned int tag_value, + unsigned char ans[ASN1_MAX_TAG_SIZE], int *ans_len) +{ + int k; + unsigned char temp[ASN1_MAX_TAG_SIZE]; + + if (tag_value < 31) + { + /* short form */ + ans[0] = (class & 0xE0) + ((unsigned char) (tag_value & 0x1F)); + *ans_len = 1; + } + else + { + /* Long form */ + ans[0] = (class & 0xE0) + 31; + k = 0; + while (tag_value != 0) + { + temp[k++] = tag_value & 0x7F; + tag_value >>= 7; + + if (k > ASN1_MAX_TAG_SIZE - 1) + break; /* will not encode larger tags */ + } + *ans_len = k + 1; + while (k--) + ans[*ans_len - 1 - k] = temp[k] + 128; + ans[*ans_len - 1] -= 128; + } +} + +/** + * asn1_octet_der: + * @str: the input data. + * @str_len: STR length (str[0]..str[*str_len-1]). + * @der: encoded string returned. + * @der_len: number of meaningful bytes of DER (der[0]..der[der_len-1]). + * + * Creates a length-value DER encoding for the input data. + * The DER encoding of the input data will be placed in the @der variable. + * + * Note that the OCTET STRING tag is not included in the output. + * + * This function does not return any value because it is expected + * that @der_len will contain enough bytes to store the string + * plus the DER encoding. The DER encoding size can be obtained using + * asn1_length_der(). + **/ +void +asn1_octet_der (const unsigned char *str, int str_len, + unsigned char *der, int *der_len) +{ + int len_len; + + if (der == NULL || str_len < 0) + return; + + asn1_length_der (str_len, der, &len_len); + memcpy (der + len_len, str, str_len); + *der_len = str_len + len_len; +} + + +/** + * asn1_encode_simple_der: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @str: the string data. + * @str_len: the string length + * @tl: the encoded tag and length + * @tl_len: the bytes of the @tl field + * + * Creates the DER encoding for various simple ASN.1 types like strings etc. + * It stores the tag and length in @tl, which should have space for at least + * %ASN1_MAX_TL_SIZE bytes. Initially @tl_len should contain the size of @tl. + * + * The complete DER encoding should consist of the value in @tl appended + * with the provided @str. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + **/ +int +asn1_encode_simple_der (unsigned int etype, const unsigned char *str, + unsigned int str_len, unsigned char *tl, + unsigned int *tl_len) +{ + int tag_len, len_len; + unsigned tlen; + unsigned char der_tag[ASN1_MAX_TAG_SIZE]; + unsigned char der_length[ASN1_MAX_LENGTH_SIZE]; + unsigned char *p; + + if (str == NULL) + return ASN1_VALUE_NOT_VALID; + + if (ETYPE_OK (etype) == 0) + return ASN1_VALUE_NOT_VALID; + + /* doesn't handle constructed classes */ + if (ETYPE_CLASS (etype) != ASN1_CLASS_UNIVERSAL) + return ASN1_VALUE_NOT_VALID; + + _asn1_tag_der (ETYPE_CLASS (etype), ETYPE_TAG (etype), der_tag, &tag_len); + + asn1_length_der (str_len, der_length, &len_len); + + if (tag_len <= 0 || len_len <= 0) + return ASN1_VALUE_NOT_VALID; + + tlen = tag_len + len_len; + + if (*tl_len < tlen) + return ASN1_MEM_ERROR; + + p = tl; + memcpy (p, der_tag, tag_len); + p += tag_len; + memcpy (p, der_length, len_len); + + *tl_len = tlen; + + return ASN1_SUCCESS; +} + +/******************************************************/ +/* Function : _asn1_time_der */ +/* Description: creates the DER coding for a TIME */ +/* type (length included). */ +/* Parameters: */ +/* str: TIME null-terminated string. */ +/* der: string returned. */ +/* der_len: number of meaningful bytes of DER */ +/* (der[0]..der[ans_len-1]). Initially it */ +/* if must store the lenght of DER. */ +/* Return: */ +/* ASN1_MEM_ERROR when DER isn't big enough */ +/* ASN1_SUCCESS otherwise */ +/******************************************************/ +static int +_asn1_time_der (unsigned char *str, int str_len, unsigned char *der, + int *der_len) +{ + int len_len; + int max_len; + + if (der == NULL) + return ASN1_VALUE_NOT_VALID; + + max_len = *der_len; + + asn1_length_der (str_len, (max_len > 0) ? der : NULL, &len_len); + + if ((len_len + str_len) <= max_len) + memcpy (der + len_len, str, str_len); + *der_len = len_len + str_len; + + if ((*der_len) > max_len) + return ASN1_MEM_ERROR; + + return ASN1_SUCCESS; +} + + +/* +void +_asn1_get_utctime_der(unsigned char *der,int *der_len,unsigned char *str) +{ + int len_len,str_len; + char temp[20]; + + if(str==NULL) return; + str_len=asn1_get_length_der(der,*der_len,&len_len); + if (str_len<0) return; + memcpy(temp,der+len_len,str_len); + *der_len=str_len+len_len; + switch(str_len) + { + case 11: + temp[10]=0; + strcat(temp,"00+0000"); + break; + case 13: + temp[12]=0; + strcat(temp,"+0000"); + break; + case 15: + temp[15]=0; + memmove(temp+12,temp+10,6); + temp[10]=temp[11]='0'; + break; + case 17: + temp[17]=0; + break; + default: + return; + } + strcpy(str,temp); +} +*/ + +static +void encode_val(uint64_t val, unsigned char *der, int max_len, int *der_len) +{ + int first, k; + unsigned char bit7; + + first = 0; + for (k = sizeof(val); k >= 0; k--) + { + bit7 = (val >> (k * 7)) & 0x7F; + if (bit7 || first || !k) + { + if (k) + bit7 |= 0x80; + if (max_len > (*der_len)) + der[*der_len] = bit7; + (*der_len)++; + first = 1; + } + } +} + +/******************************************************/ +/* Function : _asn1_object_id_der */ +/* Description: creates the DER coding for an */ +/* OBJECT IDENTIFIER type (length included). */ +/* Parameters: */ +/* str: OBJECT IDENTIFIER null-terminated string. */ +/* der: string returned. */ +/* der_len: number of meaningful bytes of DER */ +/* (der[0]..der[ans_len-1]). Initially it */ +/* must store the length of DER. */ +/* Return: */ +/* ASN1_MEM_ERROR when DER isn't big enough */ +/* ASN1_SUCCESS if succesful */ +/* or an error value. */ +/******************************************************/ +static int +_asn1_object_id_der (const char *str, unsigned char *der, int *der_len) +{ + int len_len, counter, max_len; + char *temp, *n_end, *n_start; + uint64_t val, val1 = 0; + int str_len = _asn1_strlen (str); + + max_len = *der_len; + *der_len = 0; + + if (der == NULL && max_len > 0) + return ASN1_VALUE_NOT_VALID; + + temp = malloc (str_len + 2); + if (temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + memcpy (temp, str, str_len); + temp[str_len] = '.'; + temp[str_len + 1] = 0; + + counter = 0; + n_start = temp; + while ((n_end = strchr (n_start, '.'))) + { + *n_end = 0; + val = _asn1_strtou64 (n_start, NULL, 10); + counter++; + + if (counter == 1) + { + val1 = val; + } + else if (counter == 2) + { + uint64_t val0; + + if (val1 > 2) + { + free(temp); + return ASN1_VALUE_NOT_VALID; + } + else if ((val1 == 0 || val1 == 1) && val > 39) + { + free(temp); + return ASN1_VALUE_NOT_VALID; + } + + val0 = 40 * val1 + val; + encode_val(val0, der, max_len, der_len); + } + else + { + encode_val(val, der, max_len, der_len); + } + n_start = n_end + 1; + } + + asn1_length_der (*der_len, NULL, &len_len); + if (max_len >= (*der_len + len_len)) + { + memmove (der + len_len, der, *der_len); + asn1_length_der (*der_len, der, &len_len); + } + *der_len += len_len; + + free (temp); + + if (max_len < (*der_len)) + return ASN1_MEM_ERROR; + + return ASN1_SUCCESS; +} + +/** + * asn1_object_id_der: + * @str: An object identifier in numeric, dot format. + * @der: buffer to hold the returned encoding (may be %NULL). + * @der_len: initially the size of @der; will hold the final size. + * @flags: must be zero + * + * Creates the DER encoding of the provided object identifier. + * + * Returns: %ASN1_SUCCESS if DER encoding was OK, %ASN1_VALUE_NOT_VALID + * if @str is not a valid OID, %ASN1_MEM_ERROR if the @der + * vector isn't big enough and in this case @der_len will contain the + * length needed. + **/ +int asn1_object_id_der(const char *str, unsigned char *der, int *der_len, unsigned flags) +{ + unsigned char tag_der[MAX_TAG_LEN]; + int tag_len = 0, r; + int max_len = *der_len; + + *der_len = 0; + + _asn1_tag_der (ETYPE_CLASS (ASN1_ETYPE_OBJECT_ID), ETYPE_TAG (ASN1_ETYPE_OBJECT_ID), + tag_der, &tag_len); + + if (max_len > tag_len) + { + memcpy(der, tag_der, tag_len); + } + max_len -= tag_len; + der += tag_len; + + r = _asn1_object_id_der (str, der, &max_len); + if (r == ASN1_MEM_ERROR || r == ASN1_SUCCESS) + { + *der_len = max_len + tag_len; + } + + return r; +} + +static const unsigned char bit_mask[] = + { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80 }; + +/** + * asn1_bit_der: + * @str: BIT string. + * @bit_len: number of meaningful bits in STR. + * @der: string returned. + * @der_len: number of meaningful bytes of DER + * (der[0]..der[ans_len-1]). + * + * Creates a length-value DER encoding for the input data + * as it would have been for a BIT STRING. + * The DER encoded data will be copied in @der. + * + * Note that the BIT STRING tag is not included in the output. + * + * This function does not return any value because it is expected + * that @der_len will contain enough bytes to store the string + * plus the DER encoding. The DER encoding size can be obtained using + * asn1_length_der(). + **/ +void +asn1_bit_der (const unsigned char *str, int bit_len, + unsigned char *der, int *der_len) +{ + int len_len, len_byte, len_pad; + + if (der == NULL) + return; + + len_byte = bit_len >> 3; + len_pad = 8 - (bit_len & 7); + if (len_pad == 8) + len_pad = 0; + else + len_byte++; + asn1_length_der (len_byte + 1, der, &len_len); + der[len_len] = len_pad; + + if (str) + memcpy (der + len_len + 1, str, len_byte); + der[len_len + len_byte] &= bit_mask[len_pad]; + *der_len = len_byte + len_len + 1; +} + + +/******************************************************/ +/* Function : _asn1_complete_explicit_tag */ +/* Description: add the length coding to the EXPLICIT */ +/* tags. */ +/* Parameters: */ +/* node: pointer to the tree element. */ +/* der: string with the DER coding of the whole tree*/ +/* counter: number of meaningful bytes of DER */ +/* (der[0]..der[*counter-1]). */ +/* max_len: size of der vector */ +/* Return: */ +/* ASN1_MEM_ERROR if der vector isn't big enough, */ +/* otherwise ASN1_SUCCESS. */ +/******************************************************/ +static int +_asn1_complete_explicit_tag (asn1_node node, unsigned char *der, + int *counter, int *max_len) +{ + asn1_node p; + int is_tag_implicit, len2, len3; + unsigned char temp[SIZEOF_UNSIGNED_INT]; + + if (der == NULL && *max_len > 0) + return ASN1_VALUE_NOT_VALID; + + is_tag_implicit = 0; + + if (node->type & CONST_TAG) + { + p = node->down; + if (p == NULL) + return ASN1_DER_ERROR; + /* When there are nested tags we must complete them reverse to + the order they were created. This is because completing a tag + modifies all data within it, including the incomplete tags + which store buffer positions -- simon@josefsson.org 2002-09-06 + */ + while (p->right) + p = p->right; + while (p && p != node->down->left) + { + if (type_field (p->type) == ASN1_ETYPE_TAG) + { + if (p->type & CONST_EXPLICIT) + { + len2 = strtol (p->name, NULL, 10); + _asn1_set_name (p, NULL); + + asn1_length_der (*counter - len2, temp, &len3); + if (len3 <= (*max_len)) + { + memmove (der + len2 + len3, der + len2, + *counter - len2); + memcpy (der + len2, temp, len3); + } + *max_len -= len3; + *counter += len3; + is_tag_implicit = 0; + } + else + { /* CONST_IMPLICIT */ + if (!is_tag_implicit) + { + is_tag_implicit = 1; + } + } + } + p = p->left; + } + } + + if (*max_len < 0) + return ASN1_MEM_ERROR; + + return ASN1_SUCCESS; +} + +const tag_and_class_st _asn1_tags[] = { + [ASN1_ETYPE_GENERALSTRING] = + {ASN1_TAG_GENERALSTRING, ASN1_CLASS_UNIVERSAL, "type:GENERALSTRING"}, + [ASN1_ETYPE_NUMERIC_STRING] = + {ASN1_TAG_NUMERIC_STRING, ASN1_CLASS_UNIVERSAL, "type:NUMERIC_STR"}, + [ASN1_ETYPE_IA5_STRING] = + {ASN1_TAG_IA5_STRING, ASN1_CLASS_UNIVERSAL, "type:IA5_STR"}, + [ASN1_ETYPE_TELETEX_STRING] = + {ASN1_TAG_TELETEX_STRING, ASN1_CLASS_UNIVERSAL, "type:TELETEX_STR"}, + [ASN1_ETYPE_PRINTABLE_STRING] = + {ASN1_TAG_PRINTABLE_STRING, ASN1_CLASS_UNIVERSAL, "type:PRINTABLE_STR"}, + [ASN1_ETYPE_UNIVERSAL_STRING] = + {ASN1_TAG_UNIVERSAL_STRING, ASN1_CLASS_UNIVERSAL, "type:UNIVERSAL_STR"}, + [ASN1_ETYPE_BMP_STRING] = + {ASN1_TAG_BMP_STRING, ASN1_CLASS_UNIVERSAL, "type:BMP_STR"}, + [ASN1_ETYPE_UTF8_STRING] = + {ASN1_TAG_UTF8_STRING, ASN1_CLASS_UNIVERSAL, "type:UTF8_STR"}, + [ASN1_ETYPE_VISIBLE_STRING] = + {ASN1_TAG_VISIBLE_STRING, ASN1_CLASS_UNIVERSAL, "type:VISIBLE_STR"}, + [ASN1_ETYPE_OCTET_STRING] = + {ASN1_TAG_OCTET_STRING, ASN1_CLASS_UNIVERSAL, "type:OCT_STR"}, + [ASN1_ETYPE_BIT_STRING] = + {ASN1_TAG_BIT_STRING, ASN1_CLASS_UNIVERSAL, "type:BIT_STR"}, + [ASN1_ETYPE_OBJECT_ID] = + {ASN1_TAG_OBJECT_ID, ASN1_CLASS_UNIVERSAL, "type:OBJ_ID"}, + [ASN1_ETYPE_NULL] = {ASN1_TAG_NULL, ASN1_CLASS_UNIVERSAL, "type:NULL"}, + [ASN1_ETYPE_BOOLEAN] = + {ASN1_TAG_BOOLEAN, ASN1_CLASS_UNIVERSAL, "type:BOOLEAN"}, + [ASN1_ETYPE_INTEGER] = + {ASN1_TAG_INTEGER, ASN1_CLASS_UNIVERSAL, "type:INTEGER"}, + [ASN1_ETYPE_ENUMERATED] = + {ASN1_TAG_ENUMERATED, ASN1_CLASS_UNIVERSAL, "type:ENUMERATED"}, + [ASN1_ETYPE_SEQUENCE] = + {ASN1_TAG_SEQUENCE, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, + "type:SEQUENCE"}, + [ASN1_ETYPE_SEQUENCE_OF] = + {ASN1_TAG_SEQUENCE, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, + "type:SEQ_OF"}, + [ASN1_ETYPE_SET] = + {ASN1_TAG_SET, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, "type:SET"}, + [ASN1_ETYPE_SET_OF] = + {ASN1_TAG_SET, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, + "type:SET_OF"}, + [ASN1_ETYPE_GENERALIZED_TIME] = + {ASN1_TAG_GENERALIZEDTime, ASN1_CLASS_UNIVERSAL, "type:GENERALIZED_TIME"}, + [ASN1_ETYPE_UTC_TIME] = + {ASN1_TAG_UTCTime, ASN1_CLASS_UNIVERSAL, "type:UTC_TIME"}, +}; + +unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]); + +/******************************************************/ +/* Function : _asn1_insert_tag_der */ +/* Description: creates the DER coding of tags of one */ +/* NODE. */ +/* Parameters: */ +/* node: pointer to the tree element. */ +/* der: string returned */ +/* counter: number of meaningful bytes of DER */ +/* (counter[0]..der[*counter-1]). */ +/* max_len: size of der vector */ +/* Return: */ +/* ASN1_GENERIC_ERROR if the type is unknown, */ +/* ASN1_MEM_ERROR if der vector isn't big enough, */ +/* otherwise ASN1_SUCCESS. */ +/******************************************************/ +static int +_asn1_insert_tag_der (asn1_node node, unsigned char *der, int *counter, + int *max_len) +{ + asn1_node p; + int tag_len, is_tag_implicit; + unsigned char class, class_implicit = 0, temp[MAX(SIZEOF_UNSIGNED_INT * 3 + 1, LTOSTR_MAX_SIZE)]; + unsigned long tag_implicit = 0; + unsigned char tag_der[MAX_TAG_LEN]; + + is_tag_implicit = 0; + + if (node->type & CONST_TAG) + { + p = node->down; + while (p) + { + if (type_field (p->type) == ASN1_ETYPE_TAG) + { + if (p->type & CONST_APPLICATION) + class = ASN1_CLASS_APPLICATION; + else if (p->type & CONST_UNIVERSAL) + class = ASN1_CLASS_UNIVERSAL; + else if (p->type & CONST_PRIVATE) + class = ASN1_CLASS_PRIVATE; + else + class = ASN1_CLASS_CONTEXT_SPECIFIC; + + if (p->type & CONST_EXPLICIT) + { + if (is_tag_implicit) + _asn1_tag_der (class_implicit, tag_implicit, tag_der, + &tag_len); + else + _asn1_tag_der (class | ASN1_CLASS_STRUCTURED, + _asn1_strtoul (p->value, NULL, 10), + tag_der, &tag_len); + + *max_len -= tag_len; + if (der && *max_len >= 0) + memcpy (der + *counter, tag_der, tag_len); + *counter += tag_len; + + _asn1_ltostr (*counter, (char *) temp); + _asn1_set_name (p, (const char *) temp); + + is_tag_implicit = 0; + } + else + { /* CONST_IMPLICIT */ + if (!is_tag_implicit) + { + if ((type_field (node->type) == ASN1_ETYPE_SEQUENCE) || + (type_field (node->type) == ASN1_ETYPE_SEQUENCE_OF) + || (type_field (node->type) == ASN1_ETYPE_SET) + || (type_field (node->type) == ASN1_ETYPE_SET_OF)) + class |= ASN1_CLASS_STRUCTURED; + class_implicit = class; + tag_implicit = _asn1_strtoul (p->value, NULL, 10); + is_tag_implicit = 1; + } + } + } + p = p->right; + } + } + + if (is_tag_implicit) + { + _asn1_tag_der (class_implicit, tag_implicit, tag_der, &tag_len); + } + else + { + unsigned type = type_field (node->type); + switch (type) + { + CASE_HANDLED_ETYPES: + _asn1_tag_der (_asn1_tags[type].class, _asn1_tags[type].tag, + tag_der, &tag_len); + break; + case ASN1_ETYPE_TAG: + case ASN1_ETYPE_CHOICE: + case ASN1_ETYPE_ANY: + tag_len = 0; + break; + default: + return ASN1_GENERIC_ERROR; + } + } + + *max_len -= tag_len; + if (der && *max_len >= 0) + memcpy (der + *counter, tag_der, tag_len); + *counter += tag_len; + + if (*max_len < 0) + return ASN1_MEM_ERROR; + + return ASN1_SUCCESS; +} + +/******************************************************/ +/* Function : _asn1_ordering_set */ +/* Description: puts the elements of a SET type in */ +/* the correct order according to DER rules. */ +/* Parameters: */ +/* der: string with the DER coding. */ +/* node: pointer to the SET element. */ +/* Return: */ +/* ASN1_SUCCESS if successful */ +/* or an error value. */ +/******************************************************/ +static int +_asn1_ordering_set (unsigned char *der, int der_len, asn1_node node) +{ + struct vet + { + int end; + unsigned long value; + struct vet *next, *prev; + }; + + int counter, len, len2; + struct vet *first, *last, *p_vet, *p2_vet; + asn1_node p; + unsigned char class, *temp; + unsigned long tag, t; + int err; + + counter = 0; + + if (type_field (node->type) != ASN1_ETYPE_SET) + return ASN1_VALUE_NOT_VALID; + + p = node->down; + while (p && ((type_field (p->type) == ASN1_ETYPE_TAG) || + (type_field (p->type) == ASN1_ETYPE_SIZE))) + p = p->right; + + if ((p == NULL) || (p->right == NULL)) + return ASN1_SUCCESS; + + first = last = NULL; + while (p) + { + p_vet = malloc (sizeof (struct vet)); + if (p_vet == NULL) + { + err = ASN1_MEM_ALLOC_ERROR; + goto error; + } + + p_vet->next = NULL; + p_vet->prev = last; + if (first == NULL) + first = p_vet; + else + last->next = p_vet; + last = p_vet; + + /* tag value calculation */ + err = asn1_get_tag_der (der + counter, der_len - counter, &class, &len2, + &tag); + if (err != ASN1_SUCCESS) + goto error; + + t = ((unsigned int)class) << 24; + p_vet->value = t | tag; + counter += len2; + + /* extraction and length */ + len2 = asn1_get_length_der (der + counter, der_len - counter, &len); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + counter += len + len2; + + p_vet->end = counter; + p = p->right; + } + + p_vet = first; + + while (p_vet) + { + p2_vet = p_vet->next; + counter = 0; + while (p2_vet) + { + if (p_vet->value > p2_vet->value) + { + /* change position */ + temp = malloc (p_vet->end - counter); + if (temp == NULL) + { + err = ASN1_MEM_ALLOC_ERROR; + goto error; + } + + memcpy (temp, der + counter, p_vet->end - counter); + memcpy (der + counter, der + p_vet->end, + p2_vet->end - p_vet->end); + memcpy (der + counter + p2_vet->end - p_vet->end, temp, + p_vet->end - counter); + free (temp); + + tag = p_vet->value; + p_vet->value = p2_vet->value; + p2_vet->value = tag; + + p_vet->end = counter + (p2_vet->end - p_vet->end); + } + counter = p_vet->end; + + p2_vet = p2_vet->next; + p_vet = p_vet->next; + } + + if (p_vet != first) + p_vet->prev->next = NULL; + else + first = NULL; + free (p_vet); + p_vet = first; + } + return ASN1_SUCCESS; + +error: + while (first != NULL) + { + p_vet = first; + first = first->next; + free(p_vet); + } + return err; +} + +struct vet +{ + unsigned char *ptr; + int size; +}; + +static int setof_compar(const void *_e1, const void *_e2) +{ + unsigned length; + const struct vet *e1 = _e1, *e2 = _e2; + int rval; + + /* The encodings of the component values of a set-of value shall + * appear in ascending order, the encodings being compared + * as octet strings with the shorter components being + * padded at their trailing end with 0-octets. + * The padding octets are for comparison purposes and + * do not appear in the encodings. + */ + length = MIN(e1->size, e2->size); + + rval = memcmp(e1->ptr, e2->ptr, length); + if (rval == 0 && e1->size != e2->size) + { + if (e1->size > e2->size) + rval = 1; + else if (e2->size > e1->size) + rval = -1; + } + + return rval; +} + +/******************************************************/ +/* Function : _asn1_ordering_set_of */ +/* Description: puts the elements of a SET OF type in */ +/* the correct order according to DER rules. */ +/* Parameters: */ +/* der: string with the DER coding. */ +/* node: pointer to the SET OF element. */ +/* Return: */ +/* ASN1_SUCCESS if successful */ +/* or an error value. */ +/******************************************************/ +static int +_asn1_ordering_set_of (unsigned char *der, int der_len, asn1_node node) +{ + int counter, len, len2; + struct vet *list = NULL, *tlist; + unsigned list_size = 0; + struct vet *p_vet; + asn1_node p; + unsigned char class; + unsigned i; + unsigned char *out = NULL; + int err; + + if (der == NULL) + return ASN1_VALUE_NOT_VALID; + + counter = 0; + + if (type_field (node->type) != ASN1_ETYPE_SET_OF) + return ASN1_VALUE_NOT_VALID; + + p = node->down; + while (p && ((type_field (p->type) == ASN1_ETYPE_TAG) || + (type_field (p->type) == ASN1_ETYPE_SIZE))) + p = p->right; + if (p == NULL) + return ASN1_VALUE_NOT_VALID; + p = p->right; + + if ((p == NULL) || (p->right == NULL)) + return ASN1_SUCCESS; + + while (p) + { + list_size++; + tlist = realloc (list, list_size*sizeof(struct vet)); + if (tlist == NULL) + { + err = ASN1_MEM_ALLOC_ERROR; + goto error; + } + list = tlist; + p_vet = &list[list_size-1]; + + p_vet->ptr = der+counter; + p_vet->size = 0; + + /* extraction of tag and length */ + if (der_len - counter > 0) + { + err = asn1_get_tag_der (der + counter, der_len - counter, &class, + &len, NULL); + if (err != ASN1_SUCCESS) + goto error; + counter += len; + p_vet->size += len; + + len2 = asn1_get_length_der (der + counter, der_len - counter, &len); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + counter += len + len2; + p_vet->size += len + len2; + + } + else + { + err = ASN1_DER_ERROR; + goto error; + } + p = p->right; + } + + if (counter > der_len) + { + err = ASN1_DER_ERROR; + goto error; + } + + qsort(list, list_size, sizeof(struct vet), setof_compar); + + out = malloc(der_len); + if (out == NULL) + { + err = ASN1_MEM_ERROR; + goto error; + } + + /* the sum of p_vet->size == der_len */ + counter = 0; + for (i = 0; i < list_size; i++) + { + p_vet = &list[i]; + memcpy(out+counter, p_vet->ptr, p_vet->size); + counter += p_vet->size; + } + memcpy(der, out, der_len); + free(out); + + err = ASN1_SUCCESS; + +error: + free(list); + return err; +} + +/** + * asn1_der_coding: + * @element: pointer to an ASN1 element + * @name: the name of the structure you want to encode (it must be + * inside *POINTER). + * @ider: vector that will contain the DER encoding. DER must be a + * pointer to memory cells already allocated. + * @len: number of bytes of *@ider: @ider[0]..@ider[len-1], Initialy + * holds the sizeof of der vector. + * @ErrorDescription: return the error description or an empty + * string if success. + * + * Creates the DER encoding for the NAME structure (inside *POINTER + * structure). + * + * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND + * if @name is not a valid element, %ASN1_VALUE_NOT_FOUND if there + * is an element without a value, %ASN1_MEM_ERROR if the @ider + * vector isn't big enough and in this case @len will contain the + * length needed. + **/ +int +asn1_der_coding (asn1_node_const element, const char *name, void *ider, int *len, + char *ErrorDescription) +{ + asn1_node node, p, p2; + unsigned char temp[MAX(LTOSTR_MAX_SIZE, SIZEOF_UNSIGNED_LONG_INT * 3 + 1)]; + int counter, counter_old, len2, len3, move, max_len, max_len_old; + int err; + unsigned char *der = ider; + + if (ErrorDescription) + ErrorDescription[0] = 0; + + node = asn1_find_node (element, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + /* Node is now a locally allocated variable. + * That is because in some point we modify the + * structure, and I don't know why! --nmav + */ + node = _asn1_copy_structure3 (node); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + max_len = *len; + + if (der == NULL && max_len > 0) + return ASN1_VALUE_NOT_VALID; + + counter = 0; + move = DOWN; + p = node; + + while (1) + { + + counter_old = counter; + max_len_old = max_len; + if (move != UP) + { + p->start = counter; + err = _asn1_insert_tag_der (p, der, &counter, &max_len); + if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) + goto error; + } + switch (type_field (p->type)) + { + case ASN1_ETYPE_NULL: + max_len--; + if (der != NULL && max_len >= 0) + der[counter] = 0; + counter++; + move = RIGHT; + break; + case ASN1_ETYPE_BOOLEAN: + if ((p->type & CONST_DEFAULT) && (p->value == NULL)) + { + counter = counter_old; + max_len = max_len_old; + } + else + { + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, + ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + max_len -= 2; + if (der != NULL && max_len >= 0) + { + der[counter++] = 1; + if (p->value[0] == 'F') + der[counter++] = 0; + else + der[counter++] = 0xFF; + } + else + counter += 2; + } + move = RIGHT; + break; + case ASN1_ETYPE_INTEGER: + case ASN1_ETYPE_ENUMERATED: + if ((p->type & CONST_DEFAULT) && (p->value == NULL)) + { + counter = counter_old; + max_len = max_len_old; + } + else + { + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, + ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + max_len -= len2 + len3; + if (der != NULL && max_len >= 0) + memcpy (der + counter, p->value, len3 + len2); + counter += len3 + len2; + } + move = RIGHT; + break; + case ASN1_ETYPE_OBJECT_ID: + if ((p->type & CONST_DEFAULT) && (p->value == NULL)) + { + counter = counter_old; + max_len = max_len_old; + } + else + { + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, + ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = max_len; + err = _asn1_object_id_der ((char*)p->value, der + counter, &len2); + if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) + goto error; + + max_len -= len2; + counter += len2; + } + move = RIGHT; + break; + case ASN1_ETYPE_GENERALIZED_TIME: + case ASN1_ETYPE_UTC_TIME: + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = max_len; + err = _asn1_time_der (p->value, p->value_len, der + counter, &len2); + if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) + goto error; + + max_len -= len2; + counter += len2; + move = RIGHT; + break; + case ASN1_ETYPE_OCTET_STRING: + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + case ASN1_ETYPE_BIT_STRING: + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + max_len -= len2 + len3; + if (der != NULL && max_len >= 0) + memcpy (der + counter, p->value, len3 + len2); + counter += len3 + len2; + move = RIGHT; + break; + case ASN1_ETYPE_SEQUENCE: + case ASN1_ETYPE_SET: + if (move != UP) + { + p->tmp_ival = counter; + if (p->down == NULL) + { + move = UP; + continue; + } + else + { + p2 = p->down; + while (p2 && (type_field (p2->type) == ASN1_ETYPE_TAG)) + p2 = p2->right; + if (p2) + { + p = p2; + move = RIGHT; + continue; + } + move = UP; + continue; + } + } + else + { /* move==UP */ + len2 = p->tmp_ival; + p->tmp_ival = 0; + if ((type_field (p->type) == ASN1_ETYPE_SET) && (max_len >= 0)) + { + err = _asn1_ordering_set (der + len2, counter - len2, p); + if (err != ASN1_SUCCESS) + goto error; + } + asn1_length_der (counter - len2, temp, &len3); + max_len -= len3; + if (der != NULL && max_len >= 0) + { + memmove (der + len2 + len3, der + len2, counter - len2); + memcpy (der + len2, temp, len3); + } + counter += len3; + move = RIGHT; + } + break; + case ASN1_ETYPE_SEQUENCE_OF: + case ASN1_ETYPE_SET_OF: + if (move != UP) + { + p->tmp_ival = counter; + p = p->down; + while ((type_field (p->type) == ASN1_ETYPE_TAG) + || (type_field (p->type) == ASN1_ETYPE_SIZE)) + p = p->right; + if (p->right) + { + p = p->right; + move = RIGHT; + continue; + } + else + p = _asn1_find_up (p); + move = UP; + } + if (move == UP) + { + len2 = p->tmp_ival; + p->tmp_ival = 0; + if ((type_field (p->type) == ASN1_ETYPE_SET_OF) + && (counter - len2 > 0) && (max_len >= 0)) + { + err = _asn1_ordering_set_of (der + len2, counter - len2, p); + if (err != ASN1_SUCCESS) + goto error; + } + asn1_length_der (counter - len2, temp, &len3); + max_len -= len3; + if (der != NULL && max_len >= 0) + { + memmove (der + len2 + len3, der + len2, counter - len2); + memcpy (der + len2, temp, len3); + } + counter += len3; + move = RIGHT; + } + break; + case ASN1_ETYPE_ANY: + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + max_len -= len2; + if (der != NULL && max_len >= 0) + memcpy (der + counter, p->value + len3, len2); + counter += len2; + move = RIGHT; + break; + default: + move = (move == UP) ? RIGHT : DOWN; + break; + } + + if ((move != DOWN) && (counter != counter_old)) + { + p->end = counter - 1; + err = _asn1_complete_explicit_tag (p, der, &counter, &max_len); + if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) + goto error; + } + + if (p == node && move != DOWN) + break; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + if (move == RIGHT) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + *len = counter; + + if (max_len < 0) + { + err = ASN1_MEM_ERROR; + goto error; + } + + err = ASN1_SUCCESS; + +error: + asn1_delete_structure (&node); + return err; +} diff --git a/grub-core/lib/libtasn1/lib/decoding.c b/grub-core/lib/libtasn1/lib/decoding.c new file mode 100644 index 0000000000..ff04eb778c --- /dev/null +++ b/grub-core/lib/libtasn1/lib/decoding.c @@ -0,0 +1,2478 @@ +/* + * Copyright (C) 2002-2016 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/*****************************************************/ +/* File: decoding.c */ +/* Description: Functions to manage DER decoding */ +/*****************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +# define warn() fprintf(stderr, "%s: %d\n", __func__, __LINE__) +#else +# define warn() +#endif + +#define IS_ERR(len, flags) (len < -1 || ((flags & ASN1_DECODE_FLAG_STRICT_DER) && len < 0)) + +#define HAVE_TWO(x) (x>=2?1:0) + +/* Decoding flags (dflags) used in several decoding functions. + * DECODE_FLAG_HAVE_TAG: The provided buffer includes a tag + * DECODE_FLAG_CONSTRUCTED: The provided buffer is of indefinite encoding (useful + * when no tags are present). + * DECODE_FLAG_LEVEL1: Internal flag to indicate a level of recursion for BER strings. + * DECODE_FLAG_LEVEL2: Internal flag to indicate two levels of recursion for BER strings. + * DECODE_FLAG_LEVEL3: Internal flag to indicate three levels of recursion for BER strings. + * This is the maximum levels of recursion possible to prevent stack + * exhaustion. + */ + +#define DECODE_FLAG_HAVE_TAG 1 +#define DECODE_FLAG_CONSTRUCTED (1<<1) +#define DECODE_FLAG_LEVEL1 (1<<2) +#define DECODE_FLAG_LEVEL2 (1<<3) +#define DECODE_FLAG_LEVEL3 (1<<4) + +#define DECR_LEN(l, s) do { \ + l -= s; \ + if (l < 0) { \ + warn(); \ + result = ASN1_DER_ERROR; \ + goto cleanup; \ + } \ + } while (0) + +static int +_asn1_get_indefinite_length_string (const unsigned char *der, int der_len, int *len); + +static int +_asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, + unsigned int _der_len, unsigned char **str, + unsigned int *str_len, unsigned int *ber_len, + unsigned dflags); + +static int +_asn1_decode_simple_der (unsigned int etype, const unsigned char *der, + unsigned int _der_len, const unsigned char **str, + unsigned int *str_len, unsigned dflags); + +static void +_asn1_error_description_tag_error (asn1_node node, char *ErrorDescription) +{ + + Estrcpy (ErrorDescription, ":: tag error near element '"); + _asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription), + ASN1_MAX_ERROR_DESCRIPTION_SIZE - 40); + Estrcat (ErrorDescription, "'"); + +} + +/** + * asn1_get_length_der: + * @der: DER data to decode. + * @der_len: Length of DER data to decode. + * @len: Output variable containing the length of the DER length field. + * + * Extract a length field from DER data. + * + * Returns: Return the decoded length value, or -1 on indefinite + * length, or -2 when the value was too big to fit in a int, or -4 + * when the decoded length value plus @len would exceed @der_len. + **/ +long +asn1_get_length_der (const unsigned char *der, int der_len, int *len) +{ + unsigned int ans; + int k, punt, sum; + + *len = 0; + if (der_len <= 0) + return 0; + + if (!(der[0] & 128)) + { + /* short form */ + *len = 1; + ans = der[0]; + } + else + { + /* Long form */ + k = der[0] & 0x7F; + punt = 1; + if (k) + { /* definite length method */ + ans = 0; + while (punt <= k && punt < der_len) + { + if (INT_MULTIPLY_OVERFLOW (ans, 256)) + return -2; + ans *= 256; + + if (INT_ADD_OVERFLOW (ans, ((unsigned) der[punt]))) + return -2; + ans += der[punt]; + punt++; + } + } + else + { /* indefinite length method */ + *len = punt; + return -1; + } + + *len = punt; + } + + sum = ans; + if (ans >= INT_MAX || INT_ADD_OVERFLOW (sum, (*len))) + return -2; + sum += *len; + + if (sum > der_len) + return -4; + + return ans; +} + +/** + * asn1_get_tag_der: + * @der: DER data to decode. + * @der_len: Length of DER data to decode. + * @cls: Output variable containing decoded class. + * @len: Output variable containing the length of the DER TAG data. + * @tag: Output variable containing the decoded tag (may be %NULL). + * + * Decode the class and TAG from DER code. + * + * Returns: Returns %ASN1_SUCCESS on success, or an error. + **/ +int +asn1_get_tag_der (const unsigned char *der, int der_len, + unsigned char *cls, int *len, unsigned long *tag) +{ + unsigned int ris; + int punt; + + if (der == NULL || der_len < 2 || len == NULL) + return ASN1_DER_ERROR; + + *cls = der[0] & 0xE0; + if ((der[0] & 0x1F) != 0x1F) + { + /* short form */ + *len = 1; + ris = der[0] & 0x1F; + } + else + { + /* Long form */ + punt = 1; + ris = 0; + while (punt < der_len && der[punt] & 128) + { + + if (INT_MULTIPLY_OVERFLOW (ris, 128)) + return ASN1_DER_ERROR; + ris *= 128; + + if (INT_ADD_OVERFLOW (ris, ((unsigned) (der[punt] & 0x7F)))) + return ASN1_DER_ERROR; + ris += (der[punt] & 0x7F); + punt++; + } + + if (punt >= der_len) + return ASN1_DER_ERROR; + + if (INT_MULTIPLY_OVERFLOW (ris, 128)) + return ASN1_DER_ERROR; + ris *= 128; + + if (INT_ADD_OVERFLOW (ris, ((unsigned) (der[punt] & 0x7F)))) + return ASN1_DER_ERROR; + ris += (der[punt] & 0x7F); + punt++; + + *len = punt; + } + + if (tag) + *tag = ris; + return ASN1_SUCCESS; +} + +/** + * asn1_get_length_ber: + * @ber: BER data to decode. + * @ber_len: Length of BER data to decode. + * @len: Output variable containing the length of the BER length field. + * + * Extract a length field from BER data. The difference to + * asn1_get_length_der() is that this function will return a length + * even if the value has indefinite encoding. + * + * Returns: Return the decoded length value, or negative value when + * the value was too big. + * + * Since: 2.0 + **/ +long +asn1_get_length_ber (const unsigned char *ber, int ber_len, int *len) +{ + int ret; + long err; + + ret = asn1_get_length_der (ber, ber_len, len); + + if (ret == -1 && ber_len > 1) + { /* indefinite length method */ + err = _asn1_get_indefinite_length_string (ber + 1, ber_len-1, &ret); + if (err != ASN1_SUCCESS) + return -3; + } + + return ret; +} + +/** + * asn1_get_octet_der: + * @der: DER data to decode containing the OCTET SEQUENCE. + * @der_len: The length of the @der data to decode. + * @ret_len: Output variable containing the encoded length of the DER data. + * @str: Pre-allocated output buffer to put decoded OCTET SEQUENCE in. + * @str_size: Length of pre-allocated output buffer. + * @str_len: Output variable containing the length of the contents of the OCTET SEQUENCE. + * + * Extract an OCTET SEQUENCE from DER data. Note that this function + * expects the DER data past the tag field, i.e., the length and + * content octets. + * + * Returns: Returns %ASN1_SUCCESS on success, or an error. + **/ +int +asn1_get_octet_der (const unsigned char *der, int der_len, + int *ret_len, unsigned char *str, int str_size, + int *str_len) +{ + int len_len = 0; + + if (der_len <= 0) + return ASN1_GENERIC_ERROR; + + *str_len = asn1_get_length_der (der, der_len, &len_len); + + if (*str_len < 0) + return ASN1_DER_ERROR; + + *ret_len = *str_len + len_len; + if (str_size >= *str_len) + { + if (*str_len > 0 && str != NULL) + memcpy (str, der + len_len, *str_len); + } + else + { + return ASN1_MEM_ERROR; + } + + return ASN1_SUCCESS; +} + + +/*- + * _asn1_get_time_der: + * @type: %ASN1_ETYPE_GENERALIZED_TIME or %ASN1_ETYPE_UTC_TIME + * @der: DER data to decode containing the time + * @der_len: Length of DER data to decode. + * @ret_len: Output variable containing the length of the DER data. + * @str: Pre-allocated output buffer to put the textual time in. + * @str_size: Length of pre-allocated output buffer. + * @flags: Zero or %ASN1_DECODE_FLAG_STRICT_DER + * + * Performs basic checks in the DER encoded time object and returns its textual form. + * The textual form will be in the YYYYMMDD000000Z format for GeneralizedTime + * and YYMMDD000000Z for UTCTime. + * + * Returns: %ASN1_SUCCESS on success, or an error. + -*/ +static int +_asn1_get_time_der (unsigned type, const unsigned char *der, int der_len, int *ret_len, + char *str, int str_size, unsigned flags) +{ + int len_len, str_len; + unsigned i; + unsigned sign_count = 0; + unsigned dot_count = 0; + const unsigned char *p; + + if (der_len <= 0 || str == NULL) + return ASN1_DER_ERROR; + + str_len = asn1_get_length_der (der, der_len, &len_len); + if (str_len <= 0 || str_size < str_len) + return ASN1_DER_ERROR; + + /* perform some sanity checks on the data */ + if (str_len < 8) + { + warn(); + return ASN1_TIME_ENCODING_ERROR; + } + + if ((flags & ASN1_DECODE_FLAG_STRICT_DER) && !(flags & ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME)) + { + p = &der[len_len]; + for (i=0;i<(unsigned)(str_len-1);i++) + { + if (c_isdigit(p[i]) == 0) + { + if (type == ASN1_ETYPE_GENERALIZED_TIME) + { + /* tolerate lax encodings */ + if (p[i] == '.' && dot_count == 0) + { + dot_count++; + continue; + } + + /* This is not really valid DER, but there are + * structures using that */ + if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) && + (p[i] == '+' || p[i] == '-') && sign_count == 0) + { + sign_count++; + continue; + } + } + + warn(); + return ASN1_TIME_ENCODING_ERROR; + } + } + + if (sign_count == 0 && p[str_len-1] != 'Z') + { + warn(); + return ASN1_TIME_ENCODING_ERROR; + } + } + memcpy (str, der + len_len, str_len); + str[str_len] = 0; + *ret_len = str_len + len_len; + + return ASN1_SUCCESS; +} + +/** + * asn1_get_object_id_der: + * @der: DER data to decode containing the OBJECT IDENTIFIER + * @der_len: Length of DER data to decode. + * @ret_len: Output variable containing the length of the DER data. + * @str: Pre-allocated output buffer to put the textual object id in. + * @str_size: Length of pre-allocated output buffer. + * + * Converts a DER encoded object identifier to its textual form. This + * function expects the DER object identifier without the tag. + * + * Returns: %ASN1_SUCCESS on success, or an error. + **/ +int +asn1_get_object_id_der (const unsigned char *der, int der_len, int *ret_len, + char *str, int str_size) +{ + int len_len, len, k; + int leading, parsed; + char temp[LTOSTR_MAX_SIZE]; + uint64_t val, val1, val0; + + *ret_len = 0; + if (str && str_size > 0) + str[0] = 0; /* no oid */ + + if (str == NULL || der_len <= 0) + return ASN1_GENERIC_ERROR; + + len = asn1_get_length_der (der, der_len, &len_len); + + if (len <= 0 || len + len_len > der_len) + return ASN1_DER_ERROR; + + /* leading octet can never be 0x80 */ + if (der[len_len] == 0x80) + return ASN1_DER_ERROR; + + val0 = 0; + + for (k = 0; k < len; k++) + { + if (INT_LEFT_SHIFT_OVERFLOW (val0, 7)) + return ASN1_DER_ERROR; + + val0 <<= 7; + val0 |= der[len_len + k] & 0x7F; + if (!(der[len_len + k] & 0x80)) + break; + } + parsed = ++k; + + /* val0 = (X*40) + Y, X={0,1,2}, Y<=39 when X={0,1} */ + /* X = val, Y = val1 */ + + /* check if X == 0 */ + val = 0; + val1 = val0; + if (val1 > 39) + { + val = 1; + val1 = val0 - 40; + if (val1 > 39) + { + val = 2; + val1 = val0 - 80; + } + } + + _asn1_str_cpy (str, str_size, _asn1_ltostr (val, temp)); + _asn1_str_cat (str, str_size, "."); + _asn1_str_cat (str, str_size, _asn1_ltostr (val1, temp)); + + val = 0; + leading = 1; + for (k = parsed; k < len; k++) + { + /* X.690 mandates that the leading byte must never be 0x80 + */ + if (leading != 0 && der[len_len + k] == 0x80) + return ASN1_DER_ERROR; + leading = 0; + + /* check for wrap around */ + if (INT_LEFT_SHIFT_OVERFLOW (val, 7)) + return ASN1_DER_ERROR; + + val = val << 7; + val |= der[len_len + k] & 0x7F; + + if (!(der[len_len + k] & 0x80)) + { + _asn1_str_cat (str, str_size, "."); + _asn1_str_cat (str, str_size, _asn1_ltostr (val, temp)); + val = 0; + leading = 1; + } + } + + if (INT_ADD_OVERFLOW (len, len_len)) + return ASN1_DER_ERROR; + + *ret_len = len + len_len; + + return ASN1_SUCCESS; +} + +/** + * asn1_get_bit_der: + * @der: DER data to decode containing the BIT SEQUENCE. + * @der_len: Length of DER data to decode. + * @ret_len: Output variable containing the length of the DER data. + * @str: Pre-allocated output buffer to put decoded BIT SEQUENCE in. + * @str_size: Length of pre-allocated output buffer. + * @bit_len: Output variable containing the size of the BIT SEQUENCE. + * + * Extract a BIT SEQUENCE from DER data. + * + * Returns: %ASN1_SUCCESS on success, or an error. + **/ +int +asn1_get_bit_der (const unsigned char *der, int der_len, + int *ret_len, unsigned char *str, int str_size, + int *bit_len) +{ + int len_len = 0, len_byte; + + if (der_len <= 0) + return ASN1_GENERIC_ERROR; + + len_byte = asn1_get_length_der (der, der_len, &len_len) - 1; + if (len_byte < 0) + return ASN1_DER_ERROR; + + *ret_len = len_byte + len_len + 1; + *bit_len = len_byte * 8 - der[len_len]; + + if (*bit_len < 0) + return ASN1_DER_ERROR; + + if (str_size >= len_byte) + { + if (len_byte > 0 && str) + memcpy (str, der + len_len + 1, len_byte); + } + else + { + return ASN1_MEM_ERROR; + } + + return ASN1_SUCCESS; +} + +/* tag_len: the total tag length (explicit+inner) + * inner_tag_len: the inner_tag length + */ +static int +_asn1_extract_tag_der (asn1_node node, const unsigned char *der, int der_len, + int *tag_len, int *inner_tag_len, unsigned flags) +{ + asn1_node p; + int counter, len2, len3, is_tag_implicit; + int result; + unsigned long tag, tag_implicit = 0; + unsigned char class, class2, class_implicit = 0; + + if (der_len <= 0) + return ASN1_GENERIC_ERROR; + + counter = is_tag_implicit = 0; + + if (node->type & CONST_TAG) + { + p = node->down; + while (p) + { + if (type_field (p->type) == ASN1_ETYPE_TAG) + { + if (p->type & CONST_APPLICATION) + class2 = ASN1_CLASS_APPLICATION; + else if (p->type & CONST_UNIVERSAL) + class2 = ASN1_CLASS_UNIVERSAL; + else if (p->type & CONST_PRIVATE) + class2 = ASN1_CLASS_PRIVATE; + else + class2 = ASN1_CLASS_CONTEXT_SPECIFIC; + + if (p->type & CONST_EXPLICIT) + { + if (asn1_get_tag_der + (der + counter, der_len, &class, &len2, + &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + + DECR_LEN(der_len, len2); + counter += len2; + + if (flags & ASN1_DECODE_FLAG_STRICT_DER) + len3 = + asn1_get_length_der (der + counter, der_len, + &len2); + else + len3 = + asn1_get_length_ber (der + counter, der_len, + &len2); + if (len3 < 0) + return ASN1_DER_ERROR; + + DECR_LEN(der_len, len2); + counter += len2; + + if (!is_tag_implicit) + { + if ((class != (class2 | ASN1_CLASS_STRUCTURED)) || + (tag != strtoul ((char *) p->value, NULL, 10))) + return ASN1_TAG_ERROR; + } + else + { /* ASN1_TAG_IMPLICIT */ + if ((class != class_implicit) || (tag != tag_implicit)) + return ASN1_TAG_ERROR; + } + is_tag_implicit = 0; + } + else + { /* ASN1_TAG_IMPLICIT */ + if (!is_tag_implicit) + { + if ((type_field (node->type) == ASN1_ETYPE_SEQUENCE) || + (type_field (node->type) == ASN1_ETYPE_SEQUENCE_OF) + || (type_field (node->type) == ASN1_ETYPE_SET) + || (type_field (node->type) == ASN1_ETYPE_SET_OF)) + class2 |= ASN1_CLASS_STRUCTURED; + class_implicit = class2; + tag_implicit = strtoul ((char *) p->value, NULL, 10); + is_tag_implicit = 1; + } + } + } + p = p->right; + } + } + + if (is_tag_implicit) + { + if (asn1_get_tag_der + (der + counter, der_len, &class, &len2, + &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + + DECR_LEN(der_len, len2); + + if ((class != class_implicit) || (tag != tag_implicit)) + { + if (type_field (node->type) == ASN1_ETYPE_OCTET_STRING) + { + class_implicit |= ASN1_CLASS_STRUCTURED; + if ((class != class_implicit) || (tag != tag_implicit)) + return ASN1_TAG_ERROR; + } + else + return ASN1_TAG_ERROR; + } + } + else + { + unsigned type = type_field (node->type); + if (type == ASN1_ETYPE_TAG) + { + *tag_len = 0; + if (inner_tag_len) + *inner_tag_len = 0; + return ASN1_SUCCESS; + } + + if (asn1_get_tag_der + (der + counter, der_len, &class, &len2, + &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + + DECR_LEN(der_len, len2); + + switch (type) + { + case ASN1_ETYPE_NULL: + case ASN1_ETYPE_BOOLEAN: + case ASN1_ETYPE_INTEGER: + case ASN1_ETYPE_ENUMERATED: + case ASN1_ETYPE_OBJECT_ID: + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + case ASN1_ETYPE_BIT_STRING: + case ASN1_ETYPE_SEQUENCE: + case ASN1_ETYPE_SEQUENCE_OF: + case ASN1_ETYPE_SET: + case ASN1_ETYPE_SET_OF: + case ASN1_ETYPE_GENERALIZED_TIME: + case ASN1_ETYPE_UTC_TIME: + if ((class != _asn1_tags[type].class) + || (tag != _asn1_tags[type].tag)) + return ASN1_DER_ERROR; + break; + + case ASN1_ETYPE_OCTET_STRING: + /* OCTET STRING is handled differently to allow + * BER encodings (structured class). */ + if (((class != ASN1_CLASS_UNIVERSAL) + && (class != (ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED))) + || (tag != ASN1_TAG_OCTET_STRING)) + return ASN1_DER_ERROR; + break; + case ASN1_ETYPE_ANY: + counter -= len2; + break; + case ASN1_ETYPE_CHOICE: + counter -= len2; + break; + default: + return ASN1_DER_ERROR; + break; + } + } + + counter += len2; + *tag_len = counter; + if (inner_tag_len) + *inner_tag_len = len2; + return ASN1_SUCCESS; + +cleanup: + return result; +} + +static int +extract_tag_der_recursive(asn1_node node, const unsigned char *der, int der_len, + int *ret_len, int *inner_len, unsigned flags) +{ +asn1_node p; +int ris = ASN1_DER_ERROR; + + if (type_field (node->type) == ASN1_ETYPE_CHOICE) + { + p = node->down; + while (p) + { + ris = _asn1_extract_tag_der (p, der, der_len, ret_len, inner_len, flags); + if (ris == ASN1_SUCCESS) + break; + p = p->right; + } + + *ret_len = 0; + return ris; + } + else + return _asn1_extract_tag_der (node, der, der_len, ret_len, inner_len, flags); +} + +static int +_asn1_delete_not_used (asn1_node node) +{ + asn1_node p, p2; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + while (p) + { + if (p->type & CONST_NOT_USED) + { + p2 = NULL; + if (p != node) + { + p2 = _asn1_find_left (p); + if (!p2) + p2 = _asn1_find_up (p); + } + asn1_delete_structure (&p); + p = p2; + } + + if (!p) + break; /* reach node */ + + if (p->down) + { + p = p->down; + } + else + { + if (p == node) + p = NULL; + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == node) + { + p = NULL; + break; + } + if (p->right) + { + p = p->right; + break; + } + } + } + } + } + return ASN1_SUCCESS; +} + +static int +_asn1_get_indefinite_length_string (const unsigned char *der, + int der_len, int *len) +{ + int len2, len3, counter, indefinite; + int result; + unsigned long tag; + unsigned char class; + + counter = indefinite = 0; + + while (1) + { + if (HAVE_TWO(der_len) && (der[counter] == 0) && (der[counter + 1] == 0)) + { + counter += 2; + DECR_LEN(der_len, 2); + + indefinite--; + if (indefinite <= 0) + break; + else + continue; + } + + if (asn1_get_tag_der + (der + counter, der_len, &class, &len2, + &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + + DECR_LEN(der_len, len2); + counter += len2; + + len2 = asn1_get_length_der (der + counter, der_len, &len3); + if (len2 < -1) + return ASN1_DER_ERROR; + + if (len2 == -1) + { + indefinite++; + counter += 1; + DECR_LEN(der_len, 1); + } + else + { + counter += len2 + len3; + DECR_LEN(der_len, len2+len3); + } + } + + *len = counter; + return ASN1_SUCCESS; + +cleanup: + return result; +} + +static void delete_unneeded_choice_fields(asn1_node p) +{ + asn1_node p2; + + while (p->right) + { + p2 = p->right; + asn1_delete_structure (&p2); + } +} + + +/** + * asn1_der_decoding2 + * @element: pointer to an ASN1 structure. + * @ider: vector that contains the DER encoding. + * @max_ider_len: pointer to an integer giving the information about the + * maximal number of bytes occupied by *@ider. The real size of the DER + * encoding is returned through this pointer. + * @flags: flags controlling the behaviour of the function. + * @errorDescription: null-terminated string contains details when an + * error occurred. + * + * Fill the structure *@element with values of a DER encoding string. The + * structure must just be created with function asn1_create_element(). + * + * If %ASN1_DECODE_FLAG_ALLOW_PADDING flag is set then the function will ignore + * padding after the decoded DER data. Upon a successful return the value of + * *@max_ider_len will be set to the number of bytes decoded. + * + * If %ASN1_DECODE_FLAG_STRICT_DER flag is set then the function will + * not decode any BER-encoded elements. + * + * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND + * if @ELEMENT is %NULL, and %ASN1_TAG_ERROR or + * %ASN1_DER_ERROR if the der encoding doesn't match the structure + * name (*@ELEMENT deleted). + **/ +int +asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, + unsigned int flags, char *errorDescription) +{ + asn1_node node, p, p2, p3; + char temp[128]; + int counter, len2, len3, len4, move, ris, tlen; + struct node_tail_cache_st tcache = {NULL, NULL}; + unsigned char class; + unsigned long tag; + int tag_len; + int indefinite, result, total_len = *max_ider_len, ider_len = *max_ider_len; + int inner_tag_len; + unsigned char *ptmp; + const unsigned char *ptag; + const unsigned char *der = ider; + + node = *element; + + if (errorDescription != NULL) + errorDescription[0] = 0; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + if (node->type & CONST_OPTION) + { + result = ASN1_GENERIC_ERROR; + warn(); + goto cleanup; + } + + counter = 0; + move = DOWN; + p = node; + while (1) + { + tag_len = 0; + inner_tag_len = 0; + ris = ASN1_SUCCESS; + if (move != UP) + { + if (p->type & CONST_SET) + { + p2 = _asn1_find_up (p); + len2 = p2->tmp_ival; + if (len2 == -1) + { + if (HAVE_TWO(ider_len) && !der[counter] && !der[counter + 1]) + { + p = p2; + move = UP; + counter += 2; + DECR_LEN(ider_len, 2); + continue; + } + } + else if (counter == len2) + { + p = p2; + move = UP; + continue; + } + else if (counter > len2) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + p2 = p2->down; + while (p2) + { + if ((p2->type & CONST_SET) && (p2->type & CONST_NOT_USED)) + { + ris = + extract_tag_der_recursive (p2, der + counter, + ider_len, &len2, NULL, flags); + if (ris == ASN1_SUCCESS) + { + p2->type &= ~CONST_NOT_USED; + p = p2; + break; + } + } + p2 = p2->right; + } + if (p2 == NULL) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + } + + /* the position in the DER structure this starts */ + p->start = counter; + p->end = total_len - 1; + + if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) + { + p2 = _asn1_find_up (p); + len2 = p2->tmp_ival; + if (counter == len2) + { + if (p->right) + { + p2 = p->right; + move = RIGHT; + } + else + move = UP; + + if (p->type & CONST_OPTION) + asn1_delete_structure (&p); + + p = p2; + continue; + } + } + + if (type_field (p->type) == ASN1_ETYPE_CHOICE) + { + while (p->down) + { + ris = + extract_tag_der_recursive (p->down, der + counter, + ider_len, &len2, NULL, flags); + + if (ris == ASN1_SUCCESS) + { + delete_unneeded_choice_fields(p->down); + break; + } + else if (ris == ASN1_ERROR_TYPE_ANY) + { + result = ASN1_ERROR_TYPE_ANY; + warn(); + goto cleanup; + } + else + { + p2 = p->down; + asn1_delete_structure (&p2); + } + } + + if (p->down == NULL) + { + if (!(p->type & CONST_OPTION)) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + } + else if (type_field (p->type) != ASN1_ETYPE_CHOICE) + p = p->down; + + p->start = counter; + } + + if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) + { + p2 = _asn1_find_up (p); + len2 = p2->tmp_ival; + + if ((len2 != -1) && (counter > len2)) + ris = ASN1_TAG_ERROR; + } + + if (ris == ASN1_SUCCESS) + ris = + extract_tag_der_recursive (p, der + counter, ider_len, + &tag_len, &inner_tag_len, flags); + + if (ris != ASN1_SUCCESS) + { + if (p->type & CONST_OPTION) + { + p->type |= CONST_NOT_USED; + move = RIGHT; + } + else if (p->type & CONST_DEFAULT) + { + _asn1_set_value (p, NULL, 0); + move = RIGHT; + } + else + { + if (errorDescription != NULL) + _asn1_error_description_tag_error (p, errorDescription); + + result = ASN1_TAG_ERROR; + warn(); + goto cleanup; + } + } + else + { + DECR_LEN(ider_len, tag_len); + counter += tag_len; + } + } + + if (ris == ASN1_SUCCESS) + { + switch (type_field (p->type)) + { + case ASN1_ETYPE_NULL: + DECR_LEN(ider_len, 1); + if (der[counter]) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + counter++; + move = RIGHT; + break; + case ASN1_ETYPE_BOOLEAN: + DECR_LEN(ider_len, 2); + + if (der[counter++] != 1) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + if (der[counter++] == 0) + _asn1_set_value (p, "F", 1); + else + _asn1_set_value (p, "T", 1); + move = RIGHT; + break; + case ASN1_ETYPE_INTEGER: + case ASN1_ETYPE_ENUMERATED: + len2 = + asn1_get_length_der (der + counter, ider_len, &len3); + if (len2 < 0) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len3+len2); + + _asn1_set_value (p, der + counter, len3 + len2); + counter += len3 + len2; + move = RIGHT; + break; + case ASN1_ETYPE_OBJECT_ID: + result = + asn1_get_object_id_der (der + counter, ider_len, &len2, + temp, sizeof (temp)); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len2); + + tlen = strlen (temp); + if (tlen > 0) + _asn1_set_value (p, temp, tlen + 1); + + counter += len2; + move = RIGHT; + break; + case ASN1_ETYPE_GENERALIZED_TIME: + case ASN1_ETYPE_UTC_TIME: + result = + _asn1_get_time_der (type_field (p->type), der + counter, ider_len, &len2, temp, + sizeof (temp) - 1, flags); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len2); + + tlen = strlen (temp); + if (tlen > 0) + _asn1_set_value (p, temp, tlen); + + counter += len2; + move = RIGHT; + break; + case ASN1_ETYPE_OCTET_STRING: + if (counter < inner_tag_len) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + ptag = der + counter - inner_tag_len; + if ((flags & ASN1_DECODE_FLAG_STRICT_DER) || !(ptag[0] & ASN1_CLASS_STRUCTURED)) + { + if (ptag[0] & ASN1_CLASS_STRUCTURED) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + len2 = + asn1_get_length_der (der + counter, ider_len, &len3); + if (len2 < 0) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len3+len2); + + _asn1_set_value (p, der + counter, len3 + len2); + counter += len3 + len2; + } + else + { + unsigned dflags = 0, vlen, ber_len; + + if (ptag[0] & ASN1_CLASS_STRUCTURED) + dflags |= DECODE_FLAG_CONSTRUCTED; + + result = _asn1_decode_simple_ber(type_field (p->type), der+counter, ider_len, &ptmp, &vlen, &ber_len, dflags); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, ber_len); + + _asn1_set_value_lv (p, ptmp, vlen); + + counter += ber_len; + free(ptmp); + } + move = RIGHT; + break; + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + case ASN1_ETYPE_BIT_STRING: + len2 = + asn1_get_length_der (der + counter, ider_len, &len3); + if (len2 < 0) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len3+len2); + + _asn1_set_value (p, der + counter, len3 + len2); + counter += len3 + len2; + move = RIGHT; + break; + case ASN1_ETYPE_SEQUENCE: + case ASN1_ETYPE_SET: + if (move == UP) + { + len2 = p->tmp_ival; + p->tmp_ival = 0; + if (len2 == -1) + { /* indefinite length method */ + DECR_LEN(ider_len, 2); + if ((der[counter]) || der[counter + 1]) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + counter += 2; + } + else + { /* definite length method */ + if (len2 != counter) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + } + move = RIGHT; + } + else + { /* move==DOWN || move==RIGHT */ + len3 = + asn1_get_length_der (der + counter, ider_len, &len2); + if (IS_ERR(len3, flags)) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len2); + counter += len2; + + if (len3 > 0) + { + p->tmp_ival = counter + len3; + move = DOWN; + } + else if (len3 == 0) + { + p2 = p->down; + while (p2) + { + if (type_field (p2->type) != ASN1_ETYPE_TAG) + { + p3 = p2->right; + asn1_delete_structure (&p2); + p2 = p3; + } + else + p2 = p2->right; + } + move = RIGHT; + } + else + { /* indefinite length method */ + p->tmp_ival = -1; + move = DOWN; + } + } + break; + case ASN1_ETYPE_SEQUENCE_OF: + case ASN1_ETYPE_SET_OF: + if (move == UP) + { + len2 = p->tmp_ival; + if (len2 == -1) + { /* indefinite length method */ + if (!HAVE_TWO(ider_len) || ((der[counter]) || der[counter + 1])) + { + result = _asn1_append_sequence_set (p, &tcache); + if (result != 0) + { + warn(); + goto cleanup; + } + p = tcache.tail; + move = RIGHT; + continue; + } + + p->tmp_ival = 0; + tcache.tail = NULL; /* finished decoding this structure */ + tcache.head = NULL; + DECR_LEN(ider_len, 2); + counter += 2; + } + else + { /* definite length method */ + if (len2 > counter) + { + result = _asn1_append_sequence_set (p, &tcache); + if (result != 0) + { + warn(); + goto cleanup; + } + p = tcache.tail; + move = RIGHT; + continue; + } + + p->tmp_ival = 0; + tcache.tail = NULL; /* finished decoding this structure */ + tcache.head = NULL; + + if (len2 != counter) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + } + } + else + { /* move==DOWN || move==RIGHT */ + len3 = + asn1_get_length_der (der + counter, ider_len, &len2); + if (IS_ERR(len3, flags)) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len2); + counter += len2; + if (len3) + { + if (len3 > 0) + { /* definite length method */ + p->tmp_ival = counter + len3; + } + else + { /* indefinite length method */ + p->tmp_ival = -1; + } + + p2 = p->down; + if (p2 == NULL) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + while ((type_field (p2->type) == ASN1_ETYPE_TAG) + || (type_field (p2->type) == ASN1_ETYPE_SIZE)) + p2 = p2->right; + if (p2->right == NULL) + { + result = _asn1_append_sequence_set (p, &tcache); + if (result != 0) + { + warn(); + goto cleanup; + } + } + p = p2; + } + } + move = RIGHT; + break; + case ASN1_ETYPE_ANY: + /* Check indefinite lenth method in an EXPLICIT TAG */ + + if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) && (p->type & CONST_TAG) && + tag_len == 2 && (der[counter - 1] == 0x80)) + indefinite = 1; + else + indefinite = 0; + + if (asn1_get_tag_der + (der + counter, ider_len, &class, &len2, + &tag) != ASN1_SUCCESS) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len2); + + len4 = + asn1_get_length_der (der + counter + len2, + ider_len, &len3); + if (IS_ERR(len4, flags)) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + if (len4 != -1) /* definite */ + { + len2 += len4; + + DECR_LEN(ider_len, len4+len3); + _asn1_set_value_lv (p, der + counter, len2 + len3); + counter += len2 + len3; + } + else /* == -1 */ + { /* indefinite length */ + ider_len += len2; /* undo DECR_LEN */ + + if (counter == 0) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + result = + _asn1_get_indefinite_length_string (der + counter, ider_len, &len2); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len2); + _asn1_set_value_lv (p, der + counter, len2); + counter += len2; + + } + + /* Check if a couple of 0x00 are present due to an EXPLICIT TAG with + an indefinite length method. */ + if (indefinite) + { + DECR_LEN(ider_len, 2); + if (!der[counter] && !der[counter + 1]) + { + counter += 2; + } + else + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + } + + move = RIGHT; + break; + default: + move = (move == UP) ? RIGHT : DOWN; + break; + } + } + + if (p) + { + p->end = counter - 1; + } + + if (p == node && move != DOWN) + break; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + if ((move == RIGHT) && !(p->type & CONST_SET)) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + _asn1_delete_not_used (*element); + + if ((ider_len < 0) || + (!(flags & ASN1_DECODE_FLAG_ALLOW_PADDING) && (ider_len != 0))) + { + warn(); + result = ASN1_DER_ERROR; + goto cleanup; + } + + *max_ider_len = total_len - ider_len; + + return ASN1_SUCCESS; + +cleanup: + asn1_delete_structure (element); + return result; +} + + +/** + * asn1_der_decoding: + * @element: pointer to an ASN1 structure. + * @ider: vector that contains the DER encoding. + * @ider_len: number of bytes of *@ider: @ider[0]..@ider[len-1]. + * @errorDescription: null-terminated string contains details when an + * error occurred. + * + * Fill the structure *@element with values of a DER encoding + * string. The structure must just be created with function + * asn1_create_element(). + * + * Note that the *@element variable is provided as a pointer for + * historical reasons. + * + * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND + * if @ELEMENT is %NULL, and %ASN1_TAG_ERROR or + * %ASN1_DER_ERROR if the der encoding doesn't match the structure + * name (*@ELEMENT deleted). + **/ +int +asn1_der_decoding (asn1_node * element, const void *ider, int ider_len, + char *errorDescription) +{ + return asn1_der_decoding2 (element, ider, &ider_len, 0, errorDescription); +} + +/** + * asn1_der_decoding_element: + * @structure: pointer to an ASN1 structure + * @elementName: name of the element to fill + * @ider: vector that contains the DER encoding of the whole structure. + * @len: number of bytes of *der: der[0]..der[len-1] + * @errorDescription: null-terminated string contains details when an + * error occurred. + * + * Fill the element named @ELEMENTNAME with values of a DER encoding + * string. The structure must just be created with function + * asn1_create_element(). The DER vector must contain the encoding + * string of the whole @STRUCTURE. If an error occurs during the + * decoding procedure, the *@STRUCTURE is deleted and set equal to + * %NULL. + * + * This function is deprecated and may just be an alias to asn1_der_decoding + * in future versions. Use asn1_der_decoding() instead. + * + * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND + * if ELEMENT is %NULL or @elementName == NULL, and + * %ASN1_TAG_ERROR or %ASN1_DER_ERROR if the der encoding doesn't + * match the structure @structure (*ELEMENT deleted). + **/ +int +asn1_der_decoding_element (asn1_node * structure, const char *elementName, + const void *ider, int len, char *errorDescription) +{ + return asn1_der_decoding(structure, ider, len, errorDescription); +} + +/** + * asn1_der_decoding_startEnd: + * @element: pointer to an ASN1 element + * @ider: vector that contains the DER encoding. + * @ider_len: number of bytes of *@ider: @ider[0]..@ider[len-1] + * @name_element: an element of NAME structure. + * @start: the position of the first byte of NAME_ELEMENT decoding + * (@ider[*start]) + * @end: the position of the last byte of NAME_ELEMENT decoding + * (@ider[*end]) + * + * Find the start and end point of an element in a DER encoding + * string. I mean that if you have a der encoding and you have already + * used the function asn1_der_decoding() to fill a structure, it may + * happen that you want to find the piece of string concerning an + * element of the structure. + * + * One example is the sequence "tbsCertificate" inside an X509 + * certificate. + * + * Note that since libtasn1 3.7 the @ider and @ider_len parameters + * can be omitted, if the element is already decoded using asn1_der_decoding(). + * + * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND + * if ELEMENT is %asn1_node EMPTY or @name_element is not a valid + * element, %ASN1_TAG_ERROR or %ASN1_DER_ERROR if the der encoding + * doesn't match the structure ELEMENT. + **/ +int +asn1_der_decoding_startEnd (asn1_node element, const void *ider, int ider_len, + const char *name_element, int *start, int *end) +{ + asn1_node node, node_to_find; + int result = ASN1_DER_ERROR; + + node = element; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + node_to_find = asn1_find_node (node, name_element); + + if (node_to_find == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + *start = node_to_find->start; + *end = node_to_find->end; + + if (*start == 0 && *end == 0) + { + if (ider == NULL || ider_len == 0) + return ASN1_GENERIC_ERROR; + + /* it seems asn1_der_decoding() wasn't called before. Do it now */ + result = asn1_der_decoding (&node, ider, ider_len, NULL); + if (result != ASN1_SUCCESS) + { + warn(); + return result; + } + + node_to_find = asn1_find_node (node, name_element); + if (node_to_find == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + *start = node_to_find->start; + *end = node_to_find->end; + } + + if (*end < *start) + return ASN1_GENERIC_ERROR; + + return ASN1_SUCCESS; +} + +/** + * asn1_expand_any_defined_by: + * @definitions: ASN1 definitions + * @element: pointer to an ASN1 structure + * + * Expands every "ANY DEFINED BY" element of a structure created from + * a DER decoding process (asn1_der_decoding function). The element + * ANY must be defined by an OBJECT IDENTIFIER. The type used to + * expand the element ANY is the first one following the definition of + * the actual value of the OBJECT IDENTIFIER. + * + * Returns: %ASN1_SUCCESS if Substitution OK, %ASN1_ERROR_TYPE_ANY if + * some "ANY DEFINED BY" element couldn't be expanded due to a + * problem in OBJECT_ID -> TYPE association, or other error codes + * depending on DER decoding. + **/ +int +asn1_expand_any_defined_by (asn1_node_const definitions, asn1_node * element) +{ + char name[2 * ASN1_MAX_NAME_SIZE + 2], + value[ASN1_MAX_NAME_SIZE]; + int retCode = ASN1_SUCCESS, result; + int len, len2, len3; + asn1_node_const p2; + asn1_node p, p3, aux = NULL; + char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + const char *definitionsName; + + if ((definitions == NULL) || (*element == NULL)) + return ASN1_ELEMENT_NOT_FOUND; + + definitionsName = definitions->name; + + p = *element; + while (p) + { + + switch (type_field (p->type)) + { + case ASN1_ETYPE_ANY: + if ((p->type & CONST_DEFINED_BY) && (p->value)) + { + /* search the "DEF_BY" element */ + p2 = p->down; + while ((p2) && (type_field (p2->type) != ASN1_ETYPE_CONSTANT)) + p2 = p2->right; + + if (!p2) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + + p3 = _asn1_find_up (p); + + if (!p3) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + + p3 = p3->down; + while (p3) + { + if (!(strcmp (p3->name, p2->name))) + break; + p3 = p3->right; + } + + if ((!p3) || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) || + (p3->value == NULL)) + { + + p3 = _asn1_find_up (p); + p3 = _asn1_find_up (p3); + + if (!p3) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + + p3 = p3->down; + + while (p3) + { + if (!(strcmp (p3->name, p2->name))) + break; + p3 = p3->right; + } + + if ((!p3) || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) + || (p3->value == NULL)) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + } + + /* search the OBJECT_ID into definitions */ + p2 = definitions->down; + while (p2) + { + if ((type_field (p2->type) == ASN1_ETYPE_OBJECT_ID) && + (p2->type & CONST_ASSIGN)) + { + snprintf(name, sizeof(name), "%s.%s", definitionsName, p2->name); + + len = ASN1_MAX_NAME_SIZE; + result = + asn1_read_value (definitions, name, value, &len); + + if ((result == ASN1_SUCCESS) + && (!_asn1_strcmp (p3->value, value))) + { + p2 = p2->right; /* pointer to the structure to + use for expansion */ + while ((p2) && (p2->type & CONST_ASSIGN)) + p2 = p2->right; + + if (p2) + { + snprintf(name, sizeof(name), "%s.%s", definitionsName, p2->name); + + result = + asn1_create_element (definitions, name, &aux); + if (result == ASN1_SUCCESS) + { + _asn1_cpy_name (aux, p); + len2 = + asn1_get_length_der (p->value, + p->value_len, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + + result = + asn1_der_decoding (&aux, p->value + len3, + len2, + errorDescription); + if (result == ASN1_SUCCESS) + { + + _asn1_set_right (aux, p->right); + _asn1_set_right (p, aux); + + result = asn1_delete_structure (&p); + if (result == ASN1_SUCCESS) + { + p = aux; + aux = NULL; + break; + } + else + { /* error with asn1_delete_structure */ + asn1_delete_structure (&aux); + retCode = result; + break; + } + } + else + { /* error with asn1_der_decoding */ + retCode = result; + break; + } + } + else + { /* error with asn1_create_element */ + retCode = result; + break; + } + } + else + { /* error with the pointer to the structure to exapand */ + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + } + } + p2 = p2->right; + } /* end while */ + + if (!p2) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + + } + break; + default: + break; + } + + + if (p->down) + { + p = p->down; + } + else if (p == *element) + { + p = NULL; + break; + } + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == *element) + { + p = NULL; + break; + } + if (p->right) + { + p = p->right; + break; + } + } + } + } + + return retCode; +} + +/** + * asn1_expand_octet_string: + * @definitions: ASN1 definitions + * @element: pointer to an ASN1 structure + * @octetName: name of the OCTECT STRING field to expand. + * @objectName: name of the OBJECT IDENTIFIER field to use to define + * the type for expansion. + * + * Expands an "OCTET STRING" element of a structure created from a DER + * decoding process (the asn1_der_decoding() function). The type used + * for expansion is the first one following the definition of the + * actual value of the OBJECT IDENTIFIER indicated by OBJECTNAME. + * + * Returns: %ASN1_SUCCESS if substitution OK, %ASN1_ELEMENT_NOT_FOUND + * if @objectName or @octetName are not correct, + * %ASN1_VALUE_NOT_VALID if it wasn't possible to find the type to + * use for expansion, or other errors depending on DER decoding. + **/ +int +asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element, + const char *octetName, const char *objectName) +{ + char name[2 * ASN1_MAX_NAME_SIZE + 1], value[ASN1_MAX_NAME_SIZE]; + int retCode = ASN1_SUCCESS, result; + int len, len2, len3; + asn1_node_const p2; + asn1_node aux = NULL; + asn1_node octetNode = NULL, objectNode = NULL; + char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + + if ((definitions == NULL) || (*element == NULL)) + return ASN1_ELEMENT_NOT_FOUND; + + octetNode = asn1_find_node (*element, octetName); + if (octetNode == NULL) + return ASN1_ELEMENT_NOT_FOUND; + if (type_field (octetNode->type) != ASN1_ETYPE_OCTET_STRING) + return ASN1_ELEMENT_NOT_FOUND; + if (octetNode->value == NULL) + return ASN1_VALUE_NOT_FOUND; + + objectNode = asn1_find_node (*element, objectName); + if (objectNode == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + if (type_field (objectNode->type) != ASN1_ETYPE_OBJECT_ID) + return ASN1_ELEMENT_NOT_FOUND; + + if (objectNode->value == NULL) + return ASN1_VALUE_NOT_FOUND; + + + /* search the OBJECT_ID into definitions */ + p2 = definitions->down; + while (p2) + { + if ((type_field (p2->type) == ASN1_ETYPE_OBJECT_ID) && + (p2->type & CONST_ASSIGN)) + { + strcpy (name, definitions->name); + strcat (name, "."); + strcat (name, p2->name); + + len = sizeof (value); + result = asn1_read_value (definitions, name, value, &len); + + if ((result == ASN1_SUCCESS) + && (!_asn1_strcmp (objectNode->value, value))) + { + + p2 = p2->right; /* pointer to the structure to + use for expansion */ + while ((p2) && (p2->type & CONST_ASSIGN)) + p2 = p2->right; + + if (p2) + { + strcpy (name, definitions->name); + strcat (name, "."); + strcat (name, p2->name); + + result = asn1_create_element (definitions, name, &aux); + if (result == ASN1_SUCCESS) + { + _asn1_cpy_name (aux, octetNode); + len2 = + asn1_get_length_der (octetNode->value, + octetNode->value_len, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + + result = + asn1_der_decoding (&aux, octetNode->value + len3, + len2, errorDescription); + if (result == ASN1_SUCCESS) + { + + _asn1_set_right (aux, octetNode->right); + _asn1_set_right (octetNode, aux); + + result = asn1_delete_structure (&octetNode); + if (result == ASN1_SUCCESS) + { + aux = NULL; + break; + } + else + { /* error with asn1_delete_structure */ + asn1_delete_structure (&aux); + retCode = result; + break; + } + } + else + { /* error with asn1_der_decoding */ + retCode = result; + break; + } + } + else + { /* error with asn1_create_element */ + retCode = result; + break; + } + } + else + { /* error with the pointer to the structure to exapand */ + retCode = ASN1_VALUE_NOT_VALID; + break; + } + } + } + + p2 = p2->right; + + } + + if (!p2) + retCode = ASN1_VALUE_NOT_VALID; + + return retCode; +} + +/*- + * _asn1_decode_simple_der: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @der: the encoded string + * @_der_len: the bytes of the encoded string + * @str: a pointer to the data + * @str_len: the length of the data + * @dflags: DECODE_FLAG_* + * + * Decodes a simple DER encoded type (e.g. a string, which is not constructed). + * The output is a pointer inside the @der. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + -*/ +static int +_asn1_decode_simple_der (unsigned int etype, const unsigned char *der, + unsigned int _der_len, const unsigned char **str, + unsigned int *str_len, unsigned dflags) +{ + int tag_len, len_len; + const unsigned char *p; + int der_len = _der_len; + unsigned char class; + unsigned long tag; + long ret; + + if (der == NULL || der_len == 0) + return ASN1_VALUE_NOT_VALID; + + if (ETYPE_OK (etype) == 0 || ETYPE_IS_STRING(etype) == 0) + return ASN1_VALUE_NOT_VALID; + + /* doesn't handle constructed classes */ + class = ETYPE_CLASS(etype); + if (class != ASN1_CLASS_UNIVERSAL) + return ASN1_VALUE_NOT_VALID; + + p = der; + + if (dflags & DECODE_FLAG_HAVE_TAG) + { + ret = asn1_get_tag_der (p, der_len, &class, &tag_len, &tag); + if (ret != ASN1_SUCCESS) + return ret; + + if (class != ETYPE_CLASS (etype) || tag != ETYPE_TAG (etype)) + { + warn(); + return ASN1_DER_ERROR; + } + + p += tag_len; + der_len -= tag_len; + if (der_len <= 0) + return ASN1_DER_ERROR; + } + + ret = asn1_get_length_der (p, der_len, &len_len); + if (ret < 0) + return ASN1_DER_ERROR; + + p += len_len; + der_len -= len_len; + if (der_len <= 0) + return ASN1_DER_ERROR; + + *str_len = ret; + *str = p; + + return ASN1_SUCCESS; +} + +/** + * asn1_decode_simple_der: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @der: the encoded string + * @_der_len: the bytes of the encoded string + * @str: a pointer to the data + * @str_len: the length of the data + * + * Decodes a simple DER encoded type (e.g. a string, which is not constructed). + * The output is a pointer inside the @der. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + **/ +int +asn1_decode_simple_der (unsigned int etype, const unsigned char *der, + unsigned int _der_len, const unsigned char **str, + unsigned int *str_len) +{ + return _asn1_decode_simple_der(etype, der, _der_len, str, str_len, DECODE_FLAG_HAVE_TAG); +} + +static int append(uint8_t **dst, unsigned *dst_size, const unsigned char *src, unsigned src_size) +{ + if (src_size == 0) + return ASN1_SUCCESS; + + *dst = _asn1_realloc(*dst, *dst_size+src_size); + if (*dst == NULL) + return ASN1_MEM_ALLOC_ERROR; + memcpy(*dst + *dst_size, src, src_size); + *dst_size += src_size; + return ASN1_SUCCESS; +} + +/*- + * _asn1_decode_simple_ber: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @der: the encoded string + * @_der_len: the bytes of the encoded string + * @str: a pointer to the data + * @str_len: the length of the data + * @ber_len: the total length occupied by BER (may be %NULL) + * @have_tag: whether a DER tag is included + * + * Decodes a BER encoded type. The output is an allocated value + * of the data. This decodes BER STRINGS only. Other types are + * decoded as DER. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + -*/ +static int +_asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, + unsigned int _der_len, unsigned char **str, + unsigned int *str_len, unsigned int *ber_len, + unsigned dflags) +{ + int tag_len, len_len; + const unsigned char *p; + int der_len = _der_len; + uint8_t *total = NULL; + unsigned total_size = 0; + unsigned char class; + unsigned long tag; + unsigned char *out = NULL; + const unsigned char *cout = NULL; + unsigned out_len; + long result; + + if (ber_len) *ber_len = 0; + + if (der == NULL || der_len == 0) + { + warn(); + return ASN1_VALUE_NOT_VALID; + } + + if (ETYPE_OK (etype) == 0) + { + warn(); + return ASN1_VALUE_NOT_VALID; + } + + /* doesn't handle constructed + definite classes */ + class = ETYPE_CLASS (etype); + if (class != ASN1_CLASS_UNIVERSAL) + { + warn(); + return ASN1_VALUE_NOT_VALID; + } + + p = der; + + if (dflags & DECODE_FLAG_HAVE_TAG) + { + result = asn1_get_tag_der (p, der_len, &class, &tag_len, &tag); + if (result != ASN1_SUCCESS) + { + warn(); + return result; + } + + if (tag != ETYPE_TAG (etype)) + { + warn(); + return ASN1_DER_ERROR; + } + + p += tag_len; + + DECR_LEN(der_len, tag_len); + + if (ber_len) *ber_len += tag_len; + } + + /* indefinite constructed */ + if ((((dflags & DECODE_FLAG_CONSTRUCTED) || class == ASN1_CLASS_STRUCTURED) && ETYPE_IS_STRING(etype)) && + !(dflags & DECODE_FLAG_LEVEL3)) + { + if (der_len == 0) + { + warn(); + result = ASN1_DER_ERROR; + goto cleanup; + } + + if (der_len > 0 && p[0] == 0x80) /* indefinite */ + { + len_len = 1; + DECR_LEN(der_len, len_len); + p += len_len; + + if (ber_len) *ber_len += len_len; + + /* decode the available octet strings */ + do + { + unsigned tmp_len; + unsigned flags = DECODE_FLAG_HAVE_TAG; + + if (dflags & DECODE_FLAG_LEVEL1) + flags |= DECODE_FLAG_LEVEL2; + else if (dflags & DECODE_FLAG_LEVEL2) + flags |= DECODE_FLAG_LEVEL3; + else + flags |= DECODE_FLAG_LEVEL1; + + result = _asn1_decode_simple_ber(etype, p, der_len, &out, &out_len, &tmp_len, + flags); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + p += tmp_len; + DECR_LEN(der_len, tmp_len); + + if (ber_len) *ber_len += tmp_len; + + DECR_LEN(der_len, 2); /* we need the EOC */ + + result = append(&total, &total_size, out, out_len); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + free(out); + out = NULL; + + if (p[0] == 0 && p[1] == 0) /* EOC */ + { + if (ber_len) *ber_len += 2; + break; + } + + /* no EOC */ + der_len += 2; + + if (der_len == 2) + { + warn(); + result = ASN1_DER_ERROR; + goto cleanup; + } + } + while(1); + } + else /* constructed */ + { + long const_len; + + result = asn1_get_length_ber(p, der_len, &len_len); + if (result < 0) + { + warn(); + result = ASN1_DER_ERROR; + goto cleanup; + } + + DECR_LEN(der_len, len_len); + p += len_len; + + const_len = result; + + if (ber_len) *ber_len += len_len; + + /* decode the available octet strings */ + while(const_len > 0) + { + unsigned tmp_len; + unsigned flags = DECODE_FLAG_HAVE_TAG; + + if (dflags & DECODE_FLAG_LEVEL1) + flags |= DECODE_FLAG_LEVEL2; + else if (dflags & DECODE_FLAG_LEVEL2) + flags |= DECODE_FLAG_LEVEL3; + else + flags |= DECODE_FLAG_LEVEL1; + + result = _asn1_decode_simple_ber(etype, p, der_len, &out, &out_len, &tmp_len, + flags); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + p += tmp_len; + DECR_LEN(der_len, tmp_len); + DECR_LEN(const_len, tmp_len); + + if (ber_len) *ber_len += tmp_len; + + result = append(&total, &total_size, out, out_len); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + free(out); + out = NULL; + } + } + } + else if (class == ETYPE_CLASS(etype)) + { + if (ber_len) + { + result = asn1_get_length_der (p, der_len, &len_len); + if (result < 0) + { + warn(); + result = ASN1_DER_ERROR; + goto cleanup; + } + *ber_len += result + len_len; + } + + /* non-string values are decoded as DER */ + result = _asn1_decode_simple_der(etype, der, _der_len, &cout, &out_len, dflags); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + result = append(&total, &total_size, cout, out_len); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + } + else + { + warn(); + result = ASN1_DER_ERROR; + goto cleanup; + } + + *str = total; + *str_len = total_size; + + return ASN1_SUCCESS; +cleanup: + free(out); + free(total); + return result; +} + +/** + * asn1_decode_simple_ber: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @der: the encoded string + * @_der_len: the bytes of the encoded string + * @str: a pointer to the data + * @str_len: the length of the data + * @ber_len: the total length occupied by BER (may be %NULL) + * + * Decodes a BER encoded type. The output is an allocated value + * of the data. This decodes BER STRINGS only. Other types are + * decoded as DER. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + **/ +int +asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, + unsigned int _der_len, unsigned char **str, + unsigned int *str_len, unsigned int *ber_len) +{ + return _asn1_decode_simple_ber(etype, der, _der_len, str, str_len, ber_len, DECODE_FLAG_HAVE_TAG); +} diff --git a/grub-core/lib/libtasn1/lib/element.c b/grub-core/lib/libtasn1/lib/element.c new file mode 100644 index 0000000000..997eb2725d --- /dev/null +++ b/grub-core/lib/libtasn1/lib/element.c @@ -0,0 +1,1111 @@ +/* + * Copyright (C) 2000-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/*****************************************************/ +/* File: element.c */ +/* Description: Functions with the read and write */ +/* functions. */ +/*****************************************************/ + + +#include +#include "parser_aux.h" +#include +#include "structure.h" +#include "c-ctype.h" +#include "element.h" + +void +_asn1_hierarchical_name (asn1_node_const node, char *name, int name_size) +{ + asn1_node_const p; + char tmp_name[64]; + + p = node; + + name[0] = 0; + + while (p != NULL) + { + if (p->name[0] != 0) + { + _asn1_str_cpy (tmp_name, sizeof (tmp_name), name), + _asn1_str_cpy (name, name_size, p->name); + _asn1_str_cat (name, name_size, "."); + _asn1_str_cat (name, name_size, tmp_name); + } + p = _asn1_find_up (p); + } + + if (name[0] == 0) + _asn1_str_cpy (name, name_size, "ROOT"); +} + + +/******************************************************************/ +/* Function : _asn1_convert_integer */ +/* Description: converts an integer from a null terminated string */ +/* to der decoding. The convertion from a null */ +/* terminated string to an integer is made with */ +/* the 'strtol' function. */ +/* Parameters: */ +/* value: null terminated string to convert. */ +/* value_out: convertion result (memory must be already */ +/* allocated). */ +/* value_out_size: number of bytes of value_out. */ +/* len: number of significant byte of value_out. */ +/* Return: ASN1_MEM_ERROR or ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_convert_integer (const unsigned char *value, unsigned char *value_out, + int value_out_size, int *len) +{ + char negative; + unsigned char val[SIZEOF_UNSIGNED_LONG_INT]; + long valtmp; + int k, k2; + + valtmp = _asn1_strtol (value, NULL, 10); + + for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT; k++) + { + val[SIZEOF_UNSIGNED_LONG_INT - k - 1] = (valtmp >> (8 * k)) & 0xFF; + } + + if (val[0] & 0x80) + negative = 1; + else + negative = 0; + + for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT - 1; k++) + { + if (negative && (val[k] != 0xFF)) + break; + else if (!negative && val[k]) + break; + } + + if ((negative && !(val[k] & 0x80)) || (!negative && (val[k] & 0x80))) + k--; + + *len = SIZEOF_UNSIGNED_LONG_INT - k; + + if (SIZEOF_UNSIGNED_LONG_INT - k > value_out_size) + /* VALUE_OUT is too short to contain the value conversion */ + return ASN1_MEM_ERROR; + + if (value_out != NULL) + { + for (k2 = k; k2 < SIZEOF_UNSIGNED_LONG_INT; k2++) + value_out[k2 - k] = val[k2]; + } + +#if 0 + printf ("_asn1_convert_integer: valueIn=%s, lenOut=%d", value, *len); + for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT; k++) + printf (", vOut[%d]=%d", k, value_out[k]); + printf ("\n"); +#endif + + return ASN1_SUCCESS; +} + +/* Appends a new element into the sequence (or set) defined by this + * node. The new element will have a name of '?number', where number + * is a monotonically increased serial number. + * + * The last element in the list may be provided in @pcache, to avoid + * traversing the list, an expensive operation in long lists. + * + * On success it returns in @pcache the added element (which is the + * tail in the list of added elements). + */ +int +_asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcache) +{ + asn1_node p, p2; + char temp[LTOSTR_MAX_SIZE]; + long n; + + if (!node || !(node->down)) + return ASN1_GENERIC_ERROR; + + p = node->down; + while ((type_field (p->type) == ASN1_ETYPE_TAG) + || (type_field (p->type) == ASN1_ETYPE_SIZE)) + p = p->right; + + p2 = _asn1_copy_structure3 (p); + if (p2 == NULL) + return ASN1_GENERIC_ERROR; + + if (pcache == NULL || pcache->tail == NULL || pcache->head != node) + { + while (p->right) + { + p = p->right; + } + } + else + { + p = pcache->tail; + } + + _asn1_set_right (p, p2); + if (pcache) + { + pcache->head = node; + pcache->tail = p2; + } + + if (p->name[0] == 0) + _asn1_str_cpy (temp, sizeof (temp), "?1"); + else + { + n = strtol (p->name + 1, NULL, 0); + n++; + temp[0] = '?'; + _asn1_ltostr (n, temp + 1); + } + _asn1_set_name (p2, temp); + /* p2->type |= CONST_OPTION; */ + + return ASN1_SUCCESS; +} + + +/** + * asn1_write_value: + * @node_root: pointer to a structure + * @name: the name of the element inside the structure that you want to set. + * @ivalue: vector used to specify the value to set. If len is >0, + * VALUE must be a two's complement form integer. if len=0 *VALUE + * must be a null terminated string with an integer value. + * @len: number of bytes of *value to use to set the value: + * value[0]..value[len-1] or 0 if value is a null terminated string + * + * Set the value of one element inside a structure. + * + * If an element is OPTIONAL and you want to delete it, you must use + * the value=NULL and len=0. Using "pkix.asn": + * + * result=asn1_write_value(cert, "tbsCertificate.issuerUniqueID", + * NULL, 0); + * + * Description for each type: + * + * INTEGER: VALUE must contain a two's complement form integer. + * + * value[0]=0xFF , len=1 -> integer=-1. + * value[0]=0xFF value[1]=0xFF , len=2 -> integer=-1. + * value[0]=0x01 , len=1 -> integer= 1. + * value[0]=0x00 value[1]=0x01 , len=2 -> integer= 1. + * value="123" , len=0 -> integer= 123. + * + * ENUMERATED: As INTEGER (but only with not negative numbers). + * + * BOOLEAN: VALUE must be the null terminated string "TRUE" or + * "FALSE" and LEN != 0. + * + * value="TRUE" , len=1 -> boolean=TRUE. + * value="FALSE" , len=1 -> boolean=FALSE. + * + * OBJECT IDENTIFIER: VALUE must be a null terminated string with + * each number separated by a dot (e.g. "1.2.3.543.1"). LEN != 0. + * + * value="1 2 840 10040 4 3" , len=1 -> OID=dsa-with-sha. + * + * UTCTime: VALUE must be a null terminated string in one of these + * formats: "YYMMDDhhmmssZ", "YYMMDDhhmmssZ", + * "YYMMDDhhmmss+hh'mm'", "YYMMDDhhmmss-hh'mm'", + * "YYMMDDhhmm+hh'mm'", or "YYMMDDhhmm-hh'mm'". LEN != 0. + * + * value="9801011200Z" , len=1 -> time=Jannuary 1st, 1998 + * at 12h 00m Greenwich Mean Time + * + * GeneralizedTime: VALUE must be in one of this format: + * "YYYYMMDDhhmmss.sZ", "YYYYMMDDhhmmss.sZ", + * "YYYYMMDDhhmmss.s+hh'mm'", "YYYYMMDDhhmmss.s-hh'mm'", + * "YYYYMMDDhhmm+hh'mm'", or "YYYYMMDDhhmm-hh'mm'" where ss.s + * indicates the seconds with any precision like "10.1" or "01.02". + * LEN != 0 + * + * value="2001010112001.12-0700" , len=1 -> time=Jannuary + * 1st, 2001 at 12h 00m 01.12s Pacific Daylight Time + * + * OCTET STRING: VALUE contains the octet string and LEN is the + * number of octets. + * + * value="$\backslash$x01$\backslash$x02$\backslash$x03" , + * len=3 -> three bytes octet string + * + * GeneralString: VALUE contains the generalstring and LEN is the + * number of octets. + * + * value="$\backslash$x01$\backslash$x02$\backslash$x03" , + * len=3 -> three bytes generalstring + * + * BIT STRING: VALUE contains the bit string organized by bytes and + * LEN is the number of bits. + * + * value="$\backslash$xCF" , len=6 -> bit string="110011" (six + * bits) + * + * CHOICE: if NAME indicates a choice type, VALUE must specify one of + * the alternatives with a null terminated string. LEN != 0. Using + * "pkix.asn"\: + * + * result=asn1_write_value(cert, + * "certificate1.tbsCertificate.subject", "rdnSequence", + * 1); + * + * ANY: VALUE indicates the der encoding of a structure. LEN != 0. + * + * SEQUENCE OF: VALUE must be the null terminated string "NEW" and + * LEN != 0. With this instruction another element is appended in + * the sequence. The name of this element will be "?1" if it's the + * first one, "?2" for the second and so on. + * + * Using "pkix.asn"\: + * + * result=asn1_write_value(cert, + * "certificate1.tbsCertificate.subject.rdnSequence", "NEW", 1); + * + * SET OF: the same as SEQUENCE OF. Using "pkix.asn": + * + * result=asn1_write_value(cert, + * "tbsCertificate.subject.rdnSequence.?LAST", "NEW", 1); + * + * Returns: %ASN1_SUCCESS if the value was set, + * %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element, and + * %ASN1_VALUE_NOT_VALID if @ivalue has a wrong format. + **/ +int +asn1_write_value (asn1_node node_root, const char *name, + const void *ivalue, int len) +{ + asn1_node node, p, p2; + unsigned char *temp, *value_temp = NULL, *default_temp = NULL; + int len2, k, k2, negative; + size_t i; + const unsigned char *value = ivalue; + unsigned int type; + + node = asn1_find_node (node_root, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + if ((node->type & CONST_OPTION) && (value == NULL) && (len == 0)) + { + asn1_delete_structure (&node); + return ASN1_SUCCESS; + } + + type = type_field (node->type); + + if ((type == ASN1_ETYPE_SEQUENCE_OF || type == ASN1_ETYPE_SET_OF) && (value == NULL) && (len == 0)) + { + p = node->down; + while ((type_field (p->type) == ASN1_ETYPE_TAG) + || (type_field (p->type) == ASN1_ETYPE_SIZE)) + p = p->right; + + while (p->right) + asn1_delete_structure (&p->right); + + return ASN1_SUCCESS; + } + + /* Don't allow element deletion for other types */ + if (value == NULL) + { + return ASN1_VALUE_NOT_VALID; + } + + switch (type) + { + case ASN1_ETYPE_BOOLEAN: + if (!_asn1_strcmp (value, "TRUE")) + { + if (node->type & CONST_DEFAULT) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if (p->type & CONST_TRUE) + _asn1_set_value (node, NULL, 0); + else + _asn1_set_value (node, "T", 1); + } + else + _asn1_set_value (node, "T", 1); + } + else if (!_asn1_strcmp (value, "FALSE")) + { + if (node->type & CONST_DEFAULT) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if (p->type & CONST_FALSE) + _asn1_set_value (node, NULL, 0); + else + _asn1_set_value (node, "F", 1); + } + else + _asn1_set_value (node, "F", 1); + } + else + return ASN1_VALUE_NOT_VALID; + break; + case ASN1_ETYPE_INTEGER: + case ASN1_ETYPE_ENUMERATED: + if (len == 0) + { + if ((c_isdigit (value[0])) || (value[0] == '-')) + { + value_temp = malloc (SIZEOF_UNSIGNED_LONG_INT); + if (value_temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + _asn1_convert_integer (value, value_temp, + SIZEOF_UNSIGNED_LONG_INT, &len); + } + else + { /* is an identifier like v1 */ + if (!(node->type & CONST_LIST)) + return ASN1_VALUE_NOT_VALID; + p = node->down; + while (p) + { + if (type_field (p->type) == ASN1_ETYPE_CONSTANT) + { + if (!_asn1_strcmp (p->name, value)) + { + value_temp = malloc (SIZEOF_UNSIGNED_LONG_INT); + if (value_temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + _asn1_convert_integer (p->value, + value_temp, + SIZEOF_UNSIGNED_LONG_INT, + &len); + break; + } + } + p = p->right; + } + if (p == NULL) + return ASN1_VALUE_NOT_VALID; + } + } + else + { /* len != 0 */ + value_temp = malloc (len); + if (value_temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + memcpy (value_temp, value, len); + } + + if (value_temp[0] & 0x80) + negative = 1; + else + negative = 0; + + if (negative && (type_field (node->type) == ASN1_ETYPE_ENUMERATED)) + { + free (value_temp); + return ASN1_VALUE_NOT_VALID; + } + + for (k = 0; k < len - 1; k++) + if (negative && (value_temp[k] != 0xFF)) + break; + else if (!negative && value_temp[k]) + break; + + if ((negative && !(value_temp[k] & 0x80)) || + (!negative && (value_temp[k] & 0x80))) + k--; + + _asn1_set_value_lv (node, value_temp + k, len - k); + + if (node->type & CONST_DEFAULT) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if ((c_isdigit (p->value[0])) || (p->value[0] == '-')) + { + default_temp = malloc (SIZEOF_UNSIGNED_LONG_INT); + if (default_temp == NULL) + { + free (value_temp); + return ASN1_MEM_ALLOC_ERROR; + } + + _asn1_convert_integer (p->value, default_temp, + SIZEOF_UNSIGNED_LONG_INT, &len2); + } + else + { /* is an identifier like v1 */ + if (!(node->type & CONST_LIST)) + { + free (value_temp); + return ASN1_VALUE_NOT_VALID; + } + p2 = node->down; + while (p2) + { + if (type_field (p2->type) == ASN1_ETYPE_CONSTANT) + { + if (!_asn1_strcmp (p2->name, p->value)) + { + default_temp = malloc (SIZEOF_UNSIGNED_LONG_INT); + if (default_temp == NULL) + { + free (value_temp); + return ASN1_MEM_ALLOC_ERROR; + } + + _asn1_convert_integer (p2->value, + default_temp, + SIZEOF_UNSIGNED_LONG_INT, + &len2); + break; + } + } + p2 = p2->right; + } + if (p2 == NULL) + { + free (value_temp); + return ASN1_VALUE_NOT_VALID; + } + } + + + if ((len - k) == len2) + { + for (k2 = 0; k2 < len2; k2++) + if (value_temp[k + k2] != default_temp[k2]) + { + break; + } + if (k2 == len2) + _asn1_set_value (node, NULL, 0); + } + free (default_temp); + } + free (value_temp); + break; + case ASN1_ETYPE_OBJECT_ID: + for (i = 0; i < _asn1_strlen (value); i++) + if ((!c_isdigit (value[i])) && (value[i] != '.') && (value[i] != '+')) + return ASN1_VALUE_NOT_VALID; + if (node->type & CONST_DEFAULT) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if (!_asn1_strcmp (value, p->value)) + { + _asn1_set_value (node, NULL, 0); + break; + } + } + _asn1_set_value (node, value, _asn1_strlen (value) + 1); + break; + case ASN1_ETYPE_UTC_TIME: + { + len = _asn1_strlen (value); + if (len < 11) + return ASN1_VALUE_NOT_VALID; + for (k = 0; k < 10; k++) + if (!c_isdigit (value[k])) + return ASN1_VALUE_NOT_VALID; + switch (len) + { + case 11: + if (value[10] != 'Z') + return ASN1_VALUE_NOT_VALID; + break; + case 13: + if ((!c_isdigit (value[10])) || (!c_isdigit (value[11])) || + (value[12] != 'Z')) + return ASN1_VALUE_NOT_VALID; + break; + case 15: + if ((value[10] != '+') && (value[10] != '-')) + return ASN1_VALUE_NOT_VALID; + for (k = 11; k < 15; k++) + if (!c_isdigit (value[k])) + return ASN1_VALUE_NOT_VALID; + break; + case 17: + if ((!c_isdigit (value[10])) || (!c_isdigit (value[11]))) + return ASN1_VALUE_NOT_VALID; + if ((value[12] != '+') && (value[12] != '-')) + return ASN1_VALUE_NOT_VALID; + for (k = 13; k < 17; k++) + if (!c_isdigit (value[k])) + return ASN1_VALUE_NOT_VALID; + break; + default: + return ASN1_VALUE_NOT_FOUND; + } + _asn1_set_value (node, value, len); + } + break; + case ASN1_ETYPE_GENERALIZED_TIME: + len = _asn1_strlen (value); + _asn1_set_value (node, value, len); + break; + case ASN1_ETYPE_OCTET_STRING: + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + if (len == 0) + len = _asn1_strlen (value); + _asn1_set_value_lv (node, value, len); + break; + case ASN1_ETYPE_BIT_STRING: + if (len == 0) + len = _asn1_strlen (value); + asn1_length_der ((len >> 3) + 2, NULL, &len2); + temp = malloc ((len >> 3) + 2 + len2); + if (temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + asn1_bit_der (value, len, temp, &len2); + _asn1_set_value_m (node, temp, len2); + temp = NULL; + break; + case ASN1_ETYPE_CHOICE: + p = node->down; + while (p) + { + if (!_asn1_strcmp (p->name, value)) + { + p2 = node->down; + while (p2) + { + if (p2 != p) + { + asn1_delete_structure (&p2); + p2 = node->down; + } + else + p2 = p2->right; + } + break; + } + p = p->right; + } + if (!p) + return ASN1_ELEMENT_NOT_FOUND; + break; + case ASN1_ETYPE_ANY: + _asn1_set_value_lv (node, value, len); + break; + case ASN1_ETYPE_SEQUENCE_OF: + case ASN1_ETYPE_SET_OF: + if (_asn1_strcmp (value, "NEW")) + return ASN1_VALUE_NOT_VALID; + _asn1_append_sequence_set (node, NULL); + break; + default: + return ASN1_ELEMENT_NOT_FOUND; + break; + } + + return ASN1_SUCCESS; +} + + +#define PUT_VALUE( ptr, ptr_size, data, data_size) \ + *len = data_size; \ + if (ptr_size < data_size) { \ + return ASN1_MEM_ERROR; \ + } else { \ + if (ptr && data_size > 0) \ + memcpy (ptr, data, data_size); \ + } + +#define PUT_STR_VALUE( ptr, ptr_size, data) \ + *len = _asn1_strlen (data) + 1; \ + if (ptr_size < *len) { \ + return ASN1_MEM_ERROR; \ + } else { \ + /* this strcpy is checked */ \ + if (ptr) { \ + _asn1_strcpy (ptr, data); \ + } \ + } + +#define PUT_AS_STR_VALUE( ptr, ptr_size, data, data_size) \ + *len = data_size + 1; \ + if (ptr_size < *len) { \ + return ASN1_MEM_ERROR; \ + } else { \ + /* this strcpy is checked */ \ + if (ptr) { \ + if (data_size > 0) \ + memcpy (ptr, data, data_size); \ + ptr[data_size] = 0; \ + } \ + } + +#define ADD_STR_VALUE( ptr, ptr_size, data) \ + *len += _asn1_strlen(data); \ + if (ptr_size < (int) *len) { \ + (*len)++; \ + return ASN1_MEM_ERROR; \ + } else { \ + /* this strcat is checked */ \ + if (ptr) _asn1_strcat (ptr, data); \ + } + +/** + * asn1_read_value: + * @root: pointer to a structure. + * @name: the name of the element inside a structure that you want to read. + * @ivalue: vector that will contain the element's content, must be a + * pointer to memory cells already allocated (may be %NULL). + * @len: number of bytes of *value: value[0]..value[len-1]. Initialy + * holds the sizeof value. + * + * Returns the value of one element inside a structure. + * If an element is OPTIONAL and this returns + * %ASN1_ELEMENT_NOT_FOUND, it means that this element wasn't present + * in the der encoding that created the structure. The first element + * of a SEQUENCE_OF or SET_OF is named "?1". The second one "?2" and + * so on. If the @root provided is a node to specific sequence element, + * then the keyword "?CURRENT" is also acceptable and indicates the + * current sequence element of this node. + * + * Note that there can be valid values with length zero. In these case + * this function will succeed and @len will be zero. + * + * INTEGER: VALUE will contain a two's complement form integer. + * + * integer=-1 -> value[0]=0xFF , len=1. + * integer=1 -> value[0]=0x01 , len=1. + * + * ENUMERATED: As INTEGER (but only with not negative numbers). + * + * BOOLEAN: VALUE will be the null terminated string "TRUE" or + * "FALSE" and LEN=5 or LEN=6. + * + * OBJECT IDENTIFIER: VALUE will be a null terminated string with + * each number separated by a dot (i.e. "1.2.3.543.1"). + * + * LEN = strlen(VALUE)+1 + * + * UTCTime: VALUE will be a null terminated string in one of these + * formats: "YYMMDDhhmmss+hh'mm'" or "YYMMDDhhmmss-hh'mm'". + * LEN=strlen(VALUE)+1. + * + * GeneralizedTime: VALUE will be a null terminated string in the + * same format used to set the value. + * + * OCTET STRING: VALUE will contain the octet string and LEN will be + * the number of octets. + * + * GeneralString: VALUE will contain the generalstring and LEN will + * be the number of octets. + * + * BIT STRING: VALUE will contain the bit string organized by bytes + * and LEN will be the number of bits. + * + * CHOICE: If NAME indicates a choice type, VALUE will specify the + * alternative selected. + * + * ANY: If NAME indicates an any type, VALUE will indicate the DER + * encoding of the structure actually used. + * + * Returns: %ASN1_SUCCESS if value is returned, + * %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element, + * %ASN1_VALUE_NOT_FOUND if there isn't any value for the element + * selected, and %ASN1_MEM_ERROR if The value vector isn't big enough + * to store the result, and in this case @len will contain the number of + * bytes needed. On the occasion that the stored data are of zero-length + * this function may return %ASN1_SUCCESS even if the provided @len is zero. + **/ +int +asn1_read_value (asn1_node_const root, const char *name, void *ivalue, int *len) +{ + return asn1_read_value_type (root, name, ivalue, len, NULL); +} + +/** + * asn1_read_value_type: + * @root: pointer to a structure. + * @name: the name of the element inside a structure that you want to read. + * @ivalue: vector that will contain the element's content, must be a + * pointer to memory cells already allocated (may be %NULL). + * @len: number of bytes of *value: value[0]..value[len-1]. Initialy + * holds the sizeof value. + * @etype: The type of the value read (ASN1_ETYPE) + * + * Returns the type and value of one element inside a structure. + * If an element is OPTIONAL and this returns + * %ASN1_ELEMENT_NOT_FOUND, it means that this element wasn't present + * in the der encoding that created the structure. The first element + * of a SEQUENCE_OF or SET_OF is named "?1". The second one "?2" and + * so on. If the @root provided is a node to specific sequence element, + * then the keyword "?CURRENT" is also acceptable and indicates the + * current sequence element of this node. + * + * Note that there can be valid values with length zero. In these case + * this function will succeed and @len will be zero. + * + * + * INTEGER: VALUE will contain a two's complement form integer. + * + * integer=-1 -> value[0]=0xFF , len=1. + * integer=1 -> value[0]=0x01 , len=1. + * + * ENUMERATED: As INTEGER (but only with not negative numbers). + * + * BOOLEAN: VALUE will be the null terminated string "TRUE" or + * "FALSE" and LEN=5 or LEN=6. + * + * OBJECT IDENTIFIER: VALUE will be a null terminated string with + * each number separated by a dot (i.e. "1.2.3.543.1"). + * + * LEN = strlen(VALUE)+1 + * + * UTCTime: VALUE will be a null terminated string in one of these + * formats: "YYMMDDhhmmss+hh'mm'" or "YYMMDDhhmmss-hh'mm'". + * LEN=strlen(VALUE)+1. + * + * GeneralizedTime: VALUE will be a null terminated string in the + * same format used to set the value. + * + * OCTET STRING: VALUE will contain the octet string and LEN will be + * the number of octets. + * + * GeneralString: VALUE will contain the generalstring and LEN will + * be the number of octets. + * + * BIT STRING: VALUE will contain the bit string organized by bytes + * and LEN will be the number of bits. + * + * CHOICE: If NAME indicates a choice type, VALUE will specify the + * alternative selected. + * + * ANY: If NAME indicates an any type, VALUE will indicate the DER + * encoding of the structure actually used. + * + * Returns: %ASN1_SUCCESS if value is returned, + * %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element, + * %ASN1_VALUE_NOT_FOUND if there isn't any value for the element + * selected, and %ASN1_MEM_ERROR if The value vector isn't big enough + * to store the result, and in this case @len will contain the number of + * bytes needed. On the occasion that the stored data are of zero-length + * this function may return %ASN1_SUCCESS even if the provided @len is zero. + **/ +int +asn1_read_value_type (asn1_node_const root, const char *name, void *ivalue, + int *len, unsigned int *etype) +{ + asn1_node_const node, p, p2; + int len2, len3, result; + int value_size = *len; + unsigned char *value = ivalue; + unsigned type; + + node = asn1_find_node (root, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + type = type_field (node->type); + + if ((type != ASN1_ETYPE_NULL) && + (type != ASN1_ETYPE_CHOICE) && + !(node->type & CONST_DEFAULT) && !(node->type & CONST_ASSIGN) && + (node->value == NULL)) + return ASN1_VALUE_NOT_FOUND; + + if (etype) + *etype = type; + switch (type) + { + case ASN1_ETYPE_NULL: + PUT_STR_VALUE (value, value_size, "NULL"); + break; + case ASN1_ETYPE_BOOLEAN: + if ((node->type & CONST_DEFAULT) && (node->value == NULL)) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if (p->type & CONST_TRUE) + { + PUT_STR_VALUE (value, value_size, "TRUE"); + } + else + { + PUT_STR_VALUE (value, value_size, "FALSE"); + } + } + else if (node->value[0] == 'T') + { + PUT_STR_VALUE (value, value_size, "TRUE"); + } + else + { + PUT_STR_VALUE (value, value_size, "FALSE"); + } + break; + case ASN1_ETYPE_INTEGER: + case ASN1_ETYPE_ENUMERATED: + if ((node->type & CONST_DEFAULT) && (node->value == NULL)) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if ((c_isdigit (p->value[0])) || (p->value[0] == '-') + || (p->value[0] == '+')) + { + result = _asn1_convert_integer + (p->value, value, value_size, len); + if (result != ASN1_SUCCESS) + return result; + } + else + { /* is an identifier like v1 */ + p2 = node->down; + while (p2) + { + if (type_field (p2->type) == ASN1_ETYPE_CONSTANT) + { + if (!_asn1_strcmp (p2->name, p->value)) + { + result = _asn1_convert_integer + (p2->value, value, value_size, + len); + if (result != ASN1_SUCCESS) + return result; + break; + } + } + p2 = p2->right; + } + } + } + else + { + len2 = -1; + result = asn1_get_octet_der + (node->value, node->value_len, &len2, value, value_size, + len); + if (result != ASN1_SUCCESS) + return result; + } + break; + case ASN1_ETYPE_OBJECT_ID: + if (node->type & CONST_ASSIGN) + { + *len = 0; + if (value) + value[0] = 0; + p = node->down; + while (p) + { + if (type_field (p->type) == ASN1_ETYPE_CONSTANT) + { + ADD_STR_VALUE (value, value_size, p->value); + if (p->right) + { + ADD_STR_VALUE (value, value_size, "."); + } + } + p = p->right; + } + (*len)++; + } + else if ((node->type & CONST_DEFAULT) && (node->value == NULL)) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + PUT_STR_VALUE (value, value_size, p->value); + } + else + { + PUT_STR_VALUE (value, value_size, node->value); + } + break; + case ASN1_ETYPE_GENERALIZED_TIME: + case ASN1_ETYPE_UTC_TIME: + PUT_AS_STR_VALUE (value, value_size, node->value, node->value_len); + break; + case ASN1_ETYPE_OCTET_STRING: + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + len2 = -1; + result = asn1_get_octet_der + (node->value, node->value_len, &len2, value, value_size, + len); + if (result != ASN1_SUCCESS) + return result; + break; + case ASN1_ETYPE_BIT_STRING: + len2 = -1; + result = asn1_get_bit_der + (node->value, node->value_len, &len2, value, value_size, + len); + if (result != ASN1_SUCCESS) + return result; + break; + case ASN1_ETYPE_CHOICE: + PUT_STR_VALUE (value, value_size, node->down->name); + break; + case ASN1_ETYPE_ANY: + len3 = -1; + len2 = asn1_get_length_der (node->value, node->value_len, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + PUT_VALUE (value, value_size, node->value + len3, len2); + break; + default: + return ASN1_ELEMENT_NOT_FOUND; + break; + } + return ASN1_SUCCESS; +} + + +/** + * asn1_read_tag: + * @root: pointer to a structure + * @name: the name of the element inside a structure. + * @tagValue: variable that will contain the TAG value. + * @classValue: variable that will specify the TAG type. + * + * Returns the TAG and the CLASS of one element inside a structure. + * CLASS can have one of these constants: %ASN1_CLASS_APPLICATION, + * %ASN1_CLASS_UNIVERSAL, %ASN1_CLASS_PRIVATE or + * %ASN1_CLASS_CONTEXT_SPECIFIC. + * + * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if + * @name is not a valid element. + **/ +int +asn1_read_tag (asn1_node_const root, const char *name, int *tagValue, + int *classValue) +{ + asn1_node node, p, pTag; + + node = asn1_find_node (root, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node->down; + + /* pTag will points to the IMPLICIT TAG */ + pTag = NULL; + if (node->type & CONST_TAG) + { + while (p) + { + if (type_field (p->type) == ASN1_ETYPE_TAG) + { + if ((p->type & CONST_IMPLICIT) && (pTag == NULL)) + pTag = p; + else if (p->type & CONST_EXPLICIT) + pTag = NULL; + } + p = p->right; + } + } + + if (pTag) + { + *tagValue = _asn1_strtoul (pTag->value, NULL, 10); + + if (pTag->type & CONST_APPLICATION) + *classValue = ASN1_CLASS_APPLICATION; + else if (pTag->type & CONST_UNIVERSAL) + *classValue = ASN1_CLASS_UNIVERSAL; + else if (pTag->type & CONST_PRIVATE) + *classValue = ASN1_CLASS_PRIVATE; + else + *classValue = ASN1_CLASS_CONTEXT_SPECIFIC; + } + else + { + unsigned type = type_field (node->type); + *classValue = ASN1_CLASS_UNIVERSAL; + + switch (type) + { + CASE_HANDLED_ETYPES: + *tagValue = _asn1_tags[type].tag; + break; + case ASN1_ETYPE_TAG: + case ASN1_ETYPE_CHOICE: + case ASN1_ETYPE_ANY: + *tagValue = -1; + break; + default: + break; + } + } + + return ASN1_SUCCESS; +} + +/** + * asn1_read_node_value: + * @node: pointer to a node. + * @data: a point to a asn1_data_node_st + * + * Returns the value a data node inside a asn1_node structure. + * The data returned should be handled as constant values. + * + * Returns: %ASN1_SUCCESS if the node exists. + **/ +int +asn1_read_node_value (asn1_node_const node, asn1_data_node_st * data) +{ + data->name = node->name; + data->value = node->value; + data->value_len = node->value_len; + data->type = type_field (node->type); + + return ASN1_SUCCESS; +} diff --git a/grub-core/lib/libtasn1/lib/element.h b/grub-core/lib/libtasn1/lib/element.h new file mode 100644 index 0000000000..440a33f4bb --- /dev/null +++ b/grub-core/lib/libtasn1/lib/element.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2000-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _ELEMENT_H +#define _ELEMENT_H + + +struct node_tail_cache_st +{ + asn1_node head; /* the first element of the sequence */ + asn1_node tail; +}; + +int _asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcached); + +int _asn1_convert_integer (const unsigned char *value, + unsigned char *value_out, + int value_out_size, int *len); + +void _asn1_hierarchical_name (asn1_node_const node, char *name, int name_size); + +#endif diff --git a/grub-core/lib/libtasn1/lib/errors.c b/grub-core/lib/libtasn1/lib/errors.c new file mode 100644 index 0000000000..cee74daf79 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/errors.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2002-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include +#ifdef STDC_HEADERS +#include +#endif + +#define LIBTASN1_ERROR_ENTRY(name) { #name, name } + +struct libtasn1_error_entry +{ + const char *name; + int number; +}; +typedef struct libtasn1_error_entry libtasn1_error_entry; + +static const libtasn1_error_entry error_algorithms[] = { + LIBTASN1_ERROR_ENTRY (ASN1_SUCCESS), + LIBTASN1_ERROR_ENTRY (ASN1_FILE_NOT_FOUND), + LIBTASN1_ERROR_ENTRY (ASN1_ELEMENT_NOT_FOUND), + LIBTASN1_ERROR_ENTRY (ASN1_IDENTIFIER_NOT_FOUND), + LIBTASN1_ERROR_ENTRY (ASN1_DER_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_VALUE_NOT_FOUND), + LIBTASN1_ERROR_ENTRY (ASN1_GENERIC_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_VALUE_NOT_VALID), + LIBTASN1_ERROR_ENTRY (ASN1_TAG_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_TAG_IMPLICIT), + LIBTASN1_ERROR_ENTRY (ASN1_ERROR_TYPE_ANY), + LIBTASN1_ERROR_ENTRY (ASN1_SYNTAX_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_MEM_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_MEM_ALLOC_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_DER_OVERFLOW), + LIBTASN1_ERROR_ENTRY (ASN1_NAME_TOO_LONG), + LIBTASN1_ERROR_ENTRY (ASN1_ARRAY_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_ELEMENT_NOT_EMPTY), + LIBTASN1_ERROR_ENTRY (ASN1_TIME_ENCODING_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_RECURSION), + {0, 0} +}; + +/** + * asn1_perror: + * @error: is an error returned by a libtasn1 function. + * + * Prints a string to stderr with a description of an error. This + * function is like perror(). The only difference is that it accepts + * an error returned by a libtasn1 function. + * + * Since: 1.6 + **/ +void +asn1_perror (int error) +{ + const char *str = asn1_strerror (error); + fprintf (stderr, "LIBTASN1 ERROR: %s\n", str ? str : "(null)"); +} + +/** + * asn1_strerror: + * @error: is an error returned by a libtasn1 function. + * + * Returns a string with a description of an error. This function is + * similar to strerror. The only difference is that it accepts an + * error (number) returned by a libtasn1 function. + * + * Returns: Pointer to static zero-terminated string describing error + * code. + * + * Since: 1.6 + **/ +const char * +asn1_strerror (int error) +{ + const libtasn1_error_entry *p; + + for (p = error_algorithms; p->name != NULL; p++) + if (p->number == error) + return p->name + sizeof ("ASN1_") - 1; + + return NULL; +} diff --git a/grub-core/lib/libtasn1/lib/gstr.c b/grub-core/lib/libtasn1/lib/gstr.c new file mode 100644 index 0000000000..e91a3a151c --- /dev/null +++ b/grub-core/lib/libtasn1/lib/gstr.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2002-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include +#include "gstr.h" + +/* These function are like strcat, strcpy. They only + * do bounds checking (they shouldn't cause buffer overruns), + * and they always produce null terminated strings. + * + * They should be used only with null terminated strings. + */ +void +_asn1_str_cat (char *dest, size_t dest_tot_size, const char *src) +{ + size_t str_size = strlen (src); + size_t dest_size = strlen (dest); + + if (dest_tot_size - dest_size > str_size) + { + strcat (dest, src); + } + else + { + if (dest_tot_size - dest_size > 0) + { + strncat (dest, src, (dest_tot_size - dest_size) - 1); + dest[dest_tot_size - 1] = 0; + } + } +} + +/* Returns the bytes copied (not including the null terminator) */ +unsigned int +_asn1_str_cpy (char *dest, size_t dest_tot_size, const char *src) +{ + size_t str_size = strlen (src); + + if (dest_tot_size > str_size) + { + strcpy (dest, src); + return str_size; + } + else + { + if (dest_tot_size > 0) + { + str_size = dest_tot_size - 1; + memcpy (dest, src, str_size); + dest[str_size] = 0; + return str_size; + } + else + return 0; + } +} diff --git a/grub-core/lib/libtasn1/lib/gstr.h b/grub-core/lib/libtasn1/lib/gstr.h new file mode 100644 index 0000000000..48229844ff --- /dev/null +++ b/grub-core/lib/libtasn1/lib/gstr.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2002-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef GSTR_H +# define GSTR_H + +unsigned int _asn1_str_cpy (char *dest, size_t dest_tot_size, + const char *src); +void _asn1_str_cat (char *dest, size_t dest_tot_size, const char *src); + +#define Estrcpy(x,y) _asn1_str_cpy(x,ASN1_MAX_ERROR_DESCRIPTION_SIZE,y) +#define Estrcat(x,y) _asn1_str_cat(x,ASN1_MAX_ERROR_DESCRIPTION_SIZE,y) + +inline static +void safe_memset(void *data, int c, size_t size) +{ + volatile unsigned volatile_zero = 0; + volatile char *vdata = (volatile char*)data; + + /* This is based on a nice trick for safe memset, + * sent by David Jacobson in the openssl-dev mailing list. + */ + + if (size > 0) do { + memset(data, c, size); + } while(vdata[volatile_zero] != c); +} + +#endif /* GSTR_H */ diff --git a/grub-core/lib/libtasn1/lib/int.h b/grub-core/lib/libtasn1/lib/int.h new file mode 100644 index 0000000000..ea1625786c --- /dev/null +++ b/grub-core/lib/libtasn1/lib/int.h @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2002-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef INT_H +#define INT_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include + +#define ASN1_SMALL_VALUE_SIZE 16 + +/* This structure is also in libtasn1.h, but then contains less + fields. You cannot make any modifications to these first fields + without breaking ABI. */ +struct asn1_node_st +{ + /* public fields: */ + char name[ASN1_MAX_NAME_SIZE + 1]; /* Node name */ + unsigned int name_hash; + unsigned int type; /* Node type */ + unsigned char *value; /* Node value */ + int value_len; + asn1_node down; /* Pointer to the son node */ + asn1_node right; /* Pointer to the brother node */ + asn1_node left; /* Pointer to the next list element */ + /* private fields: */ + unsigned char small_value[ASN1_SMALL_VALUE_SIZE]; /* For small values */ + + /* values used during decoding/coding */ + int tmp_ival; + unsigned start; /* the start of the DER sequence - if decoded */ + unsigned end; /* the end of the DER sequence - if decoded */ +}; + +typedef struct tag_and_class_st +{ + unsigned tag; + unsigned class; + const char *desc; +} tag_and_class_st; + +/* the types that are handled in _asn1_tags */ +#define CASE_HANDLED_ETYPES \ + case ASN1_ETYPE_NULL: \ + case ASN1_ETYPE_BOOLEAN: \ + case ASN1_ETYPE_INTEGER: \ + case ASN1_ETYPE_ENUMERATED: \ + case ASN1_ETYPE_OBJECT_ID: \ + case ASN1_ETYPE_OCTET_STRING: \ + case ASN1_ETYPE_GENERALSTRING: \ + case ASN1_ETYPE_NUMERIC_STRING: \ + case ASN1_ETYPE_IA5_STRING: \ + case ASN1_ETYPE_TELETEX_STRING: \ + case ASN1_ETYPE_PRINTABLE_STRING: \ + case ASN1_ETYPE_UNIVERSAL_STRING: \ + case ASN1_ETYPE_BMP_STRING: \ + case ASN1_ETYPE_UTF8_STRING: \ + case ASN1_ETYPE_VISIBLE_STRING: \ + case ASN1_ETYPE_BIT_STRING: \ + case ASN1_ETYPE_SEQUENCE: \ + case ASN1_ETYPE_SEQUENCE_OF: \ + case ASN1_ETYPE_SET: \ + case ASN1_ETYPE_UTC_TIME: \ + case ASN1_ETYPE_GENERALIZED_TIME: \ + case ASN1_ETYPE_SET_OF + +#define ETYPE_TAG(etype) (_asn1_tags[etype].tag) +#define ETYPE_CLASS(etype) (_asn1_tags[etype].class) +#define ETYPE_OK(etype) (((etype) != ASN1_ETYPE_INVALID && \ + (etype) <= _asn1_tags_size && \ + _asn1_tags[(etype)].desc != NULL)?1:0) + +#define ETYPE_IS_STRING(etype) ((etype == ASN1_ETYPE_GENERALSTRING || \ + etype == ASN1_ETYPE_NUMERIC_STRING || etype == ASN1_ETYPE_IA5_STRING || \ + etype == ASN1_ETYPE_TELETEX_STRING || etype == ASN1_ETYPE_PRINTABLE_STRING || \ + etype == ASN1_ETYPE_UNIVERSAL_STRING || etype == ASN1_ETYPE_BMP_STRING || \ + etype == ASN1_ETYPE_UTF8_STRING || etype == ASN1_ETYPE_VISIBLE_STRING || \ + etype == ASN1_ETYPE_OCTET_STRING)?1:0) + +extern unsigned int _asn1_tags_size; +extern const tag_and_class_st _asn1_tags[]; + +#define _asn1_strlen(s) strlen((const char *) s) +#define _asn1_strtol(n,e,b) strtol((const char *) n, e, b) +#define _asn1_strtoul(n,e,b) strtoul((const char *) n, e, b) +#define _asn1_strcmp(a,b) strcmp((const char *)a, (const char *)b) +#define _asn1_strcpy(a,b) strcpy((char *)a, (const char *)b) +#define _asn1_strcat(a,b) strcat((char *)a, (const char *)b) + +#if SIZEOF_UNSIGNED_LONG_INT == 8 +# define _asn1_strtou64(n,e,b) strtoul((const char *) n, e, b) +#else +# define _asn1_strtou64(n,e,b) strtoull((const char *) n, e, b) +#endif + +#define MAX_LOG_SIZE 1024 /* maximum number of characters of a log message */ + +/* Define used for visiting trees. */ +#define UP 1 +#define RIGHT 2 +#define DOWN 3 + +/***********************************************************************/ +/* List of constants to better specify the type of typedef asn1_node_st. */ +/***********************************************************************/ +/* Used with TYPE_TAG */ +#define CONST_UNIVERSAL (1U<<8) +#define CONST_PRIVATE (1U<<9) +#define CONST_APPLICATION (1U<<10) +#define CONST_EXPLICIT (1U<<11) +#define CONST_IMPLICIT (1U<<12) + +#define CONST_TAG (1U<<13) /* Used in ASN.1 assignement */ +#define CONST_OPTION (1U<<14) +#define CONST_DEFAULT (1U<<15) +#define CONST_TRUE (1U<<16) +#define CONST_FALSE (1U<<17) + +#define CONST_LIST (1U<<18) /* Used with TYPE_INTEGER and TYPE_BIT_STRING */ +#define CONST_MIN_MAX (1U<<19) + +#define CONST_1_PARAM (1U<<20) + +#define CONST_SIZE (1U<<21) + +#define CONST_DEFINED_BY (1U<<22) + +/* Those two are deprecated and used for backwards compatibility */ +#define CONST_GENERALIZED (1U<<23) +#define CONST_UTC (1U<<24) + +/* #define CONST_IMPORTS (1U<<25) */ + +#define CONST_NOT_USED (1U<<26) +#define CONST_SET (1U<<27) +#define CONST_ASSIGN (1U<<28) + +#define CONST_DOWN (1U<<29) +#define CONST_RIGHT (1U<<30) + + +#define ASN1_ETYPE_TIME 17 +/****************************************/ +/* Returns the first 8 bits. */ +/* Used with the field type of asn1_node_st */ +/****************************************/ +inline static unsigned int +type_field (unsigned int ntype) +{ + return (ntype & 0xff); +} + +/* To convert old types from a static structure */ +inline static unsigned int +convert_old_type (unsigned int ntype) +{ + unsigned int type = ntype & 0xff; + if (type == ASN1_ETYPE_TIME) + { + if (ntype & CONST_UTC) + type = ASN1_ETYPE_UTC_TIME; + else + type = ASN1_ETYPE_GENERALIZED_TIME; + + ntype &= ~(CONST_UTC | CONST_GENERALIZED); + ntype &= 0xffffff00; + ntype |= type; + + return ntype; + } + else + return ntype; +} + +static inline +void *_asn1_realloc(void *ptr, size_t size) +{ + void *ret; + + if (size == 0) + return ptr; + + ret = realloc(ptr, size); + if (ret == NULL) + { + free(ptr); + } + return ret; +} + +#endif /* INT_H */ diff --git a/grub-core/lib/libtasn1/lib/parser_aux.c b/grub-core/lib/libtasn1/lib/parser_aux.c new file mode 100644 index 0000000000..d5dbbf8765 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/parser_aux.c @@ -0,0 +1,1173 @@ +/* + * Copyright (C) 2000-2016 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include // WORD_BIT + +#include "int.h" +#include "parser_aux.h" +#include "gstr.h" +#include "structure.h" +#include "element.h" +#include "c-ctype.h" + +char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1]; /* identifier name not found */ + +/* Return a hash of the N bytes of X using the method described by + Bruno Haible in https://www.haible.de/bruno/hashfunc.html. + Note that while many hash functions reduce their result via modulo + to a 0..table_size-1 range, this function does not do that. + + This implementation has been changed from size_t -> unsigned int. */ + +#ifdef __clang__ +__attribute__((no_sanitize("integer"))) +#endif +_GL_ATTRIBUTE_PURE +static unsigned int +_asn1_hash_name (const char *x) +{ + const unsigned char *s = (unsigned char *) x; + unsigned h = 0; + + while (*s) + h = (*s++) + ((h << 9) | (h >> (WORD_BIT - 9))); + + return h; +} + +/******************************************************/ +/* Function : _asn1_add_static_node */ +/* Description: creates a new NODE_ASN element and */ +/* puts it in the list pointed by e_list. */ +/* Parameters: */ +/* e_list: of type list_type; must be NULL initially */ +/* type: type of the new element (see ASN1_ETYPE_ */ +/* and CONST_ constants). */ +/* Return: pointer to the new element. */ +/******************************************************/ +asn1_node +_asn1_add_static_node (list_type **e_list, unsigned int type) +{ + list_type *p; + asn1_node punt; + + punt = calloc (1, sizeof (struct asn1_node_st)); + if (punt == NULL) + return NULL; + + p = malloc (sizeof (list_type)); + if (p == NULL) + { + free (punt); + return NULL; + } + + p->node = punt; + p->next = *e_list; + *e_list = p; + + punt->type = type; + + return punt; +} + +static +int _asn1_add_static_node2 (list_type **e_list, asn1_node node) +{ + list_type *p; + + p = malloc (sizeof (list_type)); + if (p == NULL) + { + return -1; + } + + p->node = node; + p->next = *e_list; + *e_list = p; + + return 0; +} + +/** + * asn1_find_node: + * @pointer: NODE_ASN element pointer. + * @name: null terminated string with the element's name to find. + * + * Searches for an element called @name starting from @pointer. The + * name is composed by different identifiers separated by dots. When + * *@pointer has a name, the first identifier must be the name of + * *@pointer, otherwise it must be the name of one child of *@pointer. + * + * Returns: the search result, or %NULL if not found. + **/ +asn1_node +asn1_find_node (asn1_node_const pointer, const char *name) +{ + asn1_node_const p; + char *n_end, n[ASN1_MAX_NAME_SIZE + 1]; + const char *n_start; + unsigned int nsize; + unsigned int nhash; + + if (pointer == NULL) + return NULL; + + if (name == NULL) + return NULL; + + p = pointer; + n_start = name; + + if (name[0] == '?' && name[1] == 'C' && p->name[0] == '?') + { /* ?CURRENT */ + n_start = strchr(n_start, '.'); + if (n_start) + n_start++; + } + else if (p->name[0] != 0) + { /* has *pointer got a name ? */ + n_end = strchr (n_start, '.'); /* search the first dot */ + if (n_end) + { + nsize = n_end - n_start; + if (nsize >= sizeof(n)) + return NULL; + + memcpy (n, n_start, nsize); + n[nsize] = 0; + n_start = n_end; + n_start++; + + nhash = _asn1_hash_name (n); + } + else + { + _asn1_str_cpy (n, sizeof (n), n_start); + nhash = _asn1_hash_name (n); + + n_start = NULL; + } + + while (p) + { + if (nhash == p->name_hash && (!strcmp (p->name, n))) + break; + else + p = p->right; + } /* while */ + + if (p == NULL) + return NULL; + } + else + { /* *pointer doesn't have a name */ + if (n_start[0] == 0) + return (asn1_node) p; + } + + while (n_start) + { /* Has the end of NAME been reached? */ + n_end = strchr (n_start, '.'); /* search the next dot */ + if (n_end) + { + nsize = n_end - n_start; + if (nsize >= sizeof(n)) + return NULL; + + memcpy (n, n_start, nsize); + n[nsize] = 0; + n_start = n_end; + n_start++; + + nhash = _asn1_hash_name (n); + } + else + { + _asn1_str_cpy (n, sizeof (n), n_start); + nhash = _asn1_hash_name (n); + n_start = NULL; + } + + if (p->down == NULL) + return NULL; + + p = p->down; + if (p == NULL) + return NULL; + + /* The identifier "?LAST" indicates the last element + in the right chain. */ + if (n[0] == '?' && n[1] == 'L') /* ?LAST */ + { + while (p->right) + p = p->right; + } + else + { /* no "?LAST" */ + while (p) + { + if (p->name_hash == nhash && !strcmp (p->name, n)) + break; + else + p = p->right; + } + } + if (p == NULL) + return NULL; + } /* while */ + + return (asn1_node) p; +} + + +/******************************************************************/ +/* Function : _asn1_set_value */ +/* Description: sets the field VALUE in a NODE_ASN element. The */ +/* previous value (if exist) will be lost */ +/* Parameters: */ +/* node: element pointer. */ +/* value: pointer to the value that you want to set. */ +/* len: character number of value. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +asn1_node +_asn1_set_value (asn1_node node, const void *value, unsigned int len) +{ + if (node == NULL) + return node; + if (node->value) + { + if (node->value != node->small_value) + free (node->value); + node->value = NULL; + node->value_len = 0; + } + + if (!len) + return node; + + if (len < sizeof (node->small_value)) + { + node->value = node->small_value; + } + else + { + node->value = malloc (len); + if (node->value == NULL) + return NULL; + } + node->value_len = len; + + memcpy (node->value, value, len); + return node; +} + +/******************************************************************/ +/* Function : _asn1_set_value_lv */ +/* Description: sets the field VALUE in a NODE_ASN element. The */ +/* previous value (if exist) will be lost. The value */ +/* given is stored as an length-value format (LV */ +/* Parameters: */ +/* node: element pointer. */ +/* value: pointer to the value that you want to set. */ +/* len: character number of value. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +asn1_node +_asn1_set_value_lv (asn1_node node, const void *value, unsigned int len) +{ + int len2; + void *temp; + + if (node == NULL) + return node; + + asn1_length_der (len, NULL, &len2); + temp = malloc (len + len2); + if (temp == NULL) + return NULL; + + asn1_octet_der (value, len, temp, &len2); + return _asn1_set_value_m (node, temp, len2); +} + +/* the same as _asn1_set_value except that it sets an already malloc'ed + * value. + */ +asn1_node +_asn1_set_value_m (asn1_node node, void *value, unsigned int len) +{ + if (node == NULL) + return node; + + if (node->value) + { + if (node->value != node->small_value) + free (node->value); + node->value = NULL; + node->value_len = 0; + } + + if (!len) + return node; + + node->value = value; + node->value_len = len; + + return node; +} + +/******************************************************************/ +/* Function : _asn1_append_value */ +/* Description: appends to the field VALUE in a NODE_ASN element. */ +/* */ +/* Parameters: */ +/* node: element pointer. */ +/* value: pointer to the value that you want to be appended. */ +/* len: character number of value. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +asn1_node +_asn1_append_value (asn1_node node, const void *value, unsigned int len) +{ + if (node == NULL) + return node; + + if (node->value == NULL) + return _asn1_set_value (node, value, len); + + if (len == 0) + return node; + + if (node->value == node->small_value) + { + /* value is in node */ + int prev_len = node->value_len; + node->value_len += len; + node->value = malloc (node->value_len); + if (node->value == NULL) + { + node->value_len = 0; + return NULL; + } + + if (prev_len > 0) + memcpy (node->value, node->small_value, prev_len); + + memcpy (&node->value[prev_len], value, len); + + return node; + } + else /* if (node->value != NULL && node->value != node->small_value) */ + { + /* value is allocated */ + int prev_len = node->value_len; + node->value_len += len; + + node->value = _asn1_realloc (node->value, node->value_len); + if (node->value == NULL) + { + node->value_len = 0; + return NULL; + } + + memcpy (&node->value[prev_len], value, len); + + return node; + } +} + +/******************************************************************/ +/* Function : _asn1_set_name */ +/* Description: sets the field NAME in a NODE_ASN element. The */ +/* previous value (if exist) will be lost */ +/* Parameters: */ +/* node: element pointer. */ +/* name: a null terminated string with the name that you want */ +/* to set. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +asn1_node +_asn1_set_name (asn1_node node, const char *name) +{ + if (node == NULL) + return node; + + _asn1_str_cpy (node->name, sizeof (node->name), name ? name : ""); + node->name_hash = _asn1_hash_name (node->name); + + return node; +} + +/******************************************************************/ +/* Function : _asn1_cpy_name */ +/* Description: copies the field NAME in a NODE_ASN element. */ +/* Parameters: */ +/* dst: a dest element pointer. */ +/* src: a source element pointer. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +asn1_node +_asn1_cpy_name (asn1_node dst, asn1_node_const src) +{ + if (dst == NULL) + return dst; + + if (src == NULL) + { + dst->name[0] = 0; + dst->name_hash = _asn1_hash_name (dst->name); + return dst; + } + + _asn1_str_cpy (dst->name, sizeof (dst->name), src->name); + dst->name_hash = src->name_hash; + + return dst; +} + +/******************************************************************/ +/* Function : _asn1_set_right */ +/* Description: sets the field RIGHT in a NODE_ASN element. */ +/* Parameters: */ +/* node: element pointer. */ +/* right: pointer to a NODE_ASN element that you want be pointed*/ +/* by NODE. */ +/* Return: pointer to *NODE. */ +/******************************************************************/ +asn1_node +_asn1_set_right (asn1_node node, asn1_node right) +{ + if (node == NULL) + return node; + node->right = right; + if (right) + right->left = node; + return node; +} + + +/******************************************************************/ +/* Function : _asn1_get_last_right */ +/* Description: return the last element along the right chain. */ +/* Parameters: */ +/* node: starting element pointer. */ +/* Return: pointer to the last element along the right chain. */ +/******************************************************************/ +asn1_node +_asn1_get_last_right (asn1_node_const node) +{ + asn1_node_const p; + + if (node == NULL) + return NULL; + p = node; + while (p->right) + p = p->right; + return (asn1_node) p; +} + +/******************************************************************/ +/* Function : _asn1_remove_node */ +/* Description: gets free the memory allocated for an NODE_ASN */ +/* element (not the elements pointed by it). */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* flags: ASN1_DELETE_FLAG_* */ +/******************************************************************/ +void +_asn1_remove_node (asn1_node node, unsigned int flags) +{ + if (node == NULL) + return; + + if (node->value != NULL) + { + if (flags & ASN1_DELETE_FLAG_ZEROIZE) + { + safe_memset(node->value, 0, node->value_len); + } + + if (node->value != node->small_value) + free (node->value); + } + free (node); +} + +/******************************************************************/ +/* Function : _asn1_find_up */ +/* Description: return the father of the NODE_ASN element. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: Null if not found. */ +/******************************************************************/ +asn1_node +_asn1_find_up (asn1_node_const node) +{ + asn1_node_const p; + + if (node == NULL) + return NULL; + + p = node; + + while ((p->left != NULL) && (p->left->right == p)) + p = p->left; + + return p->left; +} + +static +unsigned _asn1_is_up (asn1_node_const up_cand, asn1_node_const down) +{ + asn1_node_const d, u; + + if (up_cand == NULL || down == NULL) + return 0; + + d = down; + + while ((u = _asn1_find_up(d)) != NULL && u != d) + { + if (u == up_cand) + return 1; + d = u; + } + + return 0; +} + +/******************************************************************/ +/* Function : _asn1_delete_node_from_list */ +/* Description: deletes the list element given */ +/******************************************************************/ +void +_asn1_delete_node_from_list (list_type *list, asn1_node node) +{ + list_type *p = list; + + while (p) + { + if (p->node == node) + p->node = NULL; + p = p->next; + } +} + +/******************************************************************/ +/* Function : _asn1_delete_list */ +/* Description: deletes the list elements (not the elements */ +/* pointed by them). */ +/******************************************************************/ +void +_asn1_delete_list (list_type *e_list) +{ + list_type *p; + + while (e_list) + { + p = e_list; + e_list = e_list->next; + free (p); + } +} + +/******************************************************************/ +/* Function : _asn1_delete_list_and nodes */ +/* Description: deletes the list elements and the elements */ +/* pointed by them. */ +/******************************************************************/ +void +_asn1_delete_list_and_nodes (list_type *e_list) +{ + list_type *p; + + while (e_list) + { + p = e_list; + e_list = e_list->next; + _asn1_remove_node (p->node, 0); + free (p); + } +} + + +char * +_asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE]) +{ + uint64_t d, r; + char temp[LTOSTR_MAX_SIZE]; + int count, k, start; + uint64_t val; + + if (v < 0) + { + str[0] = '-'; + start = 1; + val = -((uint64_t)v); + } + else + { + val = v; + start = 0; + } + + count = 0; + do + { + d = val / 10; + r = val - d * 10; + temp[start + count] = '0' + (char) r; + count++; + val = d; + } + while (val && ((start+count) < LTOSTR_MAX_SIZE-1)); + + for (k = 0; k < count; k++) + str[k + start] = temp[start + count - k - 1]; + str[count + start] = 0; + return str; +} + + +/******************************************************************/ +/* Function : _asn1_change_integer_value */ +/* Description: converts into DER coding the value assign to an */ +/* INTEGER constant. */ +/* Parameters: */ +/* node: root of an ASN1element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_change_integer_value (asn1_node node) +{ + asn1_node p; + unsigned char val[SIZEOF_UNSIGNED_LONG_INT]; + unsigned char val2[SIZEOF_UNSIGNED_LONG_INT + 1]; + int len; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + while (p) + { + if ((type_field (p->type) == ASN1_ETYPE_INTEGER) + && (p->type & CONST_ASSIGN)) + { + if (p->value) + { + _asn1_convert_integer (p->value, val, sizeof (val), &len); + asn1_octet_der (val, len, val2, &len); + _asn1_set_value (p, val2, len); + } + } + + if (p->down) + { + p = p->down; + } + else + { + if (p == node) + p = NULL; + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == node) + { + p = NULL; + break; + } + if (p && p->right) + { + p = p->right; + break; + } + } + } + } + } + + return ASN1_SUCCESS; +} + +#define MAX_CONSTANTS 1024 +/******************************************************************/ +/* Function : _asn1_expand_object_id */ +/* Description: expand the IDs of an OBJECT IDENTIFIER constant. */ +/* Parameters: */ +/* list: root of an object list */ +/* node: root of an ASN1 element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_expand_object_id (list_type **list, asn1_node node) +{ + asn1_node p, p2, p3, p4, p5; + char name_root[ASN1_MAX_NAME_SIZE], name2[2 * ASN1_MAX_NAME_SIZE + 1]; + int move, tlen, tries; + unsigned max_constants; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + _asn1_str_cpy (name_root, sizeof (name_root), node->name); + + p = node; + move = DOWN; + tries = 0; + + while (!((p == node) && (move == UP))) + { + if (move != UP) + { + if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) + && (p->type & CONST_ASSIGN)) + { + p2 = p->down; + if (p2 && (type_field (p2->type) == ASN1_ETYPE_CONSTANT)) + { + if (p2->value && !c_isdigit (p2->value[0])) + { + _asn1_str_cpy (name2, sizeof (name2), name_root); + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), (char *) p2->value); + p3 = asn1_find_node (node, name2); + if (!p3 || _asn1_is_up(p2, p3) || + (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) || + !(p3->type & CONST_ASSIGN)) + return ASN1_ELEMENT_NOT_FOUND; + + _asn1_set_down (p, p2->right); + if (p2->down) + _asn1_delete_structure (*list, &p2->down, 0); + _asn1_delete_node_from_list(*list, p2); + _asn1_remove_node (p2, 0); + p2 = p; + p4 = p3->down; + max_constants = 0; + while (p4) + { + if (type_field (p4->type) == ASN1_ETYPE_CONSTANT) + { + max_constants++; + if (max_constants == MAX_CONSTANTS) + return ASN1_RECURSION; + + p5 = + _asn1_add_single_node (ASN1_ETYPE_CONSTANT); + _asn1_set_name (p5, p4->name); + if (p4->value) + { + tlen = _asn1_strlen (p4->value); + if (tlen > 0) + _asn1_set_value (p5, p4->value, tlen + 1); + } + _asn1_add_static_node2(list, p5); + + if (p2 == p) + { + _asn1_set_right (p5, p->down); + _asn1_set_down (p, p5); + } + else + { + _asn1_set_right (p5, p2->right); + _asn1_set_right (p2, p5); + } + p2 = p5; + } + p4 = p4->right; + } + move = DOWN; + + tries++; + if (tries >= EXPAND_OBJECT_ID_MAX_RECURSION) + return ASN1_RECURSION; + + continue; + } + } + } + move = DOWN; + } + else + move = RIGHT; + + tries = 0; + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p && p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + /*******************************/ + /* expand DEFAULT */ + /*******************************/ + p = node; + move = DOWN; + + while (!((p == node) && (move == UP))) + { + if (move != UP) + { + if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) && + (p->type & CONST_DEFAULT)) + { + p2 = p->down; + if (p2 && (type_field (p2->type) == ASN1_ETYPE_DEFAULT)) + { + _asn1_str_cpy (name2, sizeof (name2), name_root); + _asn1_str_cat (name2, sizeof (name2), "."); + if (p2->value) + _asn1_str_cat (name2, sizeof (name2), (char *) p2->value); + p3 = asn1_find_node (node, name2); + if (!p3 || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) + || !(p3->type & CONST_ASSIGN)) + return ASN1_ELEMENT_NOT_FOUND; + p4 = p3->down; + name2[0] = 0; + while (p4) + { + if (type_field (p4->type) == ASN1_ETYPE_CONSTANT) + { + if (p4->value == NULL) + return ASN1_VALUE_NOT_FOUND; + + if (name2[0]) + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), + (char *) p4->value); + } + p4 = p4->right; + } + tlen = strlen (name2); + if (tlen > 0) + _asn1_set_value (p2, name2, tlen + 1); + } + } + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p && p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + return ASN1_SUCCESS; +} + + +/******************************************************************/ +/* Function : _asn1_type_set_config */ +/* Description: sets the CONST_SET and CONST_NOT_USED properties */ +/* in the fields of the SET elements. */ +/* Parameters: */ +/* node: root of an ASN1 element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_type_set_config (asn1_node node) +{ + asn1_node p, p2; + int move; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + move = DOWN; + + while (!((p == node) && (move == UP))) + { + if (move != UP) + { + if (type_field (p->type) == ASN1_ETYPE_SET) + { + p2 = p->down; + while (p2) + { + if (type_field (p2->type) != ASN1_ETYPE_TAG) + p2->type |= CONST_SET | CONST_NOT_USED; + p2 = p2->right; + } + } + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p && p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + return ASN1_SUCCESS; +} + + +/******************************************************************/ +/* Function : _asn1_check_identifier */ +/* Description: checks the definitions of all the identifiers */ +/* and the first element of an OBJECT_ID (e.g. {pkix 0 4}). */ +/* The _asn1_identifierMissing global variable is filled if */ +/* necessary. */ +/* Parameters: */ +/* node: root of an ASN1 element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ +/* ASN1_IDENTIFIER_NOT_FOUND if an identifier is not defined, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_check_identifier (asn1_node_const node) +{ + asn1_node_const p, p2; + char name2[ASN1_MAX_NAME_SIZE * 2 + 2]; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + while (p) + { + if (p->value && type_field (p->type) == ASN1_ETYPE_IDENTIFIER) + { + _asn1_str_cpy (name2, sizeof (name2), node->name); + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), (char *) p->value); + p2 = asn1_find_node (node, name2); + if (p2 == NULL) + { + if (p->value) + _asn1_str_cpy (_asn1_identifierMissing, sizeof(_asn1_identifierMissing), (char*)p->value); + else + _asn1_strcpy (_asn1_identifierMissing, "(null)"); + return ASN1_IDENTIFIER_NOT_FOUND; + } + } + else if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) && + (p->type & CONST_DEFAULT)) + { + p2 = p->down; + if (p2 && (type_field (p2->type) == ASN1_ETYPE_DEFAULT)) + { + _asn1_str_cpy (name2, sizeof (name2), node->name); + if (p2->value) + { + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), (char *) p2->value); + _asn1_str_cpy (_asn1_identifierMissing, sizeof(_asn1_identifierMissing), (char*)p2->value); + } + else + _asn1_strcpy (_asn1_identifierMissing, "(null)"); + + p2 = asn1_find_node (node, name2); + if (!p2 || (type_field (p2->type) != ASN1_ETYPE_OBJECT_ID) || + !(p2->type & CONST_ASSIGN)) + return ASN1_IDENTIFIER_NOT_FOUND; + else + _asn1_identifierMissing[0] = 0; + } + } + else if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) && + (p->type & CONST_ASSIGN)) + { + p2 = p->down; + if (p2 && (type_field (p2->type) == ASN1_ETYPE_CONSTANT)) + { + if (p2->value && !c_isdigit (p2->value[0])) + { + _asn1_str_cpy (name2, sizeof (name2), node->name); + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), (char *) p2->value); + _asn1_str_cpy (_asn1_identifierMissing, sizeof(_asn1_identifierMissing), (char*)p2->value); + + p2 = asn1_find_node (node, name2); + if (!p2 || (type_field (p2->type) != ASN1_ETYPE_OBJECT_ID) + || !(p2->type & CONST_ASSIGN)) + return ASN1_IDENTIFIER_NOT_FOUND; + else + _asn1_identifierMissing[0] = 0; + } + } + } + + if (p->down) + { + p = p->down; + } + else if (p->right) + p = p->right; + else + { + while (p) + { + p = _asn1_find_up (p); + if (p == node) + { + p = NULL; + break; + } + if (p && p->right) + { + p = p->right; + break; + } + } + } + } + + return ASN1_SUCCESS; +} + + +/******************************************************************/ +/* Function : _asn1_set_default_tag */ +/* Description: sets the default IMPLICIT or EXPLICIT property in */ +/* the tagged elements that don't have this declaration. */ +/* Parameters: */ +/* node: pointer to a DEFINITIONS element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL or not a pointer to */ +/* a DEFINITIONS element, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_set_default_tag (asn1_node node) +{ + asn1_node p; + + if ((node == NULL) || (type_field (node->type) != ASN1_ETYPE_DEFINITIONS)) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + while (p) + { + if ((type_field (p->type) == ASN1_ETYPE_TAG) && + !(p->type & CONST_EXPLICIT) && !(p->type & CONST_IMPLICIT)) + { + if (node->type & CONST_EXPLICIT) + p->type |= CONST_EXPLICIT; + else + p->type |= CONST_IMPLICIT; + } + + if (p->down) + { + p = p->down; + } + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == node) + { + p = NULL; + break; + } + if (p && p->right) + { + p = p->right; + break; + } + } + } + } + + return ASN1_SUCCESS; +} diff --git a/grub-core/lib/libtasn1/lib/parser_aux.h b/grub-core/lib/libtasn1/lib/parser_aux.h new file mode 100644 index 0000000000..598e684b35 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/parser_aux.h @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2000-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _PARSER_AUX_H +#define _PARSER_AUX_H + +/***********************************************/ +/* Type: list_type */ +/* Description: type used in the list during */ +/* the structure creation. */ +/***********************************************/ +typedef struct list_struct +{ + asn1_node node; + struct list_struct *next; +} list_type; + +/***************************************/ +/* Functions used by ASN.1 parser */ +/***************************************/ +asn1_node _asn1_add_static_node (list_type **e_list, unsigned int type); + +void _asn1_delete_list (list_type *e_list); + +void _asn1_delete_list_and_nodes (list_type *e_list); + +void _asn1_delete_node_from_list (list_type *list, asn1_node node); + +asn1_node +_asn1_set_value (asn1_node node, const void *value, unsigned int len); + +asn1_node _asn1_set_value_m (asn1_node node, void *value, unsigned int len); + +asn1_node +_asn1_set_value_lv (asn1_node node, const void *value, unsigned int len); + +asn1_node +_asn1_append_value (asn1_node node, const void *value, unsigned int len); + +asn1_node _asn1_set_name (asn1_node node, const char *name); + +asn1_node _asn1_cpy_name (asn1_node dst, asn1_node_const src); + +asn1_node _asn1_set_right (asn1_node node, asn1_node right); + +asn1_node _asn1_get_last_right (asn1_node_const node); + +void _asn1_remove_node (asn1_node node, unsigned int flags); + +/* Max 64-bit integer length is 20 chars + 1 for sign + 1 for null termination */ +#define LTOSTR_MAX_SIZE 22 +char *_asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE]); + +asn1_node _asn1_find_up (asn1_node_const node); + +int _asn1_change_integer_value (asn1_node node); + +#define EXPAND_OBJECT_ID_MAX_RECURSION 16 +int _asn1_expand_object_id (list_type **list, asn1_node node); + +int _asn1_type_set_config (asn1_node node); + +int _asn1_check_identifier (asn1_node_const node); + +int _asn1_set_default_tag (asn1_node node); + +/******************************************************************/ +/* Function : _asn1_get_right */ +/* Description: returns the element pointed by the RIGHT field of */ +/* a NODE_ASN element. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: field RIGHT of NODE. */ +/******************************************************************/ +inline static asn1_node +_asn1_get_right (asn1_node_const node) +{ + if (node == NULL) + return NULL; + return node->right; +} + +/******************************************************************/ +/* Function : _asn1_set_down */ +/* Description: sets the field DOWN in a NODE_ASN element. */ +/* Parameters: */ +/* node: element pointer. */ +/* down: pointer to a NODE_ASN element that you want be pointed */ +/* by NODE. */ +/* Return: pointer to *NODE. */ +/******************************************************************/ +inline static asn1_node +_asn1_set_down (asn1_node node, asn1_node down) +{ + if (node == NULL) + return node; + node->down = down; + if (down) + down->left = node; + return node; +} + +/******************************************************************/ +/* Function : _asn1_get_down */ +/* Description: returns the element pointed by the DOWN field of */ +/* a NODE_ASN element. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: field DOWN of NODE. */ +/******************************************************************/ +inline static asn1_node +_asn1_get_down (asn1_node_const node) +{ + if (node == NULL) + return NULL; + return node->down; +} + +/******************************************************************/ +/* Function : _asn1_get_name */ +/* Description: returns the name of a NODE_ASN element. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: a null terminated string. */ +/******************************************************************/ +inline static char * +_asn1_get_name (asn1_node_const node) +{ + if (node == NULL) + return NULL; + return (char *) node->name; +} + +/******************************************************************/ +/* Function : _asn1_mod_type */ +/* Description: change the field TYPE of an NODE_ASN element. */ +/* The new value is the old one | (bitwise or) the */ +/* paramener VALUE. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* value: the integer value that must be or-ed with the current */ +/* value of field TYPE. */ +/* Return: NODE pointer. */ +/******************************************************************/ +inline static asn1_node +_asn1_mod_type (asn1_node node, unsigned int value) +{ + if (node == NULL) + return node; + node->type |= value; + return node; +} + +#endif diff --git a/grub-core/lib/libtasn1/lib/structure.c b/grub-core/lib/libtasn1/lib/structure.c new file mode 100644 index 0000000000..8189c56a4c --- /dev/null +++ b/grub-core/lib/libtasn1/lib/structure.c @@ -0,0 +1,1220 @@ +/* + * Copyright (C) 2002-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/*****************************************************/ +/* File: structure.c */ +/* Description: Functions to create and delete an */ +/* ASN1 tree. */ +/*****************************************************/ + + +#include +#include +#include "parser_aux.h" +#include + + +extern char _asn1_identifierMissing[]; + + +/******************************************************/ +/* Function : _asn1_add_single_node */ +/* Description: creates a new NODE_ASN element. */ +/* Parameters: */ +/* type: type of the new element (see ASN1_ETYPE_ */ +/* and CONST_ constants). */ +/* Return: pointer to the new element. */ +/******************************************************/ +asn1_node +_asn1_add_single_node (unsigned int type) +{ + asn1_node punt; + + punt = calloc (1, sizeof (struct asn1_node_st)); + if (punt == NULL) + return NULL; + + punt->type = type; + + return punt; +} + + +/******************************************************************/ +/* Function : _asn1_find_left */ +/* Description: returns the NODE_ASN element with RIGHT field that*/ +/* points the element NODE. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: NULL if not found. */ +/******************************************************************/ +asn1_node +_asn1_find_left (asn1_node_const node) +{ + if ((node == NULL) || (node->left == NULL) || (node->left->down == node)) + return NULL; + + return node->left; +} + + +int +_asn1_create_static_structure (asn1_node_const pointer, char *output_file_name, + char *vector_name) +{ + FILE *file; + asn1_node_const p; + unsigned long t; + + file = fopen (output_file_name, "w"); + + if (file == NULL) + return ASN1_FILE_NOT_FOUND; + + fprintf (file, "#if HAVE_CONFIG_H\n"); + fprintf (file, "# include \"config.h\"\n"); + fprintf (file, "#endif\n\n"); + + fprintf (file, "#include \n\n"); + + fprintf (file, "const asn1_static_node %s[] = {\n", vector_name); + + p = pointer; + + while (p) + { + fprintf (file, " { "); + + if (p->name[0] != 0) + fprintf (file, "\"%s\", ", p->name); + else + fprintf (file, "NULL, "); + + t = p->type; + if (p->down) + t |= CONST_DOWN; + if (p->right) + t |= CONST_RIGHT; + + fprintf (file, "%lu, ", t); + + if (p->value) + fprintf (file, "\"%s\"},\n", p->value); + else + fprintf (file, "NULL },\n"); + + if (p->down) + { + p = p->down; + } + else if (p->right) + { + p = p->right; + } + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == pointer) + { + p = NULL; + break; + } + if (p->right) + { + p = p->right; + break; + } + } + } + } + + fprintf (file, " { NULL, 0, NULL }\n};\n"); + + fclose (file); + + return ASN1_SUCCESS; +} + + +/** + * asn1_array2tree: + * @array: specify the array that contains ASN.1 declarations + * @definitions: return the pointer to the structure created by + * *ARRAY ASN.1 declarations + * @errorDescription: return the error description. + * + * Creates the structures needed to manage the ASN.1 definitions. + * @array is a vector created by asn1_parser2array(). + * + * Returns: %ASN1_SUCCESS if structure was created correctly, + * %ASN1_ELEMENT_NOT_EMPTY if *@definitions not NULL, + * %ASN1_IDENTIFIER_NOT_FOUND if in the file there is an identifier + * that is not defined (see @errorDescription for more information), + * %ASN1_ARRAY_ERROR if the array pointed by @array is wrong. + **/ +int +asn1_array2tree (const asn1_static_node * array, asn1_node * definitions, + char *errorDescription) +{ + asn1_node p, p_last = NULL; + unsigned long k; + int move; + int result; + unsigned int type; + list_type *e_list = NULL; + + if (errorDescription) + errorDescription[0] = 0; + + if (*definitions != NULL) + return ASN1_ELEMENT_NOT_EMPTY; + + move = UP; + + for (k = 0; array[k].value || array[k].type || array[k].name; k++) + { + type = convert_old_type (array[k].type); + + p = _asn1_add_static_node (&e_list, type & (~CONST_DOWN)); + if (array[k].name) + _asn1_set_name (p, array[k].name); + if (array[k].value) + _asn1_set_value (p, array[k].value, strlen (array[k].value) + 1); + + if (*definitions == NULL) + *definitions = p; + + if (move == DOWN) + { + if (p_last && p_last->down) + _asn1_delete_structure (e_list, &p_last->down, 0); + _asn1_set_down (p_last, p); + } + else if (move == RIGHT) + { + if (p_last && p_last->right) + _asn1_delete_structure (e_list, &p_last->right, 0); + _asn1_set_right (p_last, p); + } + + p_last = p; + + if (type & CONST_DOWN) + move = DOWN; + else if (type & CONST_RIGHT) + move = RIGHT; + else + { + while (p_last != *definitions) + { + p_last = _asn1_find_up (p_last); + + if (p_last == NULL) + break; + + if (p_last->type & CONST_RIGHT) + { + p_last->type &= ~CONST_RIGHT; + move = RIGHT; + break; + } + } /* while */ + } + } /* while */ + + if (p_last == *definitions) + { + result = _asn1_check_identifier (*definitions); + if (result == ASN1_SUCCESS) + { + _asn1_change_integer_value (*definitions); + result = _asn1_expand_object_id (&e_list, *definitions); + } + } + else + { + result = ASN1_ARRAY_ERROR; + } + + if (errorDescription != NULL) + { + if (result == ASN1_IDENTIFIER_NOT_FOUND) + { + Estrcpy (errorDescription, ":: identifier '"); + Estrcat (errorDescription, _asn1_identifierMissing); + Estrcat (errorDescription, "' not found"); + } + else + errorDescription[0] = 0; + } + + if (result != ASN1_SUCCESS) + { + _asn1_delete_list_and_nodes (e_list); + *definitions = NULL; + } + else + _asn1_delete_list (e_list); + + return result; +} + +/** + * asn1_delete_structure: + * @structure: pointer to the structure that you want to delete. + * + * Deletes the structure *@structure. At the end, *@structure is set + * to NULL. + * + * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if + * *@structure was NULL. + **/ +int +asn1_delete_structure (asn1_node * structure) +{ + return _asn1_delete_structure (NULL, structure, 0); +} + +/** + * asn1_delete_structure2: + * @structure: pointer to the structure that you want to delete. + * @flags: additional flags (see %ASN1_DELETE_FLAG) + * + * Deletes the structure *@structure. At the end, *@structure is set + * to NULL. + * + * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if + * *@structure was NULL. + **/ +int +asn1_delete_structure2 (asn1_node * structure, unsigned int flags) +{ + return _asn1_delete_structure (NULL, structure, flags); +} + +int +_asn1_delete_structure (list_type *e_list, asn1_node * structure, unsigned int flags) +{ + asn1_node p, p2, p3; + + if (*structure == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = *structure; + while (p) + { + if (p->down) + { + p = p->down; + } + else + { /* no down */ + p2 = p->right; + if (p != *structure) + { + p3 = _asn1_find_up (p); + _asn1_set_down (p3, p2); + if (e_list) + _asn1_delete_node_from_list (e_list, p); + _asn1_remove_node (p, flags); + p = p3; + } + else + { /* p==root */ + p3 = _asn1_find_left (p); + if (!p3) + { + p3 = _asn1_find_up (p); + if (p3) + _asn1_set_down (p3, p2); + else + { + if (p->right) + p->right->left = NULL; + } + } + else + _asn1_set_right (p3, p2); + if (e_list) + _asn1_delete_node_from_list (e_list, p); + _asn1_remove_node (p, flags); + p = NULL; + } + } + } + + *structure = NULL; + return ASN1_SUCCESS; +} + + +/** + * asn1_delete_element: + * @structure: pointer to the structure that contains the element you + * want to delete. + * @element_name: element's name you want to delete. + * + * Deletes the element named *@element_name inside *@structure. + * + * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if + * the @element_name was not found. + **/ +int +asn1_delete_element (asn1_node structure, const char *element_name) +{ + asn1_node p2, p3, source_node; + + source_node = asn1_find_node (structure, element_name); + + if (source_node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p2 = source_node->right; + p3 = _asn1_find_left (source_node); + if (!p3) + { + p3 = _asn1_find_up (source_node); + if (p3) + _asn1_set_down (p3, p2); + else if (source_node->right) + source_node->right->left = NULL; + } + else + _asn1_set_right (p3, p2); + + return asn1_delete_structure (&source_node); +} + +#ifndef __clang_analyzer__ +asn1_node +_asn1_copy_structure3 (asn1_node_const source_node) +{ + asn1_node_const p_s; + asn1_node dest_node, p_d, p_d_prev; + int move; + + if (source_node == NULL) + return NULL; + + dest_node = _asn1_add_single_node (source_node->type); + + p_s = source_node; + p_d = dest_node; + + move = DOWN; + + do + { + if (move != UP) + { + if (p_s->name[0] != 0) + _asn1_cpy_name (p_d, p_s); + if (p_s->value) + _asn1_set_value (p_d, p_s->value, p_s->value_len); + if (p_s->down) + { + p_s = p_s->down; + p_d_prev = p_d; + p_d = _asn1_add_single_node (p_s->type); + _asn1_set_down (p_d_prev, p_d); + continue; + } + p_d->start = p_s->start; + p_d->end = p_s->end; + } + + if (p_s == source_node) + break; + + if (p_s->right) + { + move = RIGHT; + p_s = p_s->right; + p_d_prev = p_d; + p_d = _asn1_add_single_node (p_s->type); + _asn1_set_right (p_d_prev, p_d); + } + else + { + move = UP; + p_s = _asn1_find_up (p_s); + p_d = _asn1_find_up (p_d); + } + } + while (p_s != source_node); + return dest_node; +} +#else + +/* Non-production code */ +asn1_node +_asn1_copy_structure3 (asn1_node_const source_node) +{ + return NULL; +} +#endif /* __clang_analyzer__ */ + + +static asn1_node +_asn1_copy_structure2 (asn1_node_const root, const char *source_name) +{ + asn1_node source_node; + + source_node = asn1_find_node (root, source_name); + + return _asn1_copy_structure3 (source_node); + +} + + +static int +_asn1_type_choice_config (asn1_node node) +{ + asn1_node p, p2, p3, p4; + int move, tlen; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + move = DOWN; + + while (!((p == node) && (move == UP))) + { + if (move != UP) + { + if ((type_field (p->type) == ASN1_ETYPE_CHOICE) + && (p->type & CONST_TAG)) + { + p2 = p->down; + while (p2) + { + if (type_field (p2->type) != ASN1_ETYPE_TAG) + { + p2->type |= CONST_TAG; + p3 = _asn1_find_left (p2); + while (p3) + { + if (type_field (p3->type) == ASN1_ETYPE_TAG) + { + p4 = _asn1_add_single_node (p3->type); + tlen = _asn1_strlen (p3->value); + if (tlen > 0) + _asn1_set_value (p4, p3->value, tlen + 1); + _asn1_set_right (p4, p2->down); + _asn1_set_down (p2, p4); + } + p3 = _asn1_find_left (p3); + } + } + p2 = p2->right; + } + p->type &= ~(CONST_TAG); + p2 = p->down; + while (p2) + { + p3 = p2->right; + if (type_field (p2->type) == ASN1_ETYPE_TAG) + asn1_delete_structure (&p2); + p2 = p3; + } + } + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + return ASN1_SUCCESS; +} + + +static int +_asn1_expand_identifier (asn1_node * node, asn1_node_const root) +{ + asn1_node p, p2, p3; + char name2[ASN1_MAX_NAME_SIZE + 2]; + int move; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = *node; + move = DOWN; + + while (!((p == *node) && (move == UP))) + { + if (move != UP) + { + if (type_field (p->type) == ASN1_ETYPE_IDENTIFIER) + { + snprintf (name2, sizeof (name2), "%s.%s", root->name, p->value); + p2 = _asn1_copy_structure2 (root, name2); + if (p2 == NULL) + { + return ASN1_IDENTIFIER_NOT_FOUND; + } + _asn1_cpy_name (p2, p); + p2->right = p->right; + p2->left = p->left; + if (p->right) + p->right->left = p2; + p3 = p->down; + if (p3) + { + while (p3->right) + p3 = p3->right; + _asn1_set_right (p3, p2->down); + _asn1_set_down (p2, p->down); + } + + p3 = _asn1_find_left (p); + if (p3) + _asn1_set_right (p3, p2); + else + { + p3 = _asn1_find_up (p); + if (p3) + _asn1_set_down (p3, p2); + else + { + p2->left = NULL; + } + } + + if (p->type & CONST_SIZE) + p2->type |= CONST_SIZE; + if (p->type & CONST_TAG) + p2->type |= CONST_TAG; + if (p->type & CONST_OPTION) + p2->type |= CONST_OPTION; + if (p->type & CONST_DEFAULT) + p2->type |= CONST_DEFAULT; + if (p->type & CONST_SET) + p2->type |= CONST_SET; + if (p->type & CONST_NOT_USED) + p2->type |= CONST_NOT_USED; + + if (p == *node) + *node = p2; + _asn1_remove_node (p, 0); + p = p2; + move = DOWN; + continue; + } + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == *node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + return ASN1_SUCCESS; +} + + +/** + * asn1_create_element: + * @definitions: pointer to the structure returned by "parser_asn1" function + * @source_name: the name of the type of the new structure (must be + * inside p_structure). + * @element: pointer to the structure created. + * + * Creates a structure of type @source_name. Example using + * "pkix.asn": + * + * rc = asn1_create_element(cert_def, "PKIX1.Certificate", certptr); + * + * Returns: %ASN1_SUCCESS if creation OK, %ASN1_ELEMENT_NOT_FOUND if + * @source_name is not known. + **/ +int +asn1_create_element (asn1_node_const definitions, const char *source_name, + asn1_node * element) +{ + asn1_node dest_node; + int res; + + dest_node = _asn1_copy_structure2 (definitions, source_name); + + if (dest_node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + _asn1_set_name (dest_node, ""); + + res = _asn1_expand_identifier (&dest_node, definitions); + _asn1_type_choice_config (dest_node); + + *element = dest_node; + + return res; +} + + +/** + * asn1_print_structure: + * @out: pointer to the output file (e.g. stdout). + * @structure: pointer to the structure that you want to visit. + * @name: an element of the structure + * @mode: specify how much of the structure to print, can be + * %ASN1_PRINT_NAME, %ASN1_PRINT_NAME_TYPE, + * %ASN1_PRINT_NAME_TYPE_VALUE, or %ASN1_PRINT_ALL. + * + * Prints on the @out file descriptor the structure's tree starting + * from the @name element inside the structure @structure. + **/ +void +asn1_print_structure (FILE * out, asn1_node_const structure, const char *name, + int mode) +{ + asn1_node_const p, root; + int k, indent = 0, len, len2, len3; + + if (out == NULL) + return; + + root = asn1_find_node (structure, name); + + if (root == NULL) + return; + + p = root; + while (p) + { + if (mode == ASN1_PRINT_ALL) + { + for (k = 0; k < indent; k++) + fprintf (out, " "); + fprintf (out, "name:"); + if (p->name[0] != 0) + fprintf (out, "%s ", p->name); + else + fprintf (out, "NULL "); + } + else + { + switch (type_field (p->type)) + { + case ASN1_ETYPE_CONSTANT: + case ASN1_ETYPE_TAG: + case ASN1_ETYPE_SIZE: + break; + default: + for (k = 0; k < indent; k++) + fprintf (out, " "); + fprintf (out, "name:"); + if (p->name[0] != 0) + fprintf (out, "%s ", p->name); + else + fprintf (out, "NULL "); + } + } + + if (mode != ASN1_PRINT_NAME) + { + unsigned type = type_field (p->type); + switch (type) + { + case ASN1_ETYPE_CONSTANT: + if (mode == ASN1_PRINT_ALL) + fprintf (out, "type:CONST"); + break; + case ASN1_ETYPE_TAG: + if (mode == ASN1_PRINT_ALL) + fprintf (out, "type:TAG"); + break; + case ASN1_ETYPE_SIZE: + if (mode == ASN1_PRINT_ALL) + fprintf (out, "type:SIZE"); + break; + case ASN1_ETYPE_DEFAULT: + fprintf (out, "type:DEFAULT"); + break; + case ASN1_ETYPE_IDENTIFIER: + fprintf (out, "type:IDENTIFIER"); + break; + case ASN1_ETYPE_ANY: + fprintf (out, "type:ANY"); + break; + case ASN1_ETYPE_CHOICE: + fprintf (out, "type:CHOICE"); + break; + case ASN1_ETYPE_DEFINITIONS: + fprintf (out, "type:DEFINITIONS"); + break; + CASE_HANDLED_ETYPES: + fprintf (out, "%s", _asn1_tags[type].desc); + break; + default: + break; + } + } + + if ((mode == ASN1_PRINT_NAME_TYPE_VALUE) || (mode == ASN1_PRINT_ALL)) + { + switch (type_field (p->type)) + { + case ASN1_ETYPE_CONSTANT: + if (mode == ASN1_PRINT_ALL) + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case ASN1_ETYPE_TAG: + if (mode == ASN1_PRINT_ALL) + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case ASN1_ETYPE_SIZE: + if (mode == ASN1_PRINT_ALL) + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case ASN1_ETYPE_DEFAULT: + if (p->value) + fprintf (out, " value:%s", p->value); + else if (p->type & CONST_TRUE) + fprintf (out, " value:TRUE"); + else if (p->type & CONST_FALSE) + fprintf (out, " value:FALSE"); + break; + case ASN1_ETYPE_IDENTIFIER: + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case ASN1_ETYPE_INTEGER: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + fprintf (out, " value:0x"); + if (len > 0) + for (k = 0; k < len; k++) + fprintf (out, "%02x", (unsigned) (p->value)[k + len2]); + } + break; + case ASN1_ETYPE_ENUMERATED: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + fprintf (out, " value:0x"); + if (len > 0) + for (k = 0; k < len; k++) + fprintf (out, "%02x", (unsigned) (p->value)[k + len2]); + } + break; + case ASN1_ETYPE_BOOLEAN: + if (p->value) + { + if (p->value[0] == 'T') + fprintf (out, " value:TRUE"); + else if (p->value[0] == 'F') + fprintf (out, " value:FALSE"); + } + break; + case ASN1_ETYPE_BIT_STRING: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + if (len > 0) + { + fprintf (out, " value(%i):", + (len - 1) * 8 - (p->value[len2])); + for (k = 1; k < len; k++) + fprintf (out, "%02x", (unsigned) (p->value)[k + len2]); + } + } + break; + case ASN1_ETYPE_GENERALIZED_TIME: + case ASN1_ETYPE_UTC_TIME: + if (p->value) + { + fprintf (out, " value:"); + for (k = 0; k < p->value_len; k++) + fprintf (out, "%c", (p->value)[k]); + } + break; + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + fprintf (out, " value:"); + if (len > 0) + for (k = 0; k < len; k++) + fprintf (out, "%c", (p->value)[k + len2]); + } + break; + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_OCTET_STRING: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + fprintf (out, " value:"); + if (len > 0) + for (k = 0; k < len; k++) + fprintf (out, "%02x", (unsigned) (p->value)[k + len2]); + } + break; + case ASN1_ETYPE_OBJECT_ID: + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case ASN1_ETYPE_ANY: + if (p->value) + { + len3 = -1; + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + fprintf (out, " value:"); + if (len2 > 0) + for (k = 0; k < len2; k++) + fprintf (out, "%02x", (unsigned) (p->value)[k + len3]); + } + break; + case ASN1_ETYPE_SET: + case ASN1_ETYPE_SET_OF: + case ASN1_ETYPE_CHOICE: + case ASN1_ETYPE_DEFINITIONS: + case ASN1_ETYPE_SEQUENCE_OF: + case ASN1_ETYPE_SEQUENCE: + case ASN1_ETYPE_NULL: + break; + default: + break; + } + } + + if (mode == ASN1_PRINT_ALL) + { + if (p->type & 0x1FFFFF00) + { + fprintf (out, " attr:"); + if (p->type & CONST_UNIVERSAL) + fprintf (out, "UNIVERSAL,"); + if (p->type & CONST_PRIVATE) + fprintf (out, "PRIVATE,"); + if (p->type & CONST_APPLICATION) + fprintf (out, "APPLICATION,"); + if (p->type & CONST_EXPLICIT) + fprintf (out, "EXPLICIT,"); + if (p->type & CONST_IMPLICIT) + fprintf (out, "IMPLICIT,"); + if (p->type & CONST_TAG) + fprintf (out, "TAG,"); + if (p->type & CONST_DEFAULT) + fprintf (out, "DEFAULT,"); + if (p->type & CONST_TRUE) + fprintf (out, "TRUE,"); + if (p->type & CONST_FALSE) + fprintf (out, "FALSE,"); + if (p->type & CONST_LIST) + fprintf (out, "LIST,"); + if (p->type & CONST_MIN_MAX) + fprintf (out, "MIN_MAX,"); + if (p->type & CONST_OPTION) + fprintf (out, "OPTION,"); + if (p->type & CONST_1_PARAM) + fprintf (out, "1_PARAM,"); + if (p->type & CONST_SIZE) + fprintf (out, "SIZE,"); + if (p->type & CONST_DEFINED_BY) + fprintf (out, "DEF_BY,"); + if (p->type & CONST_GENERALIZED) + fprintf (out, "GENERALIZED,"); + if (p->type & CONST_UTC) + fprintf (out, "UTC,"); + if (p->type & CONST_SET) + fprintf (out, "SET,"); + if (p->type & CONST_NOT_USED) + fprintf (out, "NOT_USED,"); + if (p->type & CONST_ASSIGN) + fprintf (out, "ASSIGNMENT,"); + } + } + + if (mode == ASN1_PRINT_ALL) + { + fprintf (out, "\n"); + } + else + { + switch (type_field (p->type)) + { + case ASN1_ETYPE_CONSTANT: + case ASN1_ETYPE_TAG: + case ASN1_ETYPE_SIZE: + break; + default: + fprintf (out, "\n"); + } + } + + if (p->down) + { + p = p->down; + indent += 2; + } + else if (p == root) + { + p = NULL; + break; + } + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == root) + { + p = NULL; + break; + } + indent -= 2; + if (p->right) + { + p = p->right; + break; + } + } + } + } +} + + + +/** + * asn1_number_of_elements: + * @element: pointer to the root of an ASN1 structure. + * @name: the name of a sub-structure of ROOT. + * @num: pointer to an integer where the result will be stored + * + * Counts the number of elements of a sub-structure called NAME with + * names equal to "?1","?2", ... + * + * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if + * @name is not known, %ASN1_GENERIC_ERROR if pointer @num is %NULL. + **/ +int +asn1_number_of_elements (asn1_node_const element, const char *name, int *num) +{ + asn1_node_const node, p; + + if (num == NULL) + return ASN1_GENERIC_ERROR; + + *num = 0; + + node = asn1_find_node (element, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node->down; + + while (p) + { + if (p->name[0] == '?') + (*num)++; + p = p->right; + } + + return ASN1_SUCCESS; +} + + +/** + * asn1_find_structure_from_oid: + * @definitions: ASN1 definitions + * @oidValue: value of the OID to search (e.g. "1.2.3.4"). + * + * Search the structure that is defined just after an OID definition. + * + * Returns: %NULL when @oidValue not found, otherwise the pointer to a + * constant string that contains the element name defined just after + * the OID. + **/ +const char * +asn1_find_structure_from_oid (asn1_node_const definitions, const char *oidValue) +{ + char name[2 * ASN1_MAX_NAME_SIZE + 2]; + char value[ASN1_MAX_NAME_SIZE]; + asn1_node p; + int len; + int result; + const char *definitionsName; + + if ((definitions == NULL) || (oidValue == NULL)) + return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ + + definitionsName = definitions->name; + + /* search the OBJECT_ID into definitions */ + p = definitions->down; + while (p) + { + if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) && + (p->type & CONST_ASSIGN)) + { + snprintf(name, sizeof(name), "%s.%s", definitionsName, p->name); + + len = ASN1_MAX_NAME_SIZE; + result = asn1_read_value (definitions, name, value, &len); + + if ((result == ASN1_SUCCESS) && (!strcmp (oidValue, value))) + { + p = p->right; + if (p == NULL) /* reach the end of ASN1 definitions */ + return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ + + return p->name; + } + } + p = p->right; + } + + return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ +} + +/** + * asn1_copy_node: + * @dst: Destination asn1 node. + * @dst_name: Field name in destination node. + * @src: Source asn1 node. + * @src_name: Field name in source node. + * + * Create a deep copy of a asn1_node variable. That + * function requires @dst to be expanded using asn1_create_element(). + * + * Returns: Return %ASN1_SUCCESS on success. + **/ +int +asn1_copy_node (asn1_node dst, const char *dst_name, + asn1_node_const src, const char *src_name) +{ + int result; + asn1_node dst_node; + void *data = NULL; + int size = 0; + + result = asn1_der_coding (src, src_name, NULL, &size, NULL); + if (result != ASN1_MEM_ERROR) + return result; + + data = malloc (size); + if (data == NULL) + return ASN1_MEM_ERROR; + + result = asn1_der_coding (src, src_name, data, &size, NULL); + if (result != ASN1_SUCCESS) + { + free (data); + return result; + } + + dst_node = asn1_find_node (dst, dst_name); + if (dst_node == NULL) + { + free (data); + return ASN1_ELEMENT_NOT_FOUND; + } + + result = asn1_der_decoding (&dst_node, data, size, NULL); + + free (data); + + return result; +} + +/** + * asn1_dup_node: + * @src: Source asn1 node. + * @src_name: Field name in source node. + * + * Create a deep copy of a asn1_node variable. This function + * will return an exact copy of the provided structure. + * + * Returns: Return %NULL on failure. + **/ +asn1_node +asn1_dup_node (asn1_node_const src, const char *src_name) +{ + return _asn1_copy_structure2(src, src_name); +} diff --git a/grub-core/lib/libtasn1/lib/structure.h b/grub-core/lib/libtasn1/lib/structure.h new file mode 100644 index 0000000000..99e685da07 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/structure.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2002-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/*************************************************/ +/* File: structure.h */ +/* Description: list of exported object by */ +/* "structure.c" */ +/*************************************************/ + +#ifndef _STRUCTURE_H +#define _STRUCTURE_H + +#include "parser_aux.h" // list_type + +int _asn1_create_static_structure (asn1_node_const pointer, + char *output_file_name, char *vector_name); + +asn1_node _asn1_copy_structure3 (asn1_node_const source_node); + +asn1_node _asn1_add_single_node (unsigned int type); + +asn1_node _asn1_find_left (asn1_node_const node); + +int +_asn1_delete_structure (list_type *e_list, asn1_node *structure, unsigned int flags); + +#endif diff --git a/include/grub/libtasn1.h b/include/grub/libtasn1.h new file mode 100644 index 0000000000..6fd7a30dc3 --- /dev/null +++ b/include/grub/libtasn1.h @@ -0,0 +1,588 @@ +/* + * Copyright (C) 2002-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * LIBTASN1 is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * LIBTASN1 is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with LIBTASN1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +/** + * libtasn1:Short_Description: + * + * GNU ASN.1 library + */ +/** + * libtasn1:Long_Description: + * + * The Libtasn1 library provides Abstract Syntax Notation One (ASN.1, as + * specified by the X.680 ITU-T recommendation) parsing and structures + * management, and Distinguished Encoding Rules (DER, as per X.690) + * encoding and decoding functions. + */ + + +#ifndef LIBTASN1_H +#define LIBTASN1_H + +#ifndef ASN1_API +#if defined ASN1_BUILDING && defined HAVE_VISIBILITY && HAVE_VISIBILITY +#define ASN1_API __attribute__((__visibility__("default"))) +#elif defined ASN1_BUILDING && defined _MSC_VER && ! defined ASN1_STATIC +#define ASN1_API __declspec(dllexport) +#elif defined _MSC_VER && ! defined ASN1_STATIC +#define ASN1_API __declspec(dllimport) +#else +#define ASN1_API +#endif +#endif + +#ifdef __GNUC__ +# define __LIBTASN1_CONST__ __attribute__((const)) +# define __LIBTASN1_PURE__ __attribute__((pure)) +#else +# define __LIBTASN1_CONST__ +# define __LIBTASN1_PURE__ +#endif + +#include +#include +#include /* for FILE* */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * ASN1_VERSION: + * + * Version of the library as a string. + */ +#define ASN1_VERSION "4.16.0" + +/** + * ASN1_VERSION_MAJOR: + * + * Major version number of the library. + */ +#define ASN1_VERSION_MAJOR 4 + +/** + * ASN1_VERSION_MINOR: + * + * Minor version number of the library. + */ +#define ASN1_VERSION_MINOR 16 + +/** + * ASN1_VERSION_PATCH: + * + * Patch version number of the library. + */ +#define ASN1_VERSION_PATCH 0 + +/** + * ASN1_VERSION_NUMBER: + * + * Version number of the library as a number. + */ +#define ASN1_VERSION_NUMBER 0x041000 + + +#if defined __GNUC__ && !defined ASN1_INTERNAL_BUILD +# define _ASN1_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +# if _ASN1_GCC_VERSION >= 30100 +# define _ASN1_GCC_ATTR_DEPRECATED __attribute__ ((__deprecated__)) +# endif +#endif + +#ifndef _ASN1_GCC_ATTR_DEPRECATED +#define _ASN1_GCC_ATTR_DEPRECATED +#endif + +/*****************************************/ +/* Errors returned by libtasn1 functions */ +/*****************************************/ +#define ASN1_SUCCESS 0 +#define ASN1_FILE_NOT_FOUND 1 +#define ASN1_ELEMENT_NOT_FOUND 2 +#define ASN1_IDENTIFIER_NOT_FOUND 3 +#define ASN1_DER_ERROR 4 +#define ASN1_VALUE_NOT_FOUND 5 +#define ASN1_GENERIC_ERROR 6 +#define ASN1_VALUE_NOT_VALID 7 +#define ASN1_TAG_ERROR 8 +#define ASN1_TAG_IMPLICIT 9 +#define ASN1_ERROR_TYPE_ANY 10 +#define ASN1_SYNTAX_ERROR 11 +#define ASN1_MEM_ERROR 12 +#define ASN1_MEM_ALLOC_ERROR 13 +#define ASN1_DER_OVERFLOW 14 +#define ASN1_NAME_TOO_LONG 15 +#define ASN1_ARRAY_ERROR 16 +#define ASN1_ELEMENT_NOT_EMPTY 17 +#define ASN1_TIME_ENCODING_ERROR 18 +#define ASN1_RECURSION 19 + +/*************************************/ +/* Constants used in asn1_visit_tree */ +/*************************************/ +#define ASN1_PRINT_NAME 1 +#define ASN1_PRINT_NAME_TYPE 2 +#define ASN1_PRINT_NAME_TYPE_VALUE 3 +#define ASN1_PRINT_ALL 4 + +/*****************************************/ +/* Constants returned by asn1_read_tag */ +/*****************************************/ +#define ASN1_CLASS_UNIVERSAL 0x00 /* old: 1 */ +#define ASN1_CLASS_APPLICATION 0x40 /* old: 2 */ +#define ASN1_CLASS_CONTEXT_SPECIFIC 0x80 /* old: 3 */ +#define ASN1_CLASS_PRIVATE 0xC0 /* old: 4 */ +#define ASN1_CLASS_STRUCTURED 0x20 + +/*****************************************/ +/* Constants returned by asn1_read_tag */ +/*****************************************/ +#define ASN1_TAG_BOOLEAN 0x01 +#define ASN1_TAG_INTEGER 0x02 +#define ASN1_TAG_SEQUENCE 0x10 +#define ASN1_TAG_SET 0x11 +#define ASN1_TAG_OCTET_STRING 0x04 +#define ASN1_TAG_BIT_STRING 0x03 +#define ASN1_TAG_UTCTime 0x17 +#define ASN1_TAG_GENERALIZEDTime 0x18 +#define ASN1_TAG_OBJECT_ID 0x06 +#define ASN1_TAG_ENUMERATED 0x0A +#define ASN1_TAG_NULL 0x05 +#define ASN1_TAG_GENERALSTRING 0x1B +#define ASN1_TAG_NUMERIC_STRING 0x12 +#define ASN1_TAG_IA5_STRING 0x16 +#define ASN1_TAG_TELETEX_STRING 0x14 +#define ASN1_TAG_PRINTABLE_STRING 0x13 +#define ASN1_TAG_UNIVERSAL_STRING 0x1C +#define ASN1_TAG_BMP_STRING 0x1E +#define ASN1_TAG_UTF8_STRING 0x0C +#define ASN1_TAG_VISIBLE_STRING 0x1A + +/** + * asn1_node: + * + * Structure definition used for the node of the tree + * that represents an ASN.1 DEFINITION. + */ +typedef struct asn1_node_st asn1_node_st; + +typedef asn1_node_st *asn1_node; +typedef const asn1_node_st *asn1_node_const; + +/** + * ASN1_MAX_NAME_SIZE: + * + * Maximum number of characters of a name + * inside a file with ASN1 definitions. + */ +#define ASN1_MAX_NAME_SIZE 64 + + +/** + * asn1_static_node: + * @name: Node name + * @type: Node typ + * @value: Node value + * + * For the on-disk format of ASN.1 trees, created by asn1_parser2array(). + */ +struct asn1_static_node_st +{ + const char *name; /* Node name */ + unsigned int type; /* Node type */ + const void *value; /* Node value */ +}; +typedef struct asn1_static_node_st asn1_static_node; + +/* List of constants for field type of node_asn */ +#define ASN1_ETYPE_INVALID 0 +#define ASN1_ETYPE_CONSTANT 1 +#define ASN1_ETYPE_IDENTIFIER 2 +#define ASN1_ETYPE_INTEGER 3 +#define ASN1_ETYPE_BOOLEAN 4 +#define ASN1_ETYPE_SEQUENCE 5 +#define ASN1_ETYPE_BIT_STRING 6 +#define ASN1_ETYPE_OCTET_STRING 7 +#define ASN1_ETYPE_TAG 8 +#define ASN1_ETYPE_DEFAULT 9 +#define ASN1_ETYPE_SIZE 10 +#define ASN1_ETYPE_SEQUENCE_OF 11 +#define ASN1_ETYPE_OBJECT_ID 12 +#define ASN1_ETYPE_ANY 13 +#define ASN1_ETYPE_SET 14 +#define ASN1_ETYPE_SET_OF 15 +#define ASN1_ETYPE_DEFINITIONS 16 +#define ASN1_ETYPE_CHOICE 18 +#define ASN1_ETYPE_IMPORTS 19 +#define ASN1_ETYPE_NULL 20 +#define ASN1_ETYPE_ENUMERATED 21 +#define ASN1_ETYPE_GENERALSTRING 27 +#define ASN1_ETYPE_NUMERIC_STRING 28 +#define ASN1_ETYPE_IA5_STRING 29 +#define ASN1_ETYPE_TELETEX_STRING 30 +#define ASN1_ETYPE_PRINTABLE_STRING 31 +#define ASN1_ETYPE_UNIVERSAL_STRING 32 +#define ASN1_ETYPE_BMP_STRING 33 +#define ASN1_ETYPE_UTF8_STRING 34 +#define ASN1_ETYPE_VISIBLE_STRING 35 +#define ASN1_ETYPE_UTC_TIME 36 +#define ASN1_ETYPE_GENERALIZED_TIME 37 + +/** + * ASN1_DELETE_FLAG_ZEROIZE: + * + * Used by: asn1_delete_structure2() + * + * Zeroize values prior to deinitialization. + */ +#define ASN1_DELETE_FLAG_ZEROIZE 1 + +/** + * ASN1_DECODE_FLAG_ALLOW_PADDING: + * + * Used by: asn1_der_decoding2() + * + * This flag would allow arbitrary data past the DER data. + */ +#define ASN1_DECODE_FLAG_ALLOW_PADDING 1 +/** + * ASN1_DECODE_FLAG_STRICT_DER: + * + * Used by: asn1_der_decoding2() + * + * This flag would ensure that no BER decoding takes place. + */ +#define ASN1_DECODE_FLAG_STRICT_DER (1<<1) +/** + * ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME: + * + * Used by: asn1_der_decoding2() + * + * This flag will tolerate Time encoding errors when in strict DER. + */ +#define ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME (1<<2) + + +/** + * asn1_data_node_st: + * @name: Node name + * @value: Node value + * @value_len: Node value size + * @type: Node value type (ASN1_ETYPE_*) + * + * Data node inside a #asn1_node structure. + */ +struct asn1_data_node_st +{ + const char *name; /* Node name */ + const void *value; /* Node value */ + unsigned int value_len; /* Node value size */ + unsigned int type; /* Node value type (ASN1_ETYPE_*) */ +}; +typedef struct asn1_data_node_st asn1_data_node_st; + +/***********************************/ +/* Fixed constants */ +/***********************************/ + +/** + * ASN1_MAX_ERROR_DESCRIPTION_SIZE: + * + * Maximum number of characters + * of a description message + * (null character included). + */ +#define ASN1_MAX_ERROR_DESCRIPTION_SIZE 128 + +/***********************************/ +/* Functions definitions */ +/***********************************/ + +extern ASN1_API int + asn1_parser2tree (const char *file, + asn1_node * definitions, char *error_desc); + +extern ASN1_API int + asn1_parser2array (const char *inputFileName, + const char *outputFileName, + const char *vectorName, char *error_desc); + +extern ASN1_API int + asn1_array2tree (const asn1_static_node * array, + asn1_node * definitions, char *errorDescription); + +extern ASN1_API void + asn1_print_structure (FILE * out, asn1_node_const structure, + const char *name, int mode); + +extern ASN1_API int + asn1_create_element (asn1_node_const definitions, + const char *source_name, asn1_node * element); + +extern ASN1_API int asn1_delete_structure (asn1_node * structure); + +extern ASN1_API int asn1_delete_structure2 (asn1_node * structure, unsigned int flags); + +extern ASN1_API int + asn1_delete_element (asn1_node structure, const char *element_name); + +extern ASN1_API int + asn1_write_value (asn1_node node_root, const char *name, + const void *ivalue, int len); + +extern ASN1_API int + asn1_read_value (asn1_node_const root, const char *name, + void *ivalue, int *len); + +extern ASN1_API int + asn1_read_value_type (asn1_node_const root, const char *name, + void *ivalue, int *len, unsigned int *etype); + +extern ASN1_API int + asn1_read_node_value (asn1_node_const node, asn1_data_node_st * data); + +extern ASN1_API int + asn1_number_of_elements (asn1_node_const element, const char *name, int *num); + +extern ASN1_API int + asn1_der_coding (asn1_node_const element, const char *name, + void *ider, int *len, char *ErrorDescription); + +extern ASN1_API int + asn1_der_decoding2 (asn1_node *element, const void *ider, + int *max_ider_len, unsigned int flags, + char *errorDescription); + +extern ASN1_API int + asn1_der_decoding (asn1_node * element, const void *ider, + int ider_len, char *errorDescription); + +/* Do not use. Use asn1_der_decoding() instead. */ +extern ASN1_API int + asn1_der_decoding_element (asn1_node * structure, + const char *elementName, + const void *ider, int len, + char *errorDescription) _ASN1_GCC_ATTR_DEPRECATED; + +extern ASN1_API int + asn1_der_decoding_startEnd (asn1_node element, + const void *ider, int ider_len, + const char *name_element, + int *start, int *end); + +extern ASN1_API int + asn1_expand_any_defined_by (asn1_node_const definitions, asn1_node * element); + +extern ASN1_API int + asn1_expand_octet_string (asn1_node_const definitions, + asn1_node * element, + const char *octetName, const char *objectName); + +extern ASN1_API int + asn1_read_tag (asn1_node_const root, const char *name, + int *tagValue, int *classValue); + +extern ASN1_API const char *asn1_find_structure_from_oid (asn1_node_const + definitions, + const char + *oidValue); + +__LIBTASN1_PURE__ +extern ASN1_API const char *asn1_check_version (const char *req_version); + +__LIBTASN1_PURE__ +extern ASN1_API const char *asn1_strerror (int error); + +extern ASN1_API void asn1_perror (int error); + +#define ASN1_MAX_TAG_SIZE 4 +#define ASN1_MAX_LENGTH_SIZE 9 +#define ASN1_MAX_TL_SIZE (ASN1_MAX_TAG_SIZE+ASN1_MAX_LENGTH_SIZE) +extern ASN1_API long + asn1_get_length_der (const unsigned char *der, int der_len, int *len); + +extern ASN1_API long + asn1_get_length_ber (const unsigned char *ber, int ber_len, int *len); + +extern ASN1_API void + asn1_length_der (unsigned long int len, unsigned char *der, int *der_len); + +/* Other utility functions. */ + +extern ASN1_API + int asn1_decode_simple_der (unsigned int etype, const unsigned char *der, + unsigned int _der_len, + const unsigned char **str, + unsigned int *str_len); + +extern ASN1_API + int asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, + unsigned int _der_len, + unsigned char **str, + unsigned int *str_len, + unsigned int *ber_len); + +extern ASN1_API int + asn1_encode_simple_der (unsigned int etype, const unsigned char *str, + unsigned int str_len, unsigned char *tl, + unsigned int *tl_len); + +extern ASN1_API asn1_node + asn1_find_node (asn1_node_const pointer, const char *name); + +extern ASN1_API int + asn1_copy_node (asn1_node dst, const char *dst_name, + asn1_node_const src, const char *src_name); +extern ASN1_API asn1_node + asn1_dup_node (asn1_node_const src, const char *src_name); + +/* Internal and low-level DER utility functions. */ + +extern ASN1_API int + asn1_get_tag_der (const unsigned char *der, int der_len, + unsigned char *cls, int *len, unsigned long *tag); + +extern ASN1_API void + asn1_octet_der (const unsigned char *str, int str_len, + unsigned char *der, int *der_len); + +extern ASN1_API int + asn1_get_octet_der (const unsigned char *der, int der_len, + int *ret_len, unsigned char *str, + int str_size, int *str_len); + +extern ASN1_API void asn1_bit_der (const unsigned char *str, int bit_len, + unsigned char *der, int *der_len); + +extern ASN1_API int + asn1_get_bit_der (const unsigned char *der, int der_len, + int *ret_len, unsigned char *str, + int str_size, int *bit_len); + +extern ASN1_API int + asn1_get_object_id_der (const unsigned char *der, + int der_len, int *ret_len, + char *str, int str_size); + +extern ASN1_API int + asn1_object_id_der (const char *str, unsigned char *der, int *der_len, + unsigned flags); + +/* Compatibility types */ + +/** + * asn1_retCode: + * + * Type formerly returned by libtasn1 functions. + * + * Deprecated: 3.0: Use int instead. + */ +typedef int asn1_retCode; + +/** + * node_asn_struct: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_node instead. + */ +#define node_asn_struct asn1_node_st + +/** + * node_asn: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_node instead. + */ +#define node_asn asn1_node_st + +/** + * ASN1_TYPE: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_node instead. + */ +#define ASN1_TYPE asn1_node + +/** + * ASN1_TYPE_EMPTY: + * + * Compat #define. + * + * Deprecated: 3.0: Use NULL instead. + */ +#define ASN1_TYPE_EMPTY NULL + +/** + * static_struct_asn: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_static_node instead. + */ +#define static_struct_asn asn1_static_node_st + +/** + * ASN1_ARRAY_TYPE: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_static_node instead. + */ +#define ASN1_ARRAY_TYPE asn1_static_node + +/** + * asn1_static_node_t: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_static_node instead. + */ +#define asn1_static_node_t asn1_static_node + +/** + * node_data_struct: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_data_node_st instead. + */ +#define node_data_struct asn1_data_node_st + +/** + * ASN1_DATA_NODE: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_data_node_st instead. + */ +#define ASN1_DATA_NODE asn1_data_node_st + +#ifdef __cplusplus +} +#endif + +#endif /* LIBTASN1_H */ From a48f811c1ecf9fc8d704a894121518f67c1b37dd Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 1 May 2020 17:12:23 +1000 Subject: [PATCH 163/367] libtasn1: disable code not needed in grub We don't expect to be able to write ASN.1, only read it, so we can disable some code. Do that with #if 0/#endif, rather than deletion. This means that the difference between upstream and grub is smaller, which should make updating libtasn1 easier in the future. With these exclusions we also avoid the need for minmax.h, which is convenient because it means we don't have to import it from gnulib. Signed-off-by: Daniel Axtens --- grub-core/lib/libtasn1/lib/coding.c | 12 ++++++++++-- grub-core/lib/libtasn1/lib/decoding.c | 2 ++ grub-core/lib/libtasn1/lib/element.c | 4 ++-- grub-core/lib/libtasn1/lib/errors.c | 3 +++ grub-core/lib/libtasn1/lib/structure.c | 10 ++++++---- include/grub/libtasn1.h | 15 +++++++++++++++ 6 files changed, 38 insertions(+), 8 deletions(-) diff --git a/grub-core/lib/libtasn1/lib/coding.c b/grub-core/lib/libtasn1/lib/coding.c index 245ea64cf0..52def59836 100644 --- a/grub-core/lib/libtasn1/lib/coding.c +++ b/grub-core/lib/libtasn1/lib/coding.c @@ -30,11 +30,11 @@ #include "parser_aux.h" #include #include "element.h" -#include "minmax.h" #include #define MAX_TAG_LEN 16 +#if 0 /******************************************************/ /* Function : _asn1_error_description_value_not_found */ /* Description: creates the ErrorDescription string */ @@ -58,6 +58,7 @@ _asn1_error_description_value_not_found (asn1_node node, Estrcat (ErrorDescription, "' not found"); } +#endif /** * asn1_length_der: @@ -244,6 +245,7 @@ asn1_encode_simple_der (unsigned int etype, const unsigned char *str, return ASN1_SUCCESS; } +#if 0 /******************************************************/ /* Function : _asn1_time_der */ /* Description: creates the DER coding for a TIME */ @@ -281,7 +283,7 @@ _asn1_time_der (unsigned char *str, int str_len, unsigned char *der, return ASN1_SUCCESS; } - +#endif /* void @@ -520,6 +522,7 @@ asn1_bit_der (const unsigned char *str, int bit_len, } +#if 0 /******************************************************/ /* Function : _asn1_complete_explicit_tag */ /* Description: add the length coding to the EXPLICIT */ @@ -596,6 +599,7 @@ _asn1_complete_explicit_tag (asn1_node node, unsigned char *der, return ASN1_SUCCESS; } +#endif const tag_and_class_st _asn1_tags[] = { [ASN1_ETYPE_GENERALSTRING] = @@ -648,6 +652,8 @@ const tag_and_class_st _asn1_tags[] = { unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]); + +#if 0 /******************************************************/ /* Function : _asn1_insert_tag_der */ /* Description: creates the DER coding of tags of one */ @@ -1413,3 +1419,5 @@ asn1_der_coding (asn1_node_const element, const char *name, void *ider, int *len asn1_delete_structure (&node); return err; } + +#endif \ No newline at end of file diff --git a/grub-core/lib/libtasn1/lib/decoding.c b/grub-core/lib/libtasn1/lib/decoding.c index ff04eb778c..42f9a92b5d 100644 --- a/grub-core/lib/libtasn1/lib/decoding.c +++ b/grub-core/lib/libtasn1/lib/decoding.c @@ -1613,6 +1613,7 @@ asn1_der_decoding (asn1_node * element, const void *ider, int ider_len, return asn1_der_decoding2 (element, ider, &ider_len, 0, errorDescription); } +#if 0 /** * asn1_der_decoding_element: * @structure: pointer to an ASN1 structure @@ -1643,6 +1644,7 @@ asn1_der_decoding_element (asn1_node * structure, const char *elementName, { return asn1_der_decoding(structure, ider, len, errorDescription); } +#endif /** * asn1_der_decoding_startEnd: diff --git a/grub-core/lib/libtasn1/lib/element.c b/grub-core/lib/libtasn1/lib/element.c index 997eb2725d..539008d8e9 100644 --- a/grub-core/lib/libtasn1/lib/element.c +++ b/grub-core/lib/libtasn1/lib/element.c @@ -191,7 +191,7 @@ _asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcache) return ASN1_SUCCESS; } - +#if 0 /** * asn1_write_value: * @node_root: pointer to a structure @@ -645,7 +645,7 @@ asn1_write_value (asn1_node node_root, const char *name, return ASN1_SUCCESS; } - +#endif #define PUT_VALUE( ptr, ptr_size, data, data_size) \ *len = data_size; \ diff --git a/grub-core/lib/libtasn1/lib/errors.c b/grub-core/lib/libtasn1/lib/errors.c index cee74daf79..42785e8622 100644 --- a/grub-core/lib/libtasn1/lib/errors.c +++ b/grub-core/lib/libtasn1/lib/errors.c @@ -57,6 +57,8 @@ static const libtasn1_error_entry error_algorithms[] = { {0, 0} }; + +#if 0 /** * asn1_perror: * @error: is an error returned by a libtasn1 function. @@ -73,6 +75,7 @@ asn1_perror (int error) const char *str = asn1_strerror (error); fprintf (stderr, "LIBTASN1 ERROR: %s\n", str ? str : "(null)"); } +#endif /** * asn1_strerror: diff --git a/grub-core/lib/libtasn1/lib/structure.c b/grub-core/lib/libtasn1/lib/structure.c index 8189c56a4c..fcfde01a39 100644 --- a/grub-core/lib/libtasn1/lib/structure.c +++ b/grub-core/lib/libtasn1/lib/structure.c @@ -76,7 +76,7 @@ _asn1_find_left (asn1_node_const node) return node->left; } - +#if 0 int _asn1_create_static_structure (asn1_node_const pointer, char *output_file_name, char *vector_name) @@ -155,7 +155,7 @@ _asn1_create_static_structure (asn1_node_const pointer, char *output_file_name, return ASN1_SUCCESS; } - +#endif /** * asn1_array2tree: @@ -718,7 +718,7 @@ asn1_create_element (asn1_node_const definitions, const char *source_name, return res; } - +#if 0 /** * asn1_print_structure: * @out: pointer to the output file (e.g. stdout). @@ -1058,7 +1058,7 @@ asn1_print_structure (FILE * out, asn1_node_const structure, const char *name, } } } - +#endif /** @@ -1153,6 +1153,7 @@ asn1_find_structure_from_oid (asn1_node_const definitions, const char *oidValue) return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ } +#if 0 /** * asn1_copy_node: * @dst: Destination asn1 node. @@ -1202,6 +1203,7 @@ asn1_copy_node (asn1_node dst, const char *dst_name, return result; } +#endif /** * asn1_dup_node: diff --git a/include/grub/libtasn1.h b/include/grub/libtasn1.h index 6fd7a30dc3..785eda2ae3 100644 --- a/include/grub/libtasn1.h +++ b/include/grub/libtasn1.h @@ -319,6 +319,8 @@ typedef struct asn1_data_node_st asn1_data_node_st; /* Functions definitions */ /***********************************/ +/* These functions are not used in grub and should not be referenced. */ +#if 0 extern ASN1_API int asn1_parser2tree (const char *file, asn1_node * definitions, char *error_desc); @@ -327,14 +329,17 @@ extern ASN1_API int asn1_parser2array (const char *inputFileName, const char *outputFileName, const char *vectorName, char *error_desc); +#endif extern ASN1_API int asn1_array2tree (const asn1_static_node * array, asn1_node * definitions, char *errorDescription); +#if 0 extern ASN1_API void asn1_print_structure (FILE * out, asn1_node_const structure, const char *name, int mode); +#endif extern ASN1_API int asn1_create_element (asn1_node_const definitions, @@ -347,9 +352,11 @@ extern ASN1_API int asn1_delete_structure2 (asn1_node * structure, unsigned int extern ASN1_API int asn1_delete_element (asn1_node structure, const char *element_name); +#if 0 extern ASN1_API int asn1_write_value (asn1_node node_root, const char *name, const void *ivalue, int len); +#endif extern ASN1_API int asn1_read_value (asn1_node_const root, const char *name, @@ -365,9 +372,11 @@ extern ASN1_API int extern ASN1_API int asn1_number_of_elements (asn1_node_const element, const char *name, int *num); +#if 0 extern ASN1_API int asn1_der_coding (asn1_node_const element, const char *name, void *ider, int *len, char *ErrorDescription); +#endif extern ASN1_API int asn1_der_decoding2 (asn1_node *element, const void *ider, @@ -378,12 +387,14 @@ extern ASN1_API int asn1_der_decoding (asn1_node * element, const void *ider, int ider_len, char *errorDescription); +#if 0 /* Do not use. Use asn1_der_decoding() instead. */ extern ASN1_API int asn1_der_decoding_element (asn1_node * structure, const char *elementName, const void *ider, int len, char *errorDescription) _ASN1_GCC_ATTR_DEPRECATED; +#endif extern ASN1_API int asn1_der_decoding_startEnd (asn1_node element, @@ -408,13 +419,17 @@ extern ASN1_API const char *asn1_find_structure_from_oid (asn1_node_const const char *oidValue); +#if 0 __LIBTASN1_PURE__ extern ASN1_API const char *asn1_check_version (const char *req_version); +#endif __LIBTASN1_PURE__ extern ASN1_API const char *asn1_strerror (int error); +#if 0 extern ASN1_API void asn1_perror (int error); +#endif #define ASN1_MAX_TAG_SIZE 4 #define ASN1_MAX_LENGTH_SIZE 9 From 05cbc933925410fcfb061d462679fae251e0a3af Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 1 May 2020 20:44:29 +1000 Subject: [PATCH 164/367] libtasn1: changes for grub compatibility Do a few things to make libtasn1 compile as part of grub: - replace strcat. grub removed strcat so replace it with the appropriate calls to memcpy and strlen. - replace c_isdigit with grub_isdigit (and don't import c-ctype from gnulib) grub_isdigit provides the same functionality as c_isdigit: it determines if the input is an ASCII digit without regard for locale. - replace GL_ATTRIBUTE_PURE with __attribute__((pure)) which been supported since gcc-2.96. This avoids messing around with gnulib. - adjust libtasn1.h: drop the ASN1_API logic, it's not needed for our modules. Unconditionally support const and pure attributes and adjust header paths. - adjust header paths to "grub/libtasn1.h". - replace a 64 bit division with a call to grub_divmod64, preventing creation of __udivdi3 calls on 32 bit platforms. Signed-off-by: Daniel Axtens --- grub-core/lib/libtasn1/lib/decoding.c | 11 ++++++----- grub-core/lib/libtasn1/lib/element.c | 3 ++- grub-core/lib/libtasn1/lib/gstr.c | 4 ++-- grub-core/lib/libtasn1/lib/int.h | 4 ++-- grub-core/lib/libtasn1/lib/parser_aux.c | 7 ++++--- include/grub/libtasn1.h | 26 ++++++------------------- 6 files changed, 22 insertions(+), 33 deletions(-) diff --git a/grub-core/lib/libtasn1/lib/decoding.c b/grub-core/lib/libtasn1/lib/decoding.c index 42f9a92b5d..7856858b27 100644 --- a/grub-core/lib/libtasn1/lib/decoding.c +++ b/grub-core/lib/libtasn1/lib/decoding.c @@ -32,7 +32,8 @@ #include #include #include -#include + +#define c_isdigit grub_isdigit #ifdef DEBUG # define warn() fprintf(stderr, "%s: %d\n", __func__, __LINE__) @@ -2008,8 +2009,8 @@ asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element, (p2->type & CONST_ASSIGN)) { strcpy (name, definitions->name); - strcat (name, "."); - strcat (name, p2->name); + memcpy (name + strlen(name), ".", sizeof(" . ")); + memcpy (name + strlen(name), p2->name, strlen(p2->name) + 1); len = sizeof (value); result = asn1_read_value (definitions, name, value, &len); @@ -2026,8 +2027,8 @@ asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element, if (p2) { strcpy (name, definitions->name); - strcat (name, "."); - strcat (name, p2->name); + memcpy (name + strlen(name), ".", sizeof(" . ")); + memcpy (name + strlen(name), p2->name, strlen(p2->name) + 1); result = asn1_create_element (definitions, name, &aux); if (result == ASN1_SUCCESS) diff --git a/grub-core/lib/libtasn1/lib/element.c b/grub-core/lib/libtasn1/lib/element.c index 539008d8e9..ed761ff56b 100644 --- a/grub-core/lib/libtasn1/lib/element.c +++ b/grub-core/lib/libtasn1/lib/element.c @@ -30,9 +30,10 @@ #include "parser_aux.h" #include #include "structure.h" -#include "c-ctype.h" #include "element.h" +#define c_isdigit grub_isdigit + void _asn1_hierarchical_name (asn1_node_const node, char *name, int name_size) { diff --git a/grub-core/lib/libtasn1/lib/gstr.c b/grub-core/lib/libtasn1/lib/gstr.c index e91a3a151c..e33875c2c7 100644 --- a/grub-core/lib/libtasn1/lib/gstr.c +++ b/grub-core/lib/libtasn1/lib/gstr.c @@ -36,13 +36,13 @@ _asn1_str_cat (char *dest, size_t dest_tot_size, const char *src) if (dest_tot_size - dest_size > str_size) { - strcat (dest, src); + memcpy (dest + dest_size, src, str_size + 1); } else { if (dest_tot_size - dest_size > 0) { - strncat (dest, src, (dest_tot_size - dest_size) - 1); + memcpy (dest + dest_size, src, (dest_tot_size - dest_size) - 1); dest[dest_tot_size - 1] = 0; } } diff --git a/grub-core/lib/libtasn1/lib/int.h b/grub-core/lib/libtasn1/lib/int.h index ea1625786c..4a568efee9 100644 --- a/grub-core/lib/libtasn1/lib/int.h +++ b/grub-core/lib/libtasn1/lib/int.h @@ -35,7 +35,7 @@ #include #endif -#include +#include "grub/libtasn1.h" #define ASN1_SMALL_VALUE_SIZE 16 @@ -115,7 +115,7 @@ extern const tag_and_class_st _asn1_tags[]; #define _asn1_strtoul(n,e,b) strtoul((const char *) n, e, b) #define _asn1_strcmp(a,b) strcmp((const char *)a, (const char *)b) #define _asn1_strcpy(a,b) strcpy((char *)a, (const char *)b) -#define _asn1_strcat(a,b) strcat((char *)a, (const char *)b) +#define _asn1_strcat(a,b) memcpy((char *)a + strlen((const char *)a), (const char *)b, strlen((const char *)b) + 1) #if SIZEOF_UNSIGNED_LONG_INT == 8 # define _asn1_strtou64(n,e,b) strtoul((const char *) n, e, b) diff --git a/grub-core/lib/libtasn1/lib/parser_aux.c b/grub-core/lib/libtasn1/lib/parser_aux.c index d5dbbf8765..89c9be69dc 100644 --- a/grub-core/lib/libtasn1/lib/parser_aux.c +++ b/grub-core/lib/libtasn1/lib/parser_aux.c @@ -26,7 +26,8 @@ #include "gstr.h" #include "structure.h" #include "element.h" -#include "c-ctype.h" + +#define c_isdigit grub_isdigit char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1]; /* identifier name not found */ @@ -40,7 +41,7 @@ char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1]; /* identifier name not fou #ifdef __clang__ __attribute__((no_sanitize("integer"))) #endif -_GL_ATTRIBUTE_PURE +__attribute__((__pure__)) static unsigned int _asn1_hash_name (const char *x) { @@ -634,7 +635,7 @@ _asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE]) count = 0; do { - d = val / 10; + d = grub_divmod64(val, 10, NULL); r = val - d * 10; temp[start + count] = '0' + (char) r; count++; diff --git a/include/grub/libtasn1.h b/include/grub/libtasn1.h index 785eda2ae3..28dbf16c4e 100644 --- a/include/grub/libtasn1.h +++ b/include/grub/libtasn1.h @@ -38,29 +38,15 @@ #ifndef LIBTASN1_H #define LIBTASN1_H -#ifndef ASN1_API -#if defined ASN1_BUILDING && defined HAVE_VISIBILITY && HAVE_VISIBILITY -#define ASN1_API __attribute__((__visibility__("default"))) -#elif defined ASN1_BUILDING && defined _MSC_VER && ! defined ASN1_STATIC -#define ASN1_API __declspec(dllexport) -#elif defined _MSC_VER && ! defined ASN1_STATIC -#define ASN1_API __declspec(dllimport) -#else +/* grub: ASN1_API is not used */ #define ASN1_API -#endif -#endif -#ifdef __GNUC__ -# define __LIBTASN1_CONST__ __attribute__((const)) -# define __LIBTASN1_PURE__ __attribute__((pure)) -#else -# define __LIBTASN1_CONST__ -# define __LIBTASN1_PURE__ -#endif +/* grub: all our supported compilers support these attributes */ +#define __LIBTASN1_CONST__ __attribute__((const)) +#define __LIBTASN1_PURE__ __attribute__((pure)) -#include -#include -#include /* for FILE* */ +#include +#include #ifdef __cplusplus extern "C" From 17dad748a0927fd6e40addae7a2b6358da4fc770 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 5 Jun 2020 17:47:25 +1000 Subject: [PATCH 165/367] libtasn1: compile into asn1 module Create a wrapper file that specifies the module license. Set up the makefile so it is built. Signed-off-by: Daniel Axtens --- grub-core/Makefile.core.def | 15 +++++++++++++++ grub-core/lib/libtasn1_wrap/wrap.c | 26 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 grub-core/lib/libtasn1_wrap/wrap.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 64cc758835..ea92468fa2 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2577,3 +2577,18 @@ module = { common = commands/i386/wrmsr.c; enable = x86; }; + +module = { + name = asn1; + common = lib/libtasn1/lib/decoding.c; + common = lib/libtasn1/lib/coding.c; + common = lib/libtasn1/lib/element.c; + common = lib/libtasn1/lib/structure.c; + common = lib/libtasn1/lib/parser_aux.c; + common = lib/libtasn1/lib/gstr.c; + common = lib/libtasn1/lib/errors.c; + common = lib/libtasn1_wrap/wrap.c; + cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)'; + // -Wno-type-limits comes from libtasn1's configure.ac + cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) -I$(srcdir)/lib/libtasn1/lib -Wno-type-limits'; +}; diff --git a/grub-core/lib/libtasn1_wrap/wrap.c b/grub-core/lib/libtasn1_wrap/wrap.c new file mode 100644 index 0000000000..622ba942e3 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/wrap.c @@ -0,0 +1,26 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 IBM Corporation + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include + +/* + * libtasn1 is provided under LGPL2.1+, which is compatible + * with GPL3+. As Grub as a whole is under GPL3+, this module + * is therefore under GPL3+ also. + */ +GRUB_MOD_LICENSE ("GPLv3+"); From 7608c15ebab9302308cd00769df4f689c7372354 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Wed, 10 Jun 2020 17:48:42 +1000 Subject: [PATCH 166/367] test_asn1: test module for libtasn1 Import tests from libtasn1 that don't use functionality we don't import. I have put them here rather than in the libtasn1 directory because: - They need much more significant changes to run in the grub context. - I don't expect they will need to be changed when updating libtasn1: I expect the old tests will usually continue to pass on new versions. This doesn't test the full decoder but that will be exercised in test suites for coming patch sets. Signed-off-by: Daniel Axtens --- .gitignore | 1 + Makefile.util.def | 6 + grub-core/Makefile.core.def | 13 ++ .../tests/CVE-2018-1000654-1_asn1_tab.h | 32 +++ .../tests/CVE-2018-1000654-2_asn1_tab.h | 36 +++ .../libtasn1_wrap/tests/CVE-2018-1000654.c | 61 +++++ .../lib/libtasn1_wrap/tests/Test_overflow.c | 138 ++++++++++++ .../lib/libtasn1_wrap/tests/Test_simple.c | 207 +++++++++++++++++ .../lib/libtasn1_wrap/tests/Test_strings.c | 150 +++++++++++++ .../libtasn1_wrap/tests/object-id-decoding.c | 116 ++++++++++ .../libtasn1_wrap/tests/object-id-encoding.c | 120 ++++++++++ .../lib/libtasn1_wrap/tests/octet-string.c | 211 ++++++++++++++++++ .../lib/libtasn1_wrap/tests/reproducers.c | 81 +++++++ grub-core/lib/libtasn1_wrap/wrap_tests.c | 75 +++++++ grub-core/lib/libtasn1_wrap/wrap_tests.h | 38 ++++ tests/test_asn1.in | 12 + 16 files changed, 1297 insertions(+) create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_overflow.c create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_simple.c create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_strings.c create mode 100644 grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c create mode 100644 grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c create mode 100644 grub-core/lib/libtasn1_wrap/tests/octet-string.c create mode 100644 grub-core/lib/libtasn1_wrap/tests/reproducers.c create mode 100644 grub-core/lib/libtasn1_wrap/wrap_tests.c create mode 100644 grub-core/lib/libtasn1_wrap/wrap_tests.h create mode 100644 tests/test_asn1.in diff --git a/.gitignore b/.gitignore index 208d1d2325..1d005887e7 100644 --- a/.gitignore +++ b/.gitignore @@ -264,6 +264,7 @@ widthspec.bin /stamp-h1 /syslinux_test /tar_test +/test_asn1 /test_sha512sum /test_unset /tests/syslinux/ubuntu10.04_grub.cfg diff --git a/Makefile.util.def b/Makefile.util.def index 6366442129..d04b3fe68a 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -1289,6 +1289,12 @@ script = { common = tests/syslinux_test.in; }; +script = { + testcase; + name = test_asn1; + common = tests/test_asn1.in; +}; + program = { testcase; name = example_unit_test; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index ea92468fa2..a32e6ada59 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2592,3 +2592,16 @@ module = { // -Wno-type-limits comes from libtasn1's configure.ac cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) -I$(srcdir)/lib/libtasn1/lib -Wno-type-limits'; }; + +module = { + name = test_asn1; + common = lib/libtasn1_wrap/tests/CVE-2018-1000654.c; + common = lib/libtasn1_wrap/tests/object-id-decoding.c; + common = lib/libtasn1_wrap/tests/object-id-encoding.c; + common = lib/libtasn1_wrap/tests/octet-string.c; + common = lib/libtasn1_wrap/tests/reproducers.c; + common = lib/libtasn1_wrap/tests/Test_overflow.c; + common = lib/libtasn1_wrap/tests/Test_simple.c; + common = lib/libtasn1_wrap/tests/Test_strings.c; + common = lib/libtasn1_wrap/wrap_tests.c; +}; diff --git a/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h new file mode 100644 index 0000000000..1e7d3d64f5 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h @@ -0,0 +1,32 @@ +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +const asn1_static_node CVE_2018_1000654_1_asn1_tab[] = { + { "TEST_TREE", 536875024, NULL }, + { NULL, 1610612748, NULL }, + { "iso", 1073741825, "1"}, + { "identified-organization", 1073741825, "3"}, + { "dod", 1073741825, "6"}, + { "internet", 1073741825, "1"}, + { "security", 1073741825, "5"}, + { "mechanisms", 1073741825, "5"}, + { "pkix", 1073741825, "7"}, + { "id-mod", 1073741825, "0"}, + { "id-pkix1-implicit-88", 1, "2"}, + { "id-xnyTest", 1879048204, NULL }, + { NULL, 1073741825, "id-ix"}, + { NULL, 1073741825, "29"}, + { NULL, 1, "1"}, + { "id-ix", 1880096780, "OBJECR"}, + { NULL, 1073741825, "id-ix"}, + { NULL, 1073741825, "29"}, + { NULL, 1, "2"}, + { "id-xnyTest", 805306380, NULL }, + { NULL, 1073741825, "id-ix"}, + { NULL, 1073741825, "29"}, + { NULL, 1, "1"}, + { NULL, 0, NULL } +}; diff --git a/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h new file mode 100644 index 0000000000..e2561e5ec6 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h @@ -0,0 +1,36 @@ +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +const asn1_static_node CVE_2018_1000654_2_asn1_tab[] = { + { "TEST_TREE", 536875024, NULL }, + { NULL, 1610612748, NULL }, + { "iso", 1073741825, "1"}, + { "identified-organization", 1073741825, "3"}, + { "dod", 1073741825, "6"}, + { "internet", 1073741825, "1"}, + { "security", 1073741825, "5"}, + { "mechanisms", 1073741825, "5"}, + { "pkix", 1073741825, "7"}, + { "id-mod", 1073741825, "0"}, + { "id-pkix1-implicit-88", 1, "2"}, + { "id-oneTest", 1879048204, NULL }, + { NULL, 1073741825, "id-two"}, + { NULL, 1073741825, "9"}, + { NULL, 1, "1"}, + { "id-two", 1879048204, NULL }, + { NULL, 1073741825, "id-three"}, + { NULL, 1073741825, "2"}, + { NULL, 1, "2"}, + { "id-three", 1879048204, NULL }, + { NULL, 1073741825, "id-four"}, + { NULL, 1073741825, "3"}, + { NULL, 1, "3"}, + { "id-four", 805306380, NULL }, + { NULL, 1073741825, "id-two"}, + { NULL, 1073741825, "3"}, + { NULL, 1, "3"}, + { NULL, 0, NULL } +}; diff --git a/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c new file mode 100644 index 0000000000..534e304521 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2002-2018 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/****************************************************************/ +/* Description: reproducer for CVE-2018-1000654 */ +/****************************************************************/ + +#include +#include +#include +#include +#include +#include "../wrap_tests.h" + +#include "CVE-2018-1000654-1_asn1_tab.h" +#include "CVE-2018-1000654-2_asn1_tab.h" + +void +test_CVE_2018_1000654 (void) +{ + int result; + asn1_node definitions = NULL; + char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + + result = asn1_array2tree (CVE_2018_1000654_1_asn1_tab, &definitions, errorDescription); + if (result != ASN1_RECURSION) + { + grub_fatal ("Error: %s\nErrorDescription = %s\n\n", + asn1_strerror (result), errorDescription); + return; + } + + asn1_delete_structure (&definitions); + + result = asn1_array2tree (CVE_2018_1000654_2_asn1_tab, &definitions, errorDescription); + if (result != ASN1_RECURSION) + { + grub_fatal ("Error: %s\nErrorDescription = %s\n\n", + asn1_strerror (result), errorDescription); + return; + } + + asn1_delete_structure (&definitions); +} diff --git a/grub-core/lib/libtasn1_wrap/tests/Test_overflow.c b/grub-core/lib/libtasn1_wrap/tests/Test_overflow.c new file mode 100644 index 0000000000..f48aea0ef8 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/Test_overflow.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2012-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* Written by Simon Josefsson */ + +#include +#include +#include +#include +#include +#include "../wrap_tests.h" + +void +test_overflow(void) +{ + /* Test that values larger than long are rejected. This has worked + fine with all versions of libtasn1. */ + + { + unsigned char der[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; + long l; + int len; + + l = asn1_get_length_der (der, sizeof der, &len); + + if (l != -2L) + { + grub_fatal ("ERROR: asn1_get_length_der bignum (l %ld len %d)\n", l, len); + return; + } + } + + /* Test that values larger than int but smaller than long are + rejected. This limitation was introduced with libtasn1 2.12. */ +#if (GRUB_LONG_MAX > GRUB_INT_MAX) + { + unsigned long num = ((long) GRUB_UINT_MAX) << 2; + unsigned char der[20]; + int der_len; + long l; + int len; + + asn1_length_der (num, der, &der_len); + + l = asn1_get_length_der (der, der_len, &len); + + if (l != -2L) + { + grub_fatal ("ERROR: asn1_get_length_der intnum (l %ld len %d)\n", l, + len); + return; + } + } +#endif + + /* Test that values larger than would fit in the input string are + rejected. This problem was fixed in libtasn1 2.12. */ + { + unsigned long num = 64; + unsigned char der[20]; + int der_len; + long l; + int len; + + asn1_length_der (num, der, &der_len); + + der_len = sizeof (der); + l = asn1_get_length_der (der, der_len, &len); + + if (l != -4L) + { + grub_fatal ("ERROR: asn1_get_length_der overflow-small (l %ld len %d)\n", + l, len); + return; + } + } + + /* Test that values larger than would fit in the input string are + rejected. This problem was fixed in libtasn1 2.12. */ + { + unsigned long num = 1073741824; + unsigned char der[20]; + int der_len; + long l; + int len; + + asn1_length_der (num, der, &der_len); + + der_len = sizeof (der); + l = asn1_get_length_der (der, der_len, &len); + + if (l != -4L) + { + grub_fatal ("ERROR: asn1_get_length_der overflow-large1 (l %ld len %d)\n", + l, len); + return; + } + } + + /* Test that values larger than would fit in the input string are + rejected. This problem was fixed in libtasn1 2.12. */ + { + unsigned long num = 2147483649; + unsigned char der[20]; + int der_len; + long l; + int len; + + asn1_length_der (num, der, &der_len); + + der_len = sizeof (der); + l = asn1_get_length_der (der, der_len, &len); + + if (l != -2L) + { + grub_fatal ("ERROR: asn1_get_length_der overflow-large2 (l %ld len %d)\n", + l, len); + return; + } + } +} diff --git a/grub-core/lib/libtasn1_wrap/tests/Test_simple.c b/grub-core/lib/libtasn1_wrap/tests/Test_simple.c new file mode 100644 index 0000000000..9f01006ddf --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/Test_simple.c @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2011-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Written by Simon Josefsson + * + */ + +#include +#include +#include +#include +#include "../wrap_tests.h" + +struct tv +{ + int bitlen; + const char *bitstr; + int derlen; + const char *der; +}; + +static const struct tv tv[] = { + {0, "", 2, "\x01\x00"}, + {1, "\x00", 3, "\x02\x07\x00"}, + {2, "\x00", 3, "\x02\x06\x00"}, + {3, "\x00", 3, "\x02\x05\x00"}, + {4, "\x00", 3, "\x02\x04\x00"}, + {5, "\x00", 3, "\x02\x03\x00"}, + {6, "\x00", 3, "\x02\x02\x00"}, + {7, "\x00", 3, "\x02\x01\x00"}, + {8, "\x00\x00", 3, "\x02\x00\x00"}, + {9, "\x00\x00", 4, "\x03\x07\x00\x00"}, + {10, "\x00\x00", 4, "\x03\x06\x00\x00"}, + {11, "\x00\x00", 4, "\x03\x05\x00\x00"}, + {12, "\x00\x00", 4, "\x03\x04\x00\x00"}, + {13, "\x00\x00", 4, "\x03\x03\x00\x00"}, + {14, "\x00\x00", 4, "\x03\x02\x00\x00"}, + {15, "\x00\x00", 4, "\x03\x01\x00\x00"}, + {16, "\x00\x00", 4, "\x03\x00\x00\x00"}, + {17, "\x00\x00\x00", 5, "\x04\x07\x00\x00\x00"}, + {18, "\x00\x00\x00", 5, "\x04\x06\x00\x00\x00"}, + {19, "\x00\x00\x00", 5, "\x04\x05\x00\x00\x00"}, + {1, "\xFF", 3, "\x02\x07\x80"}, + {2, "\xFF", 3, "\x02\x06\xc0"}, + {3, "\xFF", 3, "\x02\x05\xe0"}, + {4, "\xFF", 3, "\x02\x04\xf0"}, + {5, "\xFF", 3, "\x02\x03\xf8"}, + {6, "\xFF", 3, "\x02\x02\xfc"}, + {7, "\xFF", 3, "\x02\x01\xfe"}, + {8, "\xFF\xFF", 3, "\x02\x00\xff"}, + {9, "\xFF\xFF", 4, "\x03\x07\xff\x80"}, + {10, "\xFF\xFF", 4, "\x03\x06\xff\xc0"}, + {11, "\xFF\xFF", 4, "\x03\x05\xff\xe0"}, + {12, "\xFF\xFF", 4, "\x03\x04\xff\xf0"}, + {13, "\xFF\xFF", 4, "\x03\x03\xff\xf8"}, + {14, "\xFF\xFF", 4, "\x03\x02\xff\xfc"}, + {15, "\xFF\xFF", 4, "\x03\x01\xff\xfe"}, + {16, "\xFF\xFF", 4, "\x03\x00\xff\xff"}, + {17, "\xFF\xFF\xFF", 5, "\x04\x07\xff\xff\x80"}, + {18, "\xFF\xFF\xFF", 5, "\x04\x06\xff\xff\xc0"}, + {19, "\xFF\xFF\xFF", 5, "\x04\x05\xff\xff\xe0"}, +}; + +void +test_simple (void) +{ + int result; + unsigned char der[100]; + unsigned char str[100]; + int der_len = sizeof (der); + int str_size = sizeof (str); + int ret_len, bit_len; + grub_size_t i; + + /* Dummy test */ + + asn1_bit_der (NULL, 0, der, &der_len); + result = asn1_get_bit_der (der, 0, &ret_len, str, str_size, &bit_len); + if (result != ASN1_GENERIC_ERROR) + { + grub_fatal ("asn1_get_bit_der zero\n"); + return; + } + + /* Encode short strings with increasing bit lengths */ + + for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) + { + /* Encode */ + + asn1_bit_der ((const unsigned char *) tv[i].bitstr, tv[i].bitlen, + der, &der_len); + +#if 0 + { + size_t j; + for (j = 0; j < der_len; j++) + printf ("\\x%02x", der[j]); + printf ("\n"); + } +#endif + + if (der_len != tv[i].derlen || grub_memcmp (der, tv[i].der, der_len) != 0) + { + grub_fatal ("asn1_bit_der iter %lu\n", (unsigned long) i); + return; + } + + /* Decode it */ + + result = asn1_get_bit_der (der, der_len, &ret_len, str, + str_size, &bit_len); + if (result != ASN1_SUCCESS || ret_len != tv[i].derlen + || bit_len != tv[i].bitlen) + { + grub_fatal ("asn1_get_bit_der iter %lu, err: %d\n", (unsigned long) i, result); + return; + } + } + + + /* Decode sample from "A Layman's Guide to a Subset of ASN.1, BER, + and DER" section 5.4 "BIT STRING": "The BER encoding of the BIT + STRING value "011011100101110111" can be any of the following, + among others, depending on the choice of padding bits, the form + of length octets [...]". + */ + + /* 03 04 06 6e 5d c0 DER encoding */ + + grub_memcpy (der, "\x04\x06\x6e\x5d\xc0", 5); + der_len = 5; + + result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len); + if (result != ASN1_SUCCESS || ret_len != 5 + || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xc0", 3) != 0) + { + grub_fatal ("asn1_get_bit_der example\n"); + return; + } + + der_len = sizeof (der); + asn1_bit_der (str, bit_len, der, &der_len); + if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) + { + grub_fatal ("asn1_bit_der example roundtrip\n"); + return; + } + + /* 03 04 06 6e 5d e0 padded with "100000" */ + + grub_memcpy (der, "\x04\x06\x6e\x5d\xe0", 5); + der_len = 5; + + result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len); + if (result != ASN1_SUCCESS || ret_len != 5 + || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xe0", 3) != 0) + { + grub_fatal ("asn1_get_bit_der example padded\n"); + return; + } + + der_len = sizeof (der); + asn1_bit_der (str, bit_len, der, &der_len); + if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) + { + grub_fatal ("asn1_bit_der example roundtrip\n"); + return; + } + + /* 03 81 04 06 6e 5d c0 long form of length octets */ + + grub_memcpy (der, "\x81\x04\x06\x6e\x5d\xc0", 6); + der_len = 6; + + result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len); + + if (result != ASN1_SUCCESS || ret_len != 6 + || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xc0", 3) != 0) + { + grub_fatal ("asn1_get_bit_der example long form\n"); + return; + } + + der_len = sizeof (der); + asn1_bit_der (str, bit_len, der, &der_len); + if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) + { + grub_fatal ("asn1_bit_der example roundtrip\n"); + return; + } +} diff --git a/grub-core/lib/libtasn1_wrap/tests/Test_strings.c b/grub-core/lib/libtasn1_wrap/tests/Test_strings.c new file mode 100644 index 0000000000..dbe1474b20 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/Test_strings.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2012-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Written by Simon Josefsson + * + */ + +#include +#include +#include +#include +#include "../wrap_tests.h" + +struct tv +{ + unsigned int etype; + unsigned int str_len; + const void *str; + unsigned int der_len; + const void *der; +}; + +static const struct tv tv[] = { + {ASN1_ETYPE_IA5_STRING, 20, + "\x63\x73\x63\x61\x40\x70\x61\x73\x73\x70\x6f\x72\x74\x2e\x67\x6f\x76\x2e\x67\x72", + 22, + "\x16\x14\x63\x73\x63\x61\x40\x70\x61\x73\x73\x70\x6f\x72\x74\x2e\x67\x6f\x76\x2e\x67\x72"}, + {ASN1_ETYPE_PRINTABLE_STRING, 5, "\x4e\x69\x6b\x6f\x73", + 7, "\x13\x05\x4e\x69\x6b\x6f\x73"}, + {ASN1_ETYPE_UTF8_STRING, 12, "Αττική", + 14, "\x0c\x0c\xce\x91\xcf\x84\xcf\x84\xce\xb9\xce\xba\xce\xae"}, + {ASN1_ETYPE_TELETEX_STRING, 15, + "\x53\x69\x6d\x6f\x6e\x20\x4a\x6f\x73\x65\x66\x73\x73\x6f\x6e", + 17, + "\x14\x0f\x53\x69\x6d\x6f\x6e\x20\x4a\x6f\x73\x65\x66\x73\x73\x6f\x6e"}, + {ASN1_ETYPE_OCTET_STRING, 36, + "\x30\x22\x80\x0F\x32\x30\x31\x31\x30\x38\x32\x31\x30\x38\x30\x30\x30\x36\x5A\x81\x0F\x32\x30\x31\x31\x30\x38\x32\x33\x32\x30\x35\x39\x35\x39\x5A", + 38, + "\x04\x24\x30\x22\x80\x0F\x32\x30\x31\x31\x30\x38\x32\x31\x30\x38\x30\x30\x30\x36\x5A\x81\x0F\x32\x30\x31\x31\x30\x38\x32\x33\x32\x30\x35\x39\x35\x39\x5A"} +}; + +#define SSTR(x) sizeof(x)-1,x +static const struct tv ber[] = { + {ASN1_ETYPE_OCTET_STRING, + SSTR("\xa0\xa0"), + SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x00\x00")}, + {ASN1_ETYPE_OCTET_STRING, + SSTR("\xa0\xa0\xb0\xb0\xb0"), + SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x00\x00")}, + {ASN1_ETYPE_OCTET_STRING, + SSTR("\xa0\xa0\xb0\xb0\xb0\xa1\xa1"), + SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x24\x80\x04\x82\x00\x02\xa1\xa1\x00\x00\x00\x00")}, + {ASN1_ETYPE_OCTET_STRING, + SSTR("\xa0\xa0\xb0\xb0\xb0\xa1\xa1\xc1"), + SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x24\x80\x04\x82\x00\x02\xa1\xa1\x04\x82\x00\x01\xc1\x00\x00\x00\x00")}, +}; + +void +test_strings () +{ + int ret; + unsigned char tl[ASN1_MAX_TL_SIZE]; + unsigned int tl_len, der_len, str_len; + const unsigned char *str; + unsigned char *b; + unsigned int i; + + /* Dummy test */ + + for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) + { + /* Encode */ + tl_len = sizeof (tl); + ret = asn1_encode_simple_der (tv[i].etype, tv[i].str, tv[i].str_len, + tl, &tl_len); + if (ret != ASN1_SUCCESS) + { + grub_fatal ("Encoding error in %u: %s\n", i, + asn1_strerror (ret)); + return; + } + der_len = tl_len + tv[i].str_len; + + if (der_len != tv[i].der_len || grub_memcmp (tl, tv[i].der, tl_len) != 0) + { + grub_fatal ( + "DER encoding differs in %u! (size: %u, expected: %u)\n", + i, der_len, tv[i].der_len); + return; + } + + /* decoding */ + ret = + asn1_decode_simple_der (tv[i].etype, tv[i].der, tv[i].der_len, &str, + &str_len); + if (ret != ASN1_SUCCESS) + { + grub_fatal ("Decoding error in %u: %s\n", i, + asn1_strerror (ret)); + return; + } + + if (str_len != tv[i].str_len || grub_memcmp (str, tv[i].str, str_len) != 0) + { + grub_fatal ( + "DER decoded data differ in %u! (size: %u, expected: %u)\n", + i, der_len, tv[i].str_len); + return; + } + } + + /* BER decoding */ + for (i = 0; i < sizeof (ber) / sizeof (ber[0]); i++) + { + /* decoding */ + ret = + asn1_decode_simple_ber (ber[i].etype, ber[i].der, ber[i].der_len, &b, + &str_len, NULL); + if (ret != ASN1_SUCCESS) + { + grub_fatal ("BER decoding error in %u: %s\n", i, + asn1_strerror (ret)); + return; + } + + if (str_len != ber[i].str_len || grub_memcmp (b, ber[i].str, str_len) != 0) + { + grub_fatal ( + "BER decoded data differ in %u! (size: %u, expected: %u)\n", + i, str_len, ber[i].str_len); + return; + } + grub_free(b); + } +} diff --git a/grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c b/grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c new file mode 100644 index 0000000000..d367bbfb5a --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. + * + * This file is part of LIBTASN1. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include "../wrap_tests.h" + +struct tv +{ + int der_len; + const unsigned char *der; + const char *oid; + int expected_error; +}; + +static const struct tv tv[] = { + {.der_len = 5, + .der = (void *) "\x06\x03\x80\x37\x03", + .oid = "2.999.3", + .expected_error = ASN1_DER_ERROR /* leading 0x80 */ + }, + {.der_len = 12, + .der = (void *) "\x06\x0a\x2b\x06\x01\x80\x01\x92\x08\x09\x05\x01", + .oid = "1.3.6.1.4.1.2312.9.5.1", + .expected_error = ASN1_DER_ERROR /* leading 0x80 */ + }, + {.der_len = 6, + .der = (void *) "\x06\x04\x01\x02\x03\x04", + .oid = "0.1.2.3.4", + .expected_error = ASN1_SUCCESS}, + {.der_len = 5, + .der = (void *) "\x06\x03\x51\x02\x03", + .oid = "2.1.2.3", + .expected_error = ASN1_SUCCESS}, + {.der_len = 5, + .der = (void *) "\x06\x03\x88\x37\x03", + .oid = "2.999.3", + .expected_error = ASN1_SUCCESS}, + {.der_len = 12, + .der = (void *) "\x06\x0a\x2b\x06\x01\x04\x01\x92\x08\x09\x05\x01", + .oid = "1.3.6.1.4.1.2312.9.5.1", + .expected_error = ASN1_SUCCESS}, + {.der_len = 19, + .der = (void *) "\x06\x11\xfa\x80\x00\x00\x00\x0e\x01\x0e\xfa\x80\x00\x00\x00\x0e\x63\x6f\x6d", + .oid = "2.1998768.0.0.14.1.14.1998848.0.0.14.99.111.109", + .expected_error = ASN1_SUCCESS}, + {.der_len = 19, + .der = + (void *) + "\x06\x11\x2b\x06\x01\x04\x01\x92\x08\x09\x02\xaa\xda\xbe\xbe\xfa\x72\x01\x07", + .oid = "1.3.6.1.4.1.2312.9.2.1467399257458.1.7", + .expected_error = ASN1_SUCCESS}, +}; + +void +test_object_id_decoding (void) +{ + char str[128]; + int ret, ret_len; + grub_size_t i; + + for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) + { + /* decode */ + ret = + asn1_get_object_id_der (tv[i].der+1, + tv[i].der_len-1, &ret_len, str, + sizeof (str)); + if (ret != tv[i].expected_error) + { + grub_fatal ( + "%d: asn1_get_object_id_der iter %lu: got '%s' expected %d\n", + __LINE__, (unsigned long) i, asn1_strerror(ret), tv[i].expected_error); + return; + } + + if (tv[i].expected_error != ASN1_SUCCESS) + continue; + + if (ret_len != tv[i].der_len-1) + { + grub_fatal ( + "%d: iter %lu: error in DER, length returned is %d, had %d\n", + __LINE__, (unsigned long)i, ret_len, tv[i].der_len-1); + return; + } + + if (grub_strcmp (tv[i].oid, str) != 0) + { + grub_fatal ( + "%d: strcmp iter %lu: got invalid OID: %s, expected: %s\n", + __LINE__, (unsigned long) i, str, tv[i].oid); + return; + } + + } +} diff --git a/grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c b/grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c new file mode 100644 index 0000000000..3a83b58c59 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2019 Nikos Mavrogiannopoulos + * + * This file is part of LIBTASN1. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include "../wrap_tests.h" + +struct tv +{ + int der_len; + const unsigned char *der; + const char *oid; + int expected_error; +}; + +static const struct tv tv[] = { + {.der_len = 0, + .der = (void *) "", + .oid = "5.999.3", + .expected_error = ASN1_VALUE_NOT_VALID /* cannot start with 5 */ + }, + {.der_len = 0, + .der = (void *) "", + .oid = "0.48.9", + .expected_error = ASN1_VALUE_NOT_VALID /* second field cannot be 48 */ + }, + {.der_len = 0, + .der = (void *) "", + .oid = "1.40.9", + .expected_error = ASN1_VALUE_NOT_VALID /* second field cannot be 40 */ + }, + {.der_len = 4, + .der = (void *) "\x06\x02\x4f\x63", + .oid = "1.39.99", + .expected_error = ASN1_SUCCESS, + }, + {.der_len = 6, + .der = (void *) "\x06\x04\x01\x02\x03\x04", + .oid = "0.1.2.3.4", + .expected_error = ASN1_SUCCESS}, + {.der_len = 5, + .der = (void *) "\x06\x03\x51\x02\x03", + .oid = "2.1.2.3", + .expected_error = ASN1_SUCCESS}, + {.der_len = 5, + .der = (void *) "\x06\x03\x88\x37\x03", + .oid = "2.999.3", + .expected_error = ASN1_SUCCESS}, + {.der_len = 12, + .der = (void *) "\x06\x0a\x2b\x06\x01\x04\x01\x92\x08\x09\x05\x01", + .oid = "1.3.6.1.4.1.2312.9.5.1", + .expected_error = ASN1_SUCCESS}, + {.der_len = 19, + .der = (void *) "\x06\x11\xfa\x80\x00\x00\x00\x0e\x01\x0e\xfa\x80\x00\x00\x00\x0e\x63\x6f\x6d", + .oid = "2.1998768.0.0.14.1.14.1998848.0.0.14.99.111.109", + .expected_error = ASN1_SUCCESS}, + {.der_len = 19, + .der = + (void *) + "\x06\x11\x2b\x06\x01\x04\x01\x92\x08\x09\x02\xaa\xda\xbe\xbe\xfa\x72\x01\x07", + .oid = "1.3.6.1.4.1.2312.9.2.1467399257458.1.7", + .expected_error = ASN1_SUCCESS}, +}; + +void +test_object_id_encoding(void) +{ + unsigned char der[128]; + int ret, der_len, i; + + for (i = 0; i < (int)(sizeof (tv) / sizeof (tv[0])); i++) + { + der_len = sizeof(der); + ret = asn1_object_id_der(tv[i].oid, der, &der_len, 0); + if (ret != ASN1_SUCCESS) + { + if (ret == tv[i].expected_error) + continue; + grub_fatal ( + "%d: iter %lu, encoding of OID failed: %s\n", + __LINE__, (unsigned long) i, asn1_strerror(ret)); + return; + } + else if (ret != tv[i].expected_error) + { + grub_fatal ( + "%d: iter %lu, encoding of OID %s succeeded when expecting failure\n", + __LINE__, (unsigned long) i, tv[i].oid); + return; + } + + if (der_len != tv[i].der_len || grub_memcmp(der, tv[i].der, der_len) != 0) + { + grub_fatal ( + "%d: iter %lu, re-encoding of OID %s resulted to different string (%d vs %d bytes)\n", + __LINE__, (unsigned long) i, tv[i].oid, der_len, tv[i].der_len); + + return; + } + } +} diff --git a/grub-core/lib/libtasn1_wrap/tests/octet-string.c b/grub-core/lib/libtasn1_wrap/tests/octet-string.c new file mode 100644 index 0000000000..d8a049e8df --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/octet-string.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2011-2020 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Written by Simon Josefsson and Nikos Mavrogiannopoulos + * + */ + +#include +#include +#include +#include +#include "../wrap_tests.h" + + +struct tv +{ + const char *name; + int der_len; + const unsigned char *der_str; + int len; + const unsigned char *string; + int expected_error; + int ber; +}; + +static const struct tv tv[] = { + {.name = "primitive octet strings", + .der_len = 10, + .der_str = + (void*)"\x04\x08\x01\x23\x45\x67\x89\xab\xcd\xef", + .len = 8, + .string = + (void*)"\x01\x23\x45\x67\x89\xab\xcd\xef", + .ber = 0}, + {.der_len = 22, + .der_str = + (void*)"\x04\x14\x13\x00\xd9\xa8\x47\xf7\xf2\x1c\xf4\xb0\xec\x5f\xc1\x73\xe5\x1b\x25\xc2\x62\x27", + .len = 20, + .string = + (void*)"\x13\x00\xD9\xA8\x47\xF7\xF2\x1C\xF4\xB0\xEC\x5F\xC1\x73\xE5\x1B\x25\xC2\x62\x27"}, + + {.name = "long type of length", + .der_len = 23, + .der_str = + (void*)"\x04\x81\x14\x13\x00\xd9\xa8\x47\xf7\xf2\x1c\xf4\xb0\xec\x5f\xc1\x73\xe5\x1b\x25\xc2\x62\x27", + .len = 20, + .string = + (void*)"\x13\x00\xD9\xA8\x47\xF7\xF2\x1C\xF4\xB0\xEC\x5F\xC1\x73\xE5\x1B\x25\xC2\x62\x27", + .ber = 1}, + {.der_len = 11, + .der_str = + (void*)"\x04\x81\x08\x01\x23\x45\x67\x89\xab\xcd\xef", + .len = 8, + .string = + (void*)"\x01\x23\x45\x67\x89\xab\xcd\xef", + .ber = 1}, + + {.name = "constructed - indefinite", + .der_len = 11, + .der_str = (void*)"\x24\x80\x04\x05\x01\x02\x03\x04\x05\x00\x00", + .len = 5, + .string = (void*)"\x01\x02\x03\x04\x05", + .ber = 1, + }, + + {.name = "constructed - definite - concat", + .der_len = 12, + .der_str = (void*)"\x24\x0a\x04\x04\x0a\x0b\x0c\x0d\x04\x02\x0e\x0f", + .len = 6, + .string = (void*)"\x0a\x0b\x0c\x0d\x0e\x0f", + .ber = 1, + }, + {.name = "constructed - definite - recursive", + .der_len = 15, + .der_str = (void*)"\x24\x0d\x04\x04\x0a\x0b\x0c\x0d\x24\x05\x04\x00\x04\x01\x0f", + .len = 5, + .string = (void*)"\x0a\x0b\x0c\x0d\x0f", + .ber = 1, + }, + {.name = "constructed - definite - single", + .der_len = 7, + .der_str = (void*)"\x24\x05\x04\x03\x01\x02\x03", + .len = 3, + .string = (void*)"\x01\x02\x03", + .ber = 1, + }, + + /* a large amount of recursive indefinite encoding */ + {.name = "a large amount of recursive indefinite encoding", + .der_len = 29325, + .der_str = (void*)"\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80", + .len = 0, + .ber = 1, + .expected_error = ASN1_DER_ERROR + } +}; + +void +test_octet_string (void) +{ + unsigned char str[100]; + unsigned char der[100]; + int der_len = sizeof (der); + int str_size = sizeof (str); + unsigned char *tmp = NULL; + int ret, ret_len; + grub_size_t i; + + for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) + { + /* Decode */ + + if (tv[i].ber == 0) + { + str_size = sizeof (str); + ret = + asn1_get_octet_der (tv[i].der_str + 1, + tv[i].der_len - 1, &ret_len, str, + sizeof (str), &str_size); + if (ret != tv[i].expected_error) + { + grub_fatal ( + "%d: asn1_get_octet_der: %s: got %d expected %d\n", + __LINE__, tv[i].name, ret, + tv[i].expected_error); + return; + } + if (tv[i].expected_error) + continue; + + if (ret_len != tv[i].der_len - 1) + { + grub_fatal ( + "%d: error in DER, length returned is %d, had %d\n", + __LINE__, ret_len, tv[i].der_len - 1); + return; + } + + if (str_size != tv[i].len + || grub_memcmp (tv[i].string, str, tv[i].len) != 0) + { + grub_fatal ( + "%d: memcmp: %s: got invalid decoding\n", + __LINE__, tv[i].name); + + return; + } + + /* Encode */ + der_len = sizeof (der); + asn1_octet_der (str, str_size, der, &der_len); + + if (der_len != tv[i].der_len - 1 + || grub_memcmp (tv[i].der_str + 1, der, tv[i].der_len - 1) != 0) + { + grub_fatal ( + "encoding: %s: got invalid encoding\n", + tv[i].name); + return; + } + } + + ret = + asn1_decode_simple_ber (ASN1_ETYPE_OCTET_STRING, + tv[i].der_str, tv[i].der_len, + &tmp, (unsigned int*)&str_size, (unsigned int*)&der_len); + if (ret != tv[i].expected_error) + { + grub_fatal ( + "%d: asn1_decode_simple_ber: %s: got %s expected %s\n", + __LINE__, tv[i].name, asn1_strerror(ret), asn1_strerror(tv[i].expected_error)); + return; + } + if (tv[i].expected_error) + continue; + + if (der_len != tv[i].der_len) + { + grub_fatal ( + "%d: error: %s: DER, length returned is %d, had %d\n", + __LINE__, tv[i].name, der_len, tv[i].der_len); + return; + } + + if (str_size != tv[i].len || grub_memcmp (tv[i].string, tmp, tv[i].len) != 0) + { + grub_fatal ( + "%d: memcmp: %s: got invalid decoding\n", + __LINE__, tv[i].name); + return; + } + grub_free (tmp); + tmp = NULL; + + } +} diff --git a/grub-core/lib/libtasn1_wrap/tests/reproducers.c b/grub-core/lib/libtasn1_wrap/tests/reproducers.c new file mode 100644 index 0000000000..dc7268d4c6 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/reproducers.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2019 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/****************************************************************/ +/* Description: run reproducers for several fixed issues */ +/****************************************************************/ + +#include +#include +#include +#include "../wrap_tests.h" + +#define CONST_DOWN (1U<<29) + +/* produces endless loop (fixed by d4b624b2): + * The following translates into a single node with all pointers + * (right,left,down) set to NULL. */ +const asn1_static_node endless_asn1_tab[] = { + { "TEST_TREE", 536875024, NULL }, + { NULL, 0, NULL } +}; + +/* produces memory leak (fixed by f16d1ff9): + * 152 bytes in 1 blocks are definitely lost in loss record 1 of 1 + * at 0x4837B65: calloc (vg_replace_malloc.c:762) + * by 0x4851C0D: _asn1_add_static_node (parser_aux.c:71) + * by 0x4853AAC: asn1_array2tree (structure.c:200) + * by 0x10923B: main (single_node.c:67) + */ +const asn1_static_node tab[] = { +{ "a", CONST_DOWN, "" }, +{ "b", 0, "" }, +{ "c", 0, "" }, +{ NULL, 0, NULL } +}; + +void +test_reproducers (void) +{ + int result; + asn1_node definitions = NULL; + char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + + result = asn1_array2tree (endless_asn1_tab, &definitions, errorDescription); + if (result != ASN1_SUCCESS) + { + grub_fatal ("Error: %s\nErrorDescription = %s\n\n", + asn1_strerror (result), errorDescription); + return; + } + + asn1_delete_structure (&definitions); + + definitions = NULL; + result = asn1_array2tree (tab, &definitions, errorDescription); + if (result != ASN1_SUCCESS) + { + grub_fatal ("Error: %s\nErrorDescription = %s\n\n", + asn1_strerror (result), errorDescription); + return; + } + + asn1_delete_structure (&definitions); +} diff --git a/grub-core/lib/libtasn1_wrap/wrap_tests.c b/grub-core/lib/libtasn1_wrap/wrap_tests.c new file mode 100644 index 0000000000..75fcd21f0d --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/wrap_tests.c @@ -0,0 +1,75 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 IBM Corporation + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include "wrap_tests.h" + +/* + * libtasn1 tests - from which this is derived - are provided under GPL3+. + */ +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_command_t cmd; + +static grub_err_t +grub_cmd_asn1test (grub_command_t cmdd __attribute__((unused)), + int argc __attribute__((unused)), + char **args __attribute__((unused))) +{ + grub_printf ("test_CVE_2018_1000654\n"); + test_CVE_2018_1000654 (); + + grub_printf ("test_object_id_decoding\n"); + test_object_id_decoding (); + + grub_printf ("test_object_id_encoding\n"); + test_object_id_encoding (); + + grub_printf ("test_octet_string\n"); + test_octet_string (); + + grub_printf ("test_overflow\n"); + test_overflow (); + + grub_printf ("test_reproducers\n"); + test_overflow (); + + grub_printf ("test_simple\n"); + test_simple (); + + grub_printf ("test_strings\n"); + test_strings (); + + grub_printf ("ASN.1 self-tests passed\n"); + + return GRUB_ERR_NONE; +} + + +GRUB_MOD_INIT(test_asn1) +{ + cmd = grub_register_command ("test_asn1", grub_cmd_asn1test, NULL, + "Run self-tests for the ASN.1 parser."); +} + +GRUB_MOD_FINI(test_asn1) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/lib/libtasn1_wrap/wrap_tests.h b/grub-core/lib/libtasn1_wrap/wrap_tests.h new file mode 100644 index 0000000000..555e56dd20 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/wrap_tests.h @@ -0,0 +1,38 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 IBM Corporation + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef LIBTASN1_WRAP_TESTS_H +#define LIBTASN1_WRAP_TESTS_H + +void test_CVE_2018_1000654 (void); + +void test_object_id_encoding (void); + +void test_object_id_decoding (void); + +void test_octet_string (void); + +void test_overflow (void); + +void test_reproducers (void); + +void test_simple (void); + +void test_strings (void); + +#endif diff --git a/tests/test_asn1.in b/tests/test_asn1.in new file mode 100644 index 0000000000..8173c5c270 --- /dev/null +++ b/tests/test_asn1.in @@ -0,0 +1,12 @@ +#! @BUILD_SHEBANG@ +set -e + +. "@builddir@/grub-core/modinfo.sh" + +out=`echo test_asn1 | @builddir@/grub-shell` + +if [ "$(echo "$out" | tail -n 1)" != "ASN.1 self-tests passed" ]; then + echo "ASN.1 test failure: $out" + exit 1 +fi + From c1c59a9a086c677b9837122c3859f3d3143f3281 Mon Sep 17 00:00:00 2001 From: Alastair D'Silva Date: Mon, 6 Jul 2020 13:33:04 +1000 Subject: [PATCH 167/367] grub-install: support embedding x509 certificates To support verification of appended signatures, we need a way to embed the necessary public keys. Existing appended signature schemes in the Linux kernel use X.509 certificates, so allow certificates to be embedded in the grub core image in the same way as PGP keys. Signed-off-by: Alastair D'Silva Signed-off-by: Daniel Axtens --- grub-core/commands/pgp.c | 2 +- include/grub/kernel.h | 4 +++- include/grub/util/install.h | 7 +++++-- util/grub-install-common.c | 23 +++++++++++++++++++++- util/grub-mkimage.c | 15 +++++++++++++-- util/mkimage.c | 38 +++++++++++++++++++++++++++++++++++-- 6 files changed, 80 insertions(+), 9 deletions(-) diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c index 355a43844a..b81ac0ae46 100644 --- a/grub-core/commands/pgp.c +++ b/grub-core/commands/pgp.c @@ -944,7 +944,7 @@ GRUB_MOD_INIT(pgp) grub_memset (&pseudo_file, 0, sizeof (pseudo_file)); /* Not an ELF module, skip. */ - if (header->type != OBJ_TYPE_PUBKEY) + if (header->type != OBJ_TYPE_GPG_PUBKEY) continue; pseudo_file.fs = &pseudo_fs; diff --git a/include/grub/kernel.h b/include/grub/kernel.h index 55849777ea..98edc0863f 100644 --- a/include/grub/kernel.h +++ b/include/grub/kernel.h @@ -30,7 +30,9 @@ enum OBJ_TYPE_PREFIX, OBJ_TYPE_PUBKEY, OBJ_TYPE_DTB, - OBJ_TYPE_DISABLE_SHIM_LOCK + OBJ_TYPE_DISABLE_SHIM_LOCK, + OBJ_TYPE_GPG_PUBKEY, + OBJ_TYPE_X509_PUBKEY, }; /* The module header. */ diff --git a/include/grub/util/install.h b/include/grub/util/install.h index cf4531e02b..51f3b13ac1 100644 --- a/include/grub/util/install.h +++ b/include/grub/util/install.h @@ -67,6 +67,8 @@ N_("SBAT metadata"), 0 }, \ { "disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, \ N_("disable shim_lock verifier"), 0 }, \ + { "x509key", 'x', N_("FILE"), 0, \ + N_("embed FILE as an x509 certificate for signature checking"), 0}, \ { "appended-signature-size", GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE,\ "SIZE", 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), \ 1}, \ @@ -188,8 +190,9 @@ void grub_install_generate_image (const char *dir, const char *prefix, FILE *out, const char *outname, char *mods[], - char *memdisk_path, char **pubkey_paths, - size_t npubkeys, + char *memdisk_path, + char **pubkey_paths, size_t npubkeys, + char **x509key_paths, size_t nx509keys, char *config_path, const struct grub_install_image_target_desc *image_target, int note, size_t appsig_size, diff --git a/util/grub-install-common.c b/util/grub-install-common.c index a74fee16e2..c603f5b308 100644 --- a/util/grub-install-common.c +++ b/util/grub-install-common.c @@ -460,6 +460,8 @@ static char **pubkeys; static size_t npubkeys; static char *sbat; static int disable_shim_lock; +static char **x509keys; +static size_t nx509keys; static grub_compression_t compression; static size_t appsig_size; @@ -501,6 +503,12 @@ grub_install_parse (int key, char *arg) case GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK: disable_shim_lock = 1; return 1; + case 'x': + x509keys = xrealloc (x509keys, + sizeof (x509keys[0]) + * (nx509keys + 1)); + x509keys[nx509keys++] = xstrdup (arg); + return 1; case GRUB_INSTALL_OPTIONS_VERBOSITY: verbosity++; @@ -627,6 +635,9 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, for (pk = pubkeys; pk < pubkeys + npubkeys; pk++) slen += 20 + grub_strlen (*pk); + for (pk = x509keys; pk < x509keys + nx509keys; pk++) + slen += 10 + grub_strlen (*pk); + for (md = modules.entries; *md; md++) { slen += 10 + grub_strlen (*md); @@ -655,6 +666,14 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, *p++ = ' '; } + for (pk = x509keys; pk < x509keys + nx509keys; pk++) + { + p = grub_stpcpy (p, "--x509 '"); + p = grub_stpcpy (p, *pk); + *p++ = '\''; + *p++ = ' '; + } + for (md = modules.entries; *md; md++) { *p++ = '\''; @@ -684,7 +703,9 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, grub_install_generate_image (dir, prefix, fp, outname, modules.entries, memdisk_path, - pubkeys, npubkeys, config_path, tgt, + pubkeys, npubkeys, + x509keys, nx509keys, + config_path, tgt, note, appsig_size, compression, dtb, sbat, disable_shim_lock); while (dc--) diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c index 8a53310548..e1f1112784 100644 --- a/util/grub-mkimage.c +++ b/util/grub-mkimage.c @@ -75,7 +75,8 @@ static struct argp_option options[] = { /* TRANSLATORS: "embed" is a verb (command description). "*/ {"config", 'c', N_("FILE"), 0, N_("embed FILE as an early config"), 0}, /* TRANSLATORS: "embed" is a verb (command description). "*/ - {"pubkey", 'k', N_("FILE"), 0, N_("embed FILE as public key for signature checking"), 0}, + {"pubkey", 'k', N_("FILE"), 0, N_("embed FILE as public key for PGP signature checking"), 0}, + {"x509", 'x', N_("FILE"), 0, N_("embed FILE as an x509 certificate for appended signature checking"), 0}, /* TRANSLATORS: NOTE is a name of segment. */ {"note", 'n', 0, 0, N_("add NOTE segment for CHRP IEEE1275"), 0}, {"output", 'o', N_("FILE"), 0, N_("output a generated image to FILE [default=stdout]"), 0}, @@ -124,6 +125,8 @@ struct arguments char *dtb; char **pubkeys; size_t npubkeys; + char **x509keys; + size_t nx509keys; char *font; char *config; char *sbat; @@ -206,6 +209,13 @@ argp_parser (int key, char *arg, struct argp_state *state) arguments->pubkeys[arguments->npubkeys++] = xstrdup (arg); break; + case 'x': + arguments->x509keys = xrealloc (arguments->x509keys, + sizeof (arguments->x509keys[0]) + * (arguments->nx509keys + 1)); + arguments->x509keys[arguments->nx509keys++] = xstrdup (arg); + break; + case 'c': if (arguments->config) free (arguments->config); @@ -332,7 +342,8 @@ main (int argc, char *argv[]) grub_install_generate_image (arguments.dir, arguments.prefix, fp, arguments.output, arguments.modules, arguments.memdisk, arguments.pubkeys, - arguments.npubkeys, arguments.config, + arguments.npubkeys, arguments.x509keys, + arguments.nx509keys, arguments.config, arguments.image_target, arguments.note, arguments.appsig_size, arguments.comp, arguments.dtb, arguments.sbat, diff --git a/util/mkimage.c b/util/mkimage.c index bab1227601..8319e8dfbd 100644 --- a/util/mkimage.c +++ b/util/mkimage.c @@ -867,7 +867,8 @@ void grub_install_generate_image (const char *dir, const char *prefix, FILE *out, const char *outname, char *mods[], char *memdisk_path, char **pubkey_paths, - size_t npubkeys, char *config_path, + size_t npubkeys, char **x509key_paths, + size_t nx509keys, char *config_path, const struct grub_install_image_target_desc *image_target, int note, size_t appsig_size, grub_compression_t comp, const char *dtb_path, const char *sbat_path, @@ -913,6 +914,19 @@ grub_install_generate_image (const char *dir, const char *prefix, } } + { + size_t i; + for (i = 0; i < nx509keys; i++) + { + size_t curs; + curs = ALIGN_ADDR (grub_util_get_image_size (x509key_paths[i])); + grub_util_info ("the size of x509 public key %u is 0x%" + GRUB_HOST_PRIxLONG_LONG, + (unsigned) i, (unsigned long long) curs); + total_module_size += curs + sizeof (struct grub_module_header); + } + } + if (memdisk_path) { memdisk_size = ALIGN_UP(grub_util_get_image_size (memdisk_path), 512); @@ -1034,7 +1048,7 @@ grub_install_generate_image (const char *dir, const char *prefix, curs = grub_util_get_image_size (pubkey_paths[i]); header = (struct grub_module_header *) (kernel_img + offset); - header->type = grub_host_to_target32 (OBJ_TYPE_PUBKEY); + header->type = grub_host_to_target32 (OBJ_TYPE_GPG_PUBKEY); header->size = grub_host_to_target32 (curs + sizeof (*header)); offset += sizeof (*header); @@ -1043,6 +1057,26 @@ grub_install_generate_image (const char *dir, const char *prefix, } } + { + size_t i; + for (i = 0; i < nx509keys; i++) + { + size_t curs; + struct grub_module_header *header; + + curs = grub_util_get_image_size (x509key_paths[i]); + + header = (struct grub_module_header *) (kernel_img + offset); + header->type = grub_host_to_target32 (OBJ_TYPE_X509_PUBKEY); + header->size = grub_host_to_target32 (curs + sizeof (*header)); + offset += sizeof (*header); + + grub_util_load_image (x509key_paths[i], kernel_img + offset); + offset += ALIGN_ADDR (curs); + } + } + + if (memdisk_path) { struct grub_module_header *header; From 9fec78f626dbd8d4120656d0f36dedd220d19e77 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Thu, 30 Jul 2020 01:35:10 +1000 Subject: [PATCH 168/367] appended signatures: import GNUTLS's ASN.1 description files In order to parse PKCS#7 messages and X.509 certificates with libtasn1, we need some information about how they are encoded. We get these from GNUTLS, which has the benefit that they support the features we need and are well tested. The GNUTLS license is LGPLv2.1+, which is GPLv3 compatible, allowing us to import it without issue. Signed-off-by: Daniel Axtens --- .../commands/appendedsig/gnutls_asn1_tab.c | 121 +++++ .../commands/appendedsig/pkix_asn1_tab.c | 484 ++++++++++++++++++ 2 files changed, 605 insertions(+) create mode 100644 grub-core/commands/appendedsig/gnutls_asn1_tab.c create mode 100644 grub-core/commands/appendedsig/pkix_asn1_tab.c diff --git a/grub-core/commands/appendedsig/gnutls_asn1_tab.c b/grub-core/commands/appendedsig/gnutls_asn1_tab.c new file mode 100644 index 0000000000..ddd1314e63 --- /dev/null +++ b/grub-core/commands/appendedsig/gnutls_asn1_tab.c @@ -0,0 +1,121 @@ +#include +#include + +const asn1_static_node gnutls_asn1_tab[] = { + { "GNUTLS", 536872976, NULL }, + { NULL, 1073741836, NULL }, + { "RSAPublicKey", 1610612741, NULL }, + { "modulus", 1073741827, NULL }, + { "publicExponent", 3, NULL }, + { "RSAPrivateKey", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "modulus", 1073741827, NULL }, + { "publicExponent", 1073741827, NULL }, + { "privateExponent", 1073741827, NULL }, + { "prime1", 1073741827, NULL }, + { "prime2", 1073741827, NULL }, + { "exponent1", 1073741827, NULL }, + { "exponent2", 1073741827, NULL }, + { "coefficient", 1073741827, NULL }, + { "otherPrimeInfos", 16386, "OtherPrimeInfos"}, + { "ProvableSeed", 1610612741, NULL }, + { "algorithm", 1073741836, NULL }, + { "seed", 7, NULL }, + { "OtherPrimeInfos", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "OtherPrimeInfo"}, + { "OtherPrimeInfo", 1610612741, NULL }, + { "prime", 1073741827, NULL }, + { "exponent", 1073741827, NULL }, + { "coefficient", 3, NULL }, + { "AlgorithmIdentifier", 1610612741, NULL }, + { "algorithm", 1073741836, NULL }, + { "parameters", 541081613, NULL }, + { "algorithm", 1, NULL }, + { "DigestInfo", 1610612741, NULL }, + { "digestAlgorithm", 1073741826, "DigestAlgorithmIdentifier"}, + { "digest", 7, NULL }, + { "DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, + { "DSAPublicKey", 1073741827, NULL }, + { "DSAParameters", 1610612741, NULL }, + { "p", 1073741827, NULL }, + { "q", 1073741827, NULL }, + { "g", 3, NULL }, + { "DSASignatureValue", 1610612741, NULL }, + { "r", 1073741827, NULL }, + { "s", 3, NULL }, + { "DSAPrivateKey", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "p", 1073741827, NULL }, + { "q", 1073741827, NULL }, + { "g", 1073741827, NULL }, + { "Y", 1073741827, NULL }, + { "priv", 3, NULL }, + { "DHParameter", 1610612741, NULL }, + { "prime", 1073741827, NULL }, + { "base", 1073741827, NULL }, + { "privateValueLength", 16387, NULL }, + { "ECParameters", 1610612754, NULL }, + { "namedCurve", 12, NULL }, + { "ECPrivateKey", 1610612741, NULL }, + { "Version", 1073741827, NULL }, + { "privateKey", 1073741831, NULL }, + { "parameters", 1610637314, "ECParameters"}, + { NULL, 2056, "0"}, + { "publicKey", 536895494, NULL }, + { NULL, 2056, "1"}, + { "PrincipalName", 1610612741, NULL }, + { "name-type", 1610620931, NULL }, + { NULL, 2056, "0"}, + { "name-string", 536879115, NULL }, + { NULL, 1073743880, "1"}, + { NULL, 27, NULL }, + { "KRB5PrincipalName", 1610612741, NULL }, + { "realm", 1610620955, NULL }, + { NULL, 2056, "0"}, + { "principalName", 536879106, "PrincipalName"}, + { NULL, 2056, "1"}, + { "RSAPSSParameters", 1610612741, NULL }, + { "hashAlgorithm", 1610637314, "AlgorithmIdentifier"}, + { NULL, 2056, "0"}, + { "maskGenAlgorithm", 1610637314, "AlgorithmIdentifier"}, + { NULL, 2056, "1"}, + { "saltLength", 1610653699, NULL }, + { NULL, 1073741833, "20"}, + { NULL, 2056, "2"}, + { "trailerField", 536911875, NULL }, + { NULL, 1073741833, "1"}, + { NULL, 2056, "3"}, + { "GOSTParameters", 1610612741, NULL }, + { "publicKeyParamSet", 1073741836, NULL }, + { "digestParamSet", 16396, NULL }, + { "GOSTParametersOld", 1610612741, NULL }, + { "publicKeyParamSet", 1073741836, NULL }, + { "digestParamSet", 1073741836, NULL }, + { "encryptionParamSet", 16396, NULL }, + { "GOSTPrivateKey", 1073741831, NULL }, + { "GOSTPrivateKeyOld", 1073741827, NULL }, + { "IssuerSignTool", 1610612741, NULL }, + { "signTool", 1073741858, NULL }, + { "cATool", 1073741858, NULL }, + { "signToolCert", 1073741858, NULL }, + { "cAToolCert", 34, NULL }, + { "Gost28147-89-EncryptedKey", 1610612741, NULL }, + { "encryptedKey", 1073741831, NULL }, + { "maskKey", 1610637319, NULL }, + { NULL, 4104, "0"}, + { "macKey", 7, NULL }, + { "SubjectPublicKeyInfo", 1610612741, NULL }, + { "algorithm", 1073741826, "AlgorithmIdentifier"}, + { "subjectPublicKey", 6, NULL }, + { "GostR3410-TransportParameters", 1610612741, NULL }, + { "encryptionParamSet", 1073741836, NULL }, + { "ephemeralPublicKey", 1610637314, "SubjectPublicKeyInfo"}, + { NULL, 4104, "0"}, + { "ukm", 7, NULL }, + { "GostR3410-KeyTransport", 536870917, NULL }, + { "sessionEncryptedKey", 1073741826, "Gost28147-89-EncryptedKey"}, + { "transportParameters", 536895490, "GostR3410-TransportParameters"}, + { NULL, 4104, "0"}, + { NULL, 0, NULL } +}; diff --git a/grub-core/commands/appendedsig/pkix_asn1_tab.c b/grub-core/commands/appendedsig/pkix_asn1_tab.c new file mode 100644 index 0000000000..adef69d95c --- /dev/null +++ b/grub-core/commands/appendedsig/pkix_asn1_tab.c @@ -0,0 +1,484 @@ +#include +#include + +const asn1_static_node pkix_asn1_tab[] = { + { "PKIX1", 536875024, NULL }, + { NULL, 1073741836, NULL }, + { "PrivateKeyUsagePeriod", 1610612741, NULL }, + { "notBefore", 1610637349, NULL }, + { NULL, 4104, "0"}, + { "notAfter", 536895525, NULL }, + { NULL, 4104, "1"}, + { "AuthorityKeyIdentifier", 1610612741, NULL }, + { "keyIdentifier", 1610637319, NULL }, + { NULL, 4104, "0"}, + { "authorityCertIssuer", 1610637314, "GeneralNames"}, + { NULL, 4104, "1"}, + { "authorityCertSerialNumber", 536895490, "CertificateSerialNumber"}, + { NULL, 4104, "2"}, + { "SubjectKeyIdentifier", 1073741831, NULL }, + { "KeyUsage", 1073741830, NULL }, + { "DirectoryString", 1610612754, NULL }, + { "teletexString", 1612709918, NULL }, + { "MAX", 524298, "1"}, + { "printableString", 1612709919, NULL }, + { "MAX", 524298, "1"}, + { "universalString", 1612709920, NULL }, + { "MAX", 524298, "1"}, + { "utf8String", 1612709922, NULL }, + { "MAX", 524298, "1"}, + { "bmpString", 1612709921, NULL }, + { "MAX", 524298, "1"}, + { "ia5String", 538968093, NULL }, + { "MAX", 524298, "1"}, + { "SubjectAltName", 1073741826, "GeneralNames"}, + { "GeneralNames", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "GeneralName"}, + { "GeneralName", 1610612754, NULL }, + { "otherName", 1610620930, "AnotherName"}, + { NULL, 4104, "0"}, + { "rfc822Name", 1610620957, NULL }, + { NULL, 4104, "1"}, + { "dNSName", 1610620957, NULL }, + { NULL, 4104, "2"}, + { "x400Address", 1610620941, NULL }, + { NULL, 4104, "3"}, + { "directoryName", 1610620939, NULL }, + { NULL, 1073743880, "4"}, + { NULL, 2, "RelativeDistinguishedName"}, + { "ediPartyName", 1610620941, NULL }, + { NULL, 4104, "5"}, + { "uniformResourceIdentifier", 1610620957, NULL }, + { NULL, 4104, "6"}, + { "iPAddress", 1610620935, NULL }, + { NULL, 4104, "7"}, + { "registeredID", 536879116, NULL }, + { NULL, 4104, "8"}, + { "AnotherName", 1610612741, NULL }, + { "type-id", 1073741836, NULL }, + { "value", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "type-id", 1, NULL }, + { "IssuerAltName", 1073741826, "GeneralNames"}, + { "BasicConstraints", 1610612741, NULL }, + { "cA", 1610645508, NULL }, + { NULL, 131081, NULL }, + { "pathLenConstraint", 537411587, NULL }, + { "0", 10, "MAX"}, + { "CRLDistributionPoints", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "DistributionPoint"}, + { "DistributionPoint", 1610612741, NULL }, + { "distributionPoint", 1610637314, "DistributionPointName"}, + { NULL, 2056, "0"}, + { "reasons", 1610637314, "ReasonFlags"}, + { NULL, 4104, "1"}, + { "cRLIssuer", 536895490, "GeneralNames"}, + { NULL, 4104, "2"}, + { "DistributionPointName", 1610612754, NULL }, + { "fullName", 1610620930, "GeneralNames"}, + { NULL, 4104, "0"}, + { "nameRelativeToCRLIssuer", 536879106, "RelativeDistinguishedName"}, + { NULL, 4104, "1"}, + { "ReasonFlags", 1073741830, NULL }, + { "ExtKeyUsageSyntax", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 12, NULL }, + { "AuthorityInfoAccessSyntax", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "AccessDescription"}, + { "AccessDescription", 1610612741, NULL }, + { "accessMethod", 1073741836, NULL }, + { "accessLocation", 2, "GeneralName"}, + { "Attribute", 1610612741, NULL }, + { "type", 1073741836, NULL }, + { "values", 536870927, NULL }, + { NULL, 13, NULL }, + { "AttributeTypeAndValue", 1610612741, NULL }, + { "type", 1073741836, NULL }, + { "value", 13, NULL }, + { "Name", 1610612754, NULL }, + { "rdnSequence", 536870923, NULL }, + { NULL, 2, "RelativeDistinguishedName"}, + { "DistinguishedName", 1610612747, NULL }, + { NULL, 2, "RelativeDistinguishedName"}, + { "RelativeDistinguishedName", 1612709903, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "AttributeTypeAndValue"}, + { "Certificate", 1610612741, NULL }, + { "tbsCertificate", 1073741826, "TBSCertificate"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 6, NULL }, + { "TBSCertificate", 1610612741, NULL }, + { "version", 1610653699, NULL }, + { NULL, 1073741833, "0"}, + { NULL, 2056, "0"}, + { "serialNumber", 1073741826, "CertificateSerialNumber"}, + { "signature", 1073741826, "AlgorithmIdentifier"}, + { "issuer", 1073741826, "Name"}, + { "validity", 1073741826, "Validity"}, + { "subject", 1073741826, "Name"}, + { "subjectPublicKeyInfo", 1073741826, "SubjectPublicKeyInfo"}, + { "issuerUniqueID", 1610637314, "UniqueIdentifier"}, + { NULL, 4104, "1"}, + { "subjectUniqueID", 1610637314, "UniqueIdentifier"}, + { NULL, 4104, "2"}, + { "extensions", 536895490, "Extensions"}, + { NULL, 2056, "3"}, + { "CertificateSerialNumber", 1073741827, NULL }, + { "Validity", 1610612741, NULL }, + { "notBefore", 1073741826, "Time"}, + { "notAfter", 2, "Time"}, + { "Time", 1610612754, NULL }, + { "utcTime", 1073741860, NULL }, + { "generalTime", 37, NULL }, + { "UniqueIdentifier", 1073741830, NULL }, + { "SubjectPublicKeyInfo", 1610612741, NULL }, + { "algorithm", 1073741826, "AlgorithmIdentifier"}, + { "subjectPublicKey", 6, NULL }, + { "Extensions", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "Extension"}, + { "Extension", 1610612741, NULL }, + { "extnID", 1073741836, NULL }, + { "critical", 1610645508, NULL }, + { NULL, 131081, NULL }, + { "extnValue", 7, NULL }, + { "CertificateList", 1610612741, NULL }, + { "tbsCertList", 1073741826, "TBSCertList"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 6, NULL }, + { "TBSCertList", 1610612741, NULL }, + { "version", 1073758211, NULL }, + { "signature", 1073741826, "AlgorithmIdentifier"}, + { "issuer", 1073741826, "Name"}, + { "thisUpdate", 1073741826, "Time"}, + { "nextUpdate", 1073758210, "Time"}, + { "revokedCertificates", 1610629131, NULL }, + { NULL, 536870917, NULL }, + { "userCertificate", 1073741826, "CertificateSerialNumber"}, + { "revocationDate", 1073741826, "Time"}, + { "crlEntryExtensions", 16386, "Extensions"}, + { "crlExtensions", 536895490, "Extensions"}, + { NULL, 2056, "0"}, + { "AlgorithmIdentifier", 1610612741, NULL }, + { "algorithm", 1073741836, NULL }, + { "parameters", 541081613, NULL }, + { "algorithm", 1, NULL }, + { "Dss-Sig-Value", 1610612741, NULL }, + { "r", 1073741827, NULL }, + { "s", 3, NULL }, + { "Dss-Parms", 1610612741, NULL }, + { "p", 1073741827, NULL }, + { "q", 1073741827, NULL }, + { "g", 3, NULL }, + { "pkcs-7-ContentInfo", 1610612741, NULL }, + { "contentType", 1073741836, NULL }, + { "content", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "contentType", 1, NULL }, + { "pkcs-7-DigestInfo", 1610612741, NULL }, + { "digestAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "digest", 7, NULL }, + { "pkcs-7-SignedData", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "digestAlgorithms", 1073741826, "pkcs-7-DigestAlgorithmIdentifiers"}, + { "encapContentInfo", 1073741826, "pkcs-7-EncapsulatedContentInfo"}, + { "certificates", 1610637314, "pkcs-7-CertificateSet"}, + { NULL, 4104, "0"}, + { "crls", 1610637314, "pkcs-7-CertificateRevocationLists"}, + { NULL, 4104, "1"}, + { "signerInfos", 2, "pkcs-7-SignerInfos"}, + { "pkcs-7-DigestAlgorithmIdentifiers", 1610612751, NULL }, + { NULL, 2, "AlgorithmIdentifier"}, + { "pkcs-7-EncapsulatedContentInfo", 1610612741, NULL }, + { "eContentType", 1073741836, NULL }, + { "eContent", 536895501, NULL }, + { NULL, 2056, "0"}, + { "pkcs-7-CertificateRevocationLists", 1610612751, NULL }, + { NULL, 13, NULL }, + { "pkcs-7-CertificateChoices", 1610612754, NULL }, + { "certificate", 13, NULL }, + { "pkcs-7-CertificateSet", 1610612751, NULL }, + { NULL, 2, "pkcs-7-CertificateChoices"}, + { "IssuerAndSerialNumber", 1610612741, NULL }, + { "issuer", 1073741826, "Name"}, + { "serialNumber", 2, "CertificateSerialNumber"}, + { "pkcs-7-SignerInfo", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "sid", 1073741826, "SignerIdentifier"}, + { "digestAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signedAttrs", 1610637314, "SignedAttributes"}, + { NULL, 4104, "0"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 1073741831, NULL }, + { "unsignedAttrs", 536895490, "SignedAttributes"}, + { NULL, 4104, "1"}, + { "SignedAttributes", 1612709903, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "Attribute"}, + { "SignerIdentifier", 1610612754, NULL }, + { "issuerAndSerialNumber", 1073741826, "IssuerAndSerialNumber"}, + { "subjectKeyIdentifier", 536879111, NULL }, + { NULL, 4104, "0"}, + { "pkcs-7-SignerInfos", 1610612751, NULL }, + { NULL, 2, "pkcs-7-SignerInfo"}, + { "pkcs-10-CertificationRequestInfo", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "subject", 1073741826, "Name"}, + { "subjectPKInfo", 1073741826, "SubjectPublicKeyInfo"}, + { "attributes", 536879106, "Attributes"}, + { NULL, 4104, "0"}, + { "Attributes", 1610612751, NULL }, + { NULL, 2, "Attribute"}, + { "pkcs-10-CertificationRequest", 1610612741, NULL }, + { "certificationRequestInfo", 1073741826, "pkcs-10-CertificationRequestInfo"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 6, NULL }, + { "pkcs-9-at-challengePassword", 1879048204, NULL }, + { "iso", 1073741825, "1"}, + { "member-body", 1073741825, "2"}, + { "us", 1073741825, "840"}, + { "rsadsi", 1073741825, "113549"}, + { "pkcs", 1073741825, "1"}, + { NULL, 1073741825, "9"}, + { NULL, 1, "7"}, + { "pkcs-9-challengePassword", 1610612754, NULL }, + { "printableString", 1073741855, NULL }, + { "utf8String", 34, NULL }, + { "pkcs-9-localKeyId", 1073741831, NULL }, + { "pkcs-8-PrivateKeyInfo", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "privateKey", 1073741831, NULL }, + { "attributes", 536895490, "Attributes"}, + { NULL, 4104, "0"}, + { "pkcs-8-EncryptedPrivateKeyInfo", 1610612741, NULL }, + { "encryptionAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "encryptedData", 2, "pkcs-8-EncryptedData"}, + { "pkcs-8-EncryptedData", 1073741831, NULL }, + { "pkcs-5-des-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "8"}, + { "pkcs-5-des-EDE3-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "8"}, + { "pkcs-5-aes128-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "16"}, + { "pkcs-5-aes192-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "16"}, + { "pkcs-5-aes256-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "16"}, + { "Gost28147-89-Parameters", 1610612741, NULL }, + { "iv", 1073741831, NULL }, + { "encryptionParamSet", 12, NULL }, + { "pkcs-5-PBE-params", 1610612741, NULL }, + { "salt", 1073741831, NULL }, + { "iterationCount", 3, NULL }, + { "pkcs-5-PBES2-params", 1610612741, NULL }, + { "keyDerivationFunc", 1073741826, "AlgorithmIdentifier"}, + { "encryptionScheme", 2, "AlgorithmIdentifier"}, + { "pkcs-5-PBKDF2-params", 1610612741, NULL }, + { "salt", 1610612754, NULL }, + { "specified", 1073741831, NULL }, + { "otherSource", 2, "AlgorithmIdentifier"}, + { "iterationCount", 1611137027, NULL }, + { "1", 10, "MAX"}, + { "keyLength", 1611153411, NULL }, + { "1", 10, "MAX"}, + { "prf", 16386, "AlgorithmIdentifier"}, + { "pkcs-12-PFX", 1610612741, NULL }, + { "version", 1610874883, NULL }, + { "v3", 1, "3"}, + { "authSafe", 1073741826, "pkcs-7-ContentInfo"}, + { "macData", 16386, "pkcs-12-MacData"}, + { "pkcs-12-PbeParams", 1610612741, NULL }, + { "salt", 1073741831, NULL }, + { "iterations", 3, NULL }, + { "pkcs-12-MacData", 1610612741, NULL }, + { "mac", 1073741826, "pkcs-7-DigestInfo"}, + { "macSalt", 1073741831, NULL }, + { "iterations", 536903683, NULL }, + { NULL, 9, "1"}, + { "pkcs-12-AuthenticatedSafe", 1610612747, NULL }, + { NULL, 2, "pkcs-7-ContentInfo"}, + { "pkcs-12-SafeContents", 1610612747, NULL }, + { NULL, 2, "pkcs-12-SafeBag"}, + { "pkcs-12-SafeBag", 1610612741, NULL }, + { "bagId", 1073741836, NULL }, + { "bagValue", 1614815245, NULL }, + { NULL, 1073743880, "0"}, + { "badId", 1, NULL }, + { "bagAttributes", 536887311, NULL }, + { NULL, 2, "Attribute"}, + { "pkcs-12-CertBag", 1610612741, NULL }, + { "certId", 1073741836, NULL }, + { "certValue", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "certId", 1, NULL }, + { "pkcs-12-CRLBag", 1610612741, NULL }, + { "crlId", 1073741836, NULL }, + { "crlValue", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "crlId", 1, NULL }, + { "pkcs-12-SecretBag", 1610612741, NULL }, + { "secretTypeId", 1073741836, NULL }, + { "secretValue", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "secretTypeId", 1, NULL }, + { "pkcs-7-Data", 1073741831, NULL }, + { "pkcs-7-EncryptedData", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "encryptedContentInfo", 1073741826, "pkcs-7-EncryptedContentInfo"}, + { "unprotectedAttrs", 536895490, "pkcs-7-UnprotectedAttributes"}, + { NULL, 4104, "1"}, + { "pkcs-7-EncryptedContentInfo", 1610612741, NULL }, + { "contentType", 1073741836, NULL }, + { "contentEncryptionAlgorithm", 1073741826, "pkcs-7-ContentEncryptionAlgorithmIdentifier"}, + { "encryptedContent", 536895495, NULL }, + { NULL, 4104, "0"}, + { "pkcs-7-ContentEncryptionAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, + { "pkcs-7-UnprotectedAttributes", 1612709903, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "Attribute"}, + { "ProxyCertInfo", 1610612741, NULL }, + { "pCPathLenConstraint", 1611153411, NULL }, + { "0", 10, "MAX"}, + { "proxyPolicy", 2, "ProxyPolicy"}, + { "ProxyPolicy", 1610612741, NULL }, + { "policyLanguage", 1073741836, NULL }, + { "policy", 16391, NULL }, + { "certificatePolicies", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "PolicyInformation"}, + { "PolicyInformation", 1610612741, NULL }, + { "policyIdentifier", 1073741836, NULL }, + { "policyQualifiers", 538984459, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "PolicyQualifierInfo"}, + { "PolicyQualifierInfo", 1610612741, NULL }, + { "policyQualifierId", 1073741836, NULL }, + { "qualifier", 541065229, NULL }, + { "policyQualifierId", 1, NULL }, + { "CPSuri", 1073741853, NULL }, + { "UserNotice", 1610612741, NULL }, + { "noticeRef", 1073758210, "NoticeReference"}, + { "explicitText", 16386, "DisplayText"}, + { "NoticeReference", 1610612741, NULL }, + { "organization", 1073741826, "DisplayText"}, + { "noticeNumbers", 536870923, NULL }, + { NULL, 3, NULL }, + { "DisplayText", 1610612754, NULL }, + { "ia5String", 1612709917, NULL }, + { "200", 524298, "1"}, + { "visibleString", 1612709923, NULL }, + { "200", 524298, "1"}, + { "bmpString", 1612709921, NULL }, + { "200", 524298, "1"}, + { "utf8String", 538968098, NULL }, + { "200", 524298, "1"}, + { "OCSPRequest", 1610612741, NULL }, + { "tbsRequest", 1073741826, "TBSRequest"}, + { "optionalSignature", 536895490, "Signature"}, + { NULL, 2056, "0"}, + { "TBSRequest", 1610612741, NULL }, + { "version", 1610653699, NULL }, + { NULL, 1073741833, "0"}, + { NULL, 2056, "0"}, + { "requestorName", 1610637314, "GeneralName"}, + { NULL, 2056, "1"}, + { "requestList", 1610612747, NULL }, + { NULL, 2, "Request"}, + { "requestExtensions", 536895490, "Extensions"}, + { NULL, 2056, "2"}, + { "Signature", 1610612741, NULL }, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 1073741830, NULL }, + { "certs", 536895499, NULL }, + { NULL, 1073743880, "0"}, + { NULL, 2, "Certificate"}, + { "Request", 1610612741, NULL }, + { "reqCert", 1073741826, "CertID"}, + { "singleRequestExtensions", 536895490, "Extensions"}, + { NULL, 2056, "0"}, + { "CertID", 1610612741, NULL }, + { "hashAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "issuerNameHash", 1073741831, NULL }, + { "issuerKeyHash", 1073741831, NULL }, + { "serialNumber", 2, "CertificateSerialNumber"}, + { "OCSPResponse", 1610612741, NULL }, + { "responseStatus", 1073741826, "OCSPResponseStatus"}, + { "responseBytes", 536895490, "ResponseBytes"}, + { NULL, 2056, "0"}, + { "OCSPResponseStatus", 1610874901, NULL }, + { "successful", 1073741825, "0"}, + { "malformedRequest", 1073741825, "1"}, + { "internalError", 1073741825, "2"}, + { "tryLater", 1073741825, "3"}, + { "sigRequired", 1073741825, "5"}, + { "unauthorized", 1, "6"}, + { "ResponseBytes", 1610612741, NULL }, + { "responseType", 1073741836, NULL }, + { "response", 7, NULL }, + { "BasicOCSPResponse", 1610612741, NULL }, + { "tbsResponseData", 1073741826, "ResponseData"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 1073741830, NULL }, + { "certs", 536895499, NULL }, + { NULL, 1073743880, "0"}, + { NULL, 2, "Certificate"}, + { "ResponseData", 1610612741, NULL }, + { "version", 1610653699, NULL }, + { NULL, 1073741833, "0"}, + { NULL, 2056, "0"}, + { "responderID", 1073741826, "ResponderID"}, + { "producedAt", 1073741861, NULL }, + { "responses", 1610612747, NULL }, + { NULL, 2, "SingleResponse"}, + { "responseExtensions", 536895490, "Extensions"}, + { NULL, 2056, "1"}, + { "ResponderID", 1610612754, NULL }, + { "byName", 1610620939, NULL }, + { NULL, 1073743880, "1"}, + { NULL, 2, "RelativeDistinguishedName"}, + { "byKey", 536879111, NULL }, + { NULL, 2056, "2"}, + { "SingleResponse", 1610612741, NULL }, + { "certID", 1073741826, "CertID"}, + { "certStatus", 1073741826, "CertStatus"}, + { "thisUpdate", 1073741861, NULL }, + { "nextUpdate", 1610637349, NULL }, + { NULL, 2056, "0"}, + { "singleExtensions", 536895490, "Extensions"}, + { NULL, 2056, "1"}, + { "CertStatus", 1610612754, NULL }, + { "good", 1610620948, NULL }, + { NULL, 4104, "0"}, + { "revoked", 1610620930, "RevokedInfo"}, + { NULL, 4104, "1"}, + { "unknown", 536879106, "UnknownInfo"}, + { NULL, 4104, "2"}, + { "RevokedInfo", 1610612741, NULL }, + { "revocationTime", 1073741861, NULL }, + { "revocationReason", 537157653, NULL }, + { NULL, 1073743880, "0"}, + { "unspecified", 1, "0"}, + { "UnknownInfo", 1073741844, NULL }, + { "NameConstraints", 1610612741, NULL }, + { "permittedSubtrees", 1610637314, "GeneralSubtrees"}, + { NULL, 4104, "0"}, + { "excludedSubtrees", 536895490, "GeneralSubtrees"}, + { NULL, 4104, "1"}, + { "GeneralSubtrees", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "GeneralSubtree"}, + { "GeneralSubtree", 1610612741, NULL }, + { "base", 1073741826, "GeneralName"}, + { "minimum", 1610653699, NULL }, + { NULL, 1073741833, "0"}, + { NULL, 4104, "0"}, + { "maximum", 536895491, NULL }, + { NULL, 4104, "1"}, + { "TlsFeatures", 536870923, NULL }, + { NULL, 3, NULL }, + { NULL, 0, NULL } +}; From ccea040c29aff6a21941842475932694c4efc4cf Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Thu, 30 Jul 2020 01:33:46 +1000 Subject: [PATCH 169/367] appended signatures: parse PKCS#7 signedData and X.509 certificates This code allows us to parse: - PKCS#7 signedData messages. Only a single signerInfo is supported, which is all that the Linux sign-file utility supports creating out-of-the-box. Only RSA, SHA-256 and SHA-512 are supported. Any certificate embedded in the PKCS#7 message will be ignored. - X.509 certificates: at least enough to verify the signatures on the PKCS#7 messages. We expect that the certificates embedded in grub will be leaf certificates, not CA certificates. The parser enforces this. Signed-off-by: Daniel Axtens --- grub-core/commands/appendedsig/appendedsig.h | 110 +++ grub-core/commands/appendedsig/asn1util.c | 102 ++ grub-core/commands/appendedsig/pkcs7.c | 305 ++++++ grub-core/commands/appendedsig/x509.c | 958 +++++++++++++++++++ 4 files changed, 1475 insertions(+) create mode 100644 grub-core/commands/appendedsig/appendedsig.h create mode 100644 grub-core/commands/appendedsig/asn1util.c create mode 100644 grub-core/commands/appendedsig/pkcs7.c create mode 100644 grub-core/commands/appendedsig/x509.c diff --git a/grub-core/commands/appendedsig/appendedsig.h b/grub-core/commands/appendedsig/appendedsig.h new file mode 100644 index 0000000000..9792ef3901 --- /dev/null +++ b/grub-core/commands/appendedsig/appendedsig.h @@ -0,0 +1,110 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 IBM Corporation. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include + +extern asn1_node _gnutls_gnutls_asn; +extern asn1_node _gnutls_pkix_asn; + +#define MAX_OID_LEN 32 + +/* + * One or more x509 certificates. + * + * We do limited parsing: extracting only the serial, CN and RSA public key. + */ +struct x509_certificate +{ + struct x509_certificate *next; + + grub_uint8_t *serial; + grub_size_t serial_len; + + char *subject; + grub_size_t subject_len; + + /* We only support RSA public keys. This encodes [modulus, publicExponent] */ + gcry_mpi_t mpis[2]; +}; + +/* + * A PKCS#7 signedData message. + * + * We make no attempt to match intelligently, so we don't save any info about + * the signer. We also support only 1 signerInfo, so we only store a single + * MPI for the signature. + */ +struct pkcs7_signedData +{ + const gcry_md_spec_t *hash; + gcry_mpi_t sig_mpi; +}; + + +/* Do libtasn1 init */ +int asn1_init (void); + +/* + * Import a DER-encoded certificate at 'data', of size 'size'. + * + * Place the results into 'results', which must be already allocated. + */ +grub_err_t +certificate_import (void *data, grub_size_t size, + struct x509_certificate *results); + +/* + * Release all the storage associated with the x509 certificate. + * If the caller dynamically allocated the certificate, it must free it. + * The caller is also responsible for maintenance of the linked list. + */ +void certificate_release (struct x509_certificate *cert); + +/* + * Parse a PKCS#7 message, which must be a signedData message. + * + * The message must be in 'sigbuf' and of size 'data_size'. The result is + * placed in 'msg', which must already be allocated. + */ +grub_err_t +parse_pkcs7_signedData (void *sigbuf, grub_size_t data_size, + struct pkcs7_signedData *msg); + +/* + * Release all the storage associated with the PKCS#7 message. + * If the caller dynamically allocated the message, it must free it. + */ +void pkcs7_signedData_release (struct pkcs7_signedData *msg); + +/* + * Read a value from an ASN1 node, allocating memory to store it. + * + * It will work for anything where the size libtasn1 returns is right: + * - Integers + * - Octet strings + * - DER encoding of other structures + * It will _not_ work for things where libtasn1 size requires adjustment: + * - Strings that require an extra NULL byte at the end + * - Bit strings because libtasn1 returns the length in bits, not bytes. + * + * If the function returns a non-NULL value, the caller must free it. + */ +void *grub_asn1_allocate_and_read (asn1_node node, const char *name, + const char *friendly_name, + int *content_size); diff --git a/grub-core/commands/appendedsig/asn1util.c b/grub-core/commands/appendedsig/asn1util.c new file mode 100644 index 0000000000..eff095a9df --- /dev/null +++ b/grub-core/commands/appendedsig/asn1util.c @@ -0,0 +1,102 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 IBM Corporation. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "appendedsig.h" + +asn1_node _gnutls_gnutls_asn = ASN1_TYPE_EMPTY; +asn1_node _gnutls_pkix_asn = ASN1_TYPE_EMPTY; + +extern const ASN1_ARRAY_TYPE gnutls_asn1_tab[]; +extern const ASN1_ARRAY_TYPE pkix_asn1_tab[]; + +/* + * Read a value from an ASN1 node, allocating memory to store it. + * + * It will work for anything where the size libtasn1 returns is right: + * - Integers + * - Octet strings + * - DER encoding of other structures + * It will _not_ work for things where libtasn1 size requires adjustment: + * - Strings that require an extra NULL byte at the end + * - Bit strings because libtasn1 returns the length in bits, not bytes. + * + * If the function returns a non-NULL value, the caller must free it. + */ +void * +grub_asn1_allocate_and_read (asn1_node node, const char *name, + const char *friendly_name, int *content_size) +{ + int result; + grub_uint8_t *tmpstr = NULL; + int tmpstr_size = 0; + + result = asn1_read_value (node, name, NULL, &tmpstr_size); + if (result != ASN1_MEM_ERROR) + { + grub_snprintf (grub_errmsg, sizeof (grub_errmsg), + _ + ("Reading size of %s did not return expected status: %s"), + friendly_name, asn1_strerror (result)); + grub_errno = GRUB_ERR_BAD_FILE_TYPE; + return NULL; + } + + tmpstr = grub_malloc (tmpstr_size); + if (tmpstr == NULL) + { + grub_snprintf (grub_errmsg, sizeof (grub_errmsg), + "Could not allocate memory to store %s", friendly_name); + grub_errno = GRUB_ERR_OUT_OF_MEMORY; + return NULL; + } + + result = asn1_read_value (node, name, tmpstr, &tmpstr_size); + if (result != ASN1_SUCCESS) + { + grub_free (tmpstr); + grub_snprintf (grub_errmsg, sizeof (grub_errmsg), + "Error reading %s: %s", + friendly_name, asn1_strerror (result)); + grub_errno = GRUB_ERR_BAD_FILE_TYPE; + return NULL; + } + + *content_size = tmpstr_size; + + return tmpstr; +} + +int +asn1_init (void) +{ + int res; + res = asn1_array2tree (gnutls_asn1_tab, &_gnutls_gnutls_asn, NULL); + if (res != ASN1_SUCCESS) + { + return res; + } + res = asn1_array2tree (pkix_asn1_tab, &_gnutls_pkix_asn, NULL); + return res; +} diff --git a/grub-core/commands/appendedsig/pkcs7.c b/grub-core/commands/appendedsig/pkcs7.c new file mode 100644 index 0000000000..dc6afe203f --- /dev/null +++ b/grub-core/commands/appendedsig/pkcs7.c @@ -0,0 +1,305 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 IBM Corporation. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include "appendedsig.h" +#include +#include +#include + + +static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + +/* + * RFC 5652 s 5.1 + */ +const char *signedData_oid = "1.2.840.113549.1.7.2"; + +/* + * RFC 4055 s 2.1 + */ +const char *sha256_oid = "2.16.840.1.101.3.4.2.1"; +const char *sha512_oid = "2.16.840.1.101.3.4.2.3"; + +static grub_err_t +process_content (grub_uint8_t * content, int size, + struct pkcs7_signedData *msg) +{ + int res; + asn1_node signed_part; + grub_err_t err = GRUB_ERR_NONE; + char algo_oid[MAX_OID_LEN]; + int algo_oid_size = sizeof (algo_oid); + int algo_count; + char version; + int version_size = sizeof (version); + grub_uint8_t *result_buf; + int result_size = 0; + int crls_size = 0; + gcry_error_t gcry_err; + + res = asn1_create_element (_gnutls_pkix_asn, "PKIX1.pkcs-7-SignedData", + &signed_part); + if (res != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not create ASN.1 structure for PKCS#7 signed part."); + } + + res = asn1_der_decoding2 (&signed_part, content, &size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (res != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Error reading PKCS#7 signed data: %s", asn1_error); + goto cleanup_signed_part; + } + + /* SignedData ::= SEQUENCE { + * version CMSVersion, + * digestAlgorithms DigestAlgorithmIdentifiers, + * encapContentInfo EncapsulatedContentInfo, + * certificates [0] IMPLICIT CertificateSet OPTIONAL, + * crls [1] IMPLICIT RevocationInfoChoices OPTIONAL, + * signerInfos SignerInfos } + */ + + /* version per the algo in 5.1, must be 1 */ + res = asn1_read_value (signed_part, "version", &version, &version_size); + if (res != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Error reading signedData version: %s", + asn1_strerror (res)); + goto cleanup_signed_part; + } + + if (version != 1) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Unexpected signature version v%d, only v1 supported", + version); + goto cleanup_signed_part; + } + + /* + * digestAlgorithms DigestAlgorithmIdentifiers + * + * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier + * DigestAlgorithmIdentifer is an X.509 AlgorithmIdentifier (10.1.1) + * + * RFC 4055 s 2.1: + * sha256Identifier AlgorithmIdentifier ::= { id-sha256, NULL } + * sha512Identifier AlgorithmIdentifier ::= { id-sha512, NULL } + * + * We only support 1 element in the set, and we do not check parameters atm. + */ + res = + asn1_number_of_elements (signed_part, "digestAlgorithms", &algo_count); + if (res != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Error counting number of digest algorithms: %s", + asn1_strerror (res)); + goto cleanup_signed_part; + } + + if (algo_count != 1) + { + err = + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Only 1 digest algorithm is supported"); + goto cleanup_signed_part; + } + + res = + asn1_read_value (signed_part, "digestAlgorithms.?1.algorithm", algo_oid, + &algo_oid_size); + if (res != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Error reading digest algorithm: %s", + asn1_strerror (res)); + goto cleanup_signed_part; + } + + if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0) + { + msg->hash = grub_crypto_lookup_md_by_name ("sha512"); + } + else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0) + { + msg->hash = grub_crypto_lookup_md_by_name ("sha256"); + } + else + { + err = + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Only SHA-256 and SHA-512 hashes are supported, found OID %s", + algo_oid); + goto cleanup_signed_part; + } + + if (!msg->hash) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Hash algorithm for OID %s not loaded", algo_oid); + goto cleanup_signed_part; + } + + /* + * We ignore the certificates, but we don't permit CRLs. + * A CRL entry might be revoking the certificate we're using, and we have + * no way of dealing with that at the moment. + */ + res = asn1_read_value (signed_part, "crls", NULL, &crls_size); + if (res != ASN1_ELEMENT_NOT_FOUND) + { + err = + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "PKCS#7 messages with embedded CRLs are not supported"); + goto cleanup_signed_part; + } + + /* read the signature */ + result_buf = + grub_asn1_allocate_and_read (signed_part, "signerInfos.?1.signature", + "signature data", &result_size); + if (!result_buf) + { + err = grub_errno; + goto cleanup_signed_part; + } + + gcry_err = + gcry_mpi_scan (&(msg->sig_mpi), GCRYMPI_FMT_USG, result_buf, result_size, + NULL); + if (gcry_err != GPG_ERR_NO_ERROR) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Error loading signature into MPI structure: %d", + gcry_err); + goto cleanup_result; + } + +cleanup_result: + grub_free (result_buf); +cleanup_signed_part: + asn1_delete_structure (&signed_part); + + return err; +} + +grub_err_t +parse_pkcs7_signedData (void *sigbuf, grub_size_t data_size, + struct pkcs7_signedData *msg) +{ + int res; + asn1_node content_info; + grub_err_t err = GRUB_ERR_NONE; + char content_oid[MAX_OID_LEN]; + grub_uint8_t *content; + int content_size; + int content_oid_size = sizeof (content_oid); + int size; + + if (data_size > GRUB_INT_MAX) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "Cannot parse a PKCS#7 message where data size > INT_MAX"); + size = (int) data_size; + + res = asn1_create_element (_gnutls_pkix_asn, + "PKIX1.pkcs-7-ContentInfo", &content_info); + if (res != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not create ASN.1 structure for PKCS#7 data: %s", + asn1_strerror (res)); + } + + res = asn1_der_decoding2 (&content_info, sigbuf, &size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (res != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Error decoding PKCS#7 message DER: %s", asn1_error); + goto cleanup; + } + + /* + * ContentInfo ::= SEQUENCE { + * contentType ContentType, + * content [0] EXPLICIT ANY DEFINED BY contentType } + * + * ContentType ::= OBJECT IDENTIFIER + */ + res = + asn1_read_value (content_info, "contentType", content_oid, + &content_oid_size); + if (res != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Error reading PKCS#7 content type: %s", + asn1_strerror (res)); + goto cleanup; + } + + /* OID for SignedData defined in 5.1 */ + if (grub_strncmp (signedData_oid, content_oid, content_oid_size) != 0) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Unexpected content type in PKCS#7 message: OID %s", + content_oid); + goto cleanup; + } + + content = + grub_asn1_allocate_and_read (content_info, "content", + "PKCS#7 message content", &content_size); + if (!content) + { + err = grub_errno; + goto cleanup; + } + + err = process_content (content, content_size, msg); + grub_free (content); + +cleanup: + asn1_delete_structure (&content_info); + return err; +} + +/* + * Release all the storage associated with the PKCS#7 message. + * If the caller dynamically allocated the message, it must free it. + */ +void +pkcs7_signedData_release (struct pkcs7_signedData *msg) +{ + gcry_mpi_release (msg->sig_mpi); +} diff --git a/grub-core/commands/appendedsig/x509.c b/grub-core/commands/appendedsig/x509.c new file mode 100644 index 0000000000..2b38b3670a --- /dev/null +++ b/grub-core/commands/appendedsig/x509.c @@ -0,0 +1,958 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 IBM Corporation. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "appendedsig.h" + +static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + +/* + * RFC 3279 2.3.1 RSA Keys + */ +const char *rsaEncryption_oid = "1.2.840.113549.1.1.1"; + +/* + * RFC 5280 Appendix A + */ +const char *commonName_oid = "2.5.4.3"; + +/* + * RFC 5280 4.2.1.3 Key Usage + */ +const char *keyUsage_oid = "2.5.29.15"; + +/* + * RFC 5280 4.2.1.9 Basic Constraints + */ +const char *basicConstraints_oid = "2.5.29.19"; + +/* + * RFC 3279 2.3.1 + * + * The RSA public key MUST be encoded using the ASN.1 type RSAPublicKey: + * + * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER } -- e + * + * where modulus is the modulus n, and publicExponent is the public + * exponent e. + */ +static grub_err_t +grub_parse_rsa_pubkey (grub_uint8_t * der, int dersize, + struct x509_certificate *certificate) +{ + int result; + asn1_node spk = ASN1_TYPE_EMPTY; + grub_uint8_t *m_data, *e_data; + int m_size, e_size; + grub_err_t err = GRUB_ERR_NONE; + gcry_error_t gcry_err; + + result = + asn1_create_element (_gnutls_gnutls_asn, "GNUTLS.RSAPublicKey", &spk); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Cannot create storage for public key ASN.1 data"); + } + + result = asn1_der_decoding2 (&spk, der, &dersize, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Cannot decode certificate public key DER: %s", + asn1_error); + goto cleanup; + } + + m_data = + grub_asn1_allocate_and_read (spk, "modulus", "RSA modulus", &m_size); + if (!m_data) + { + err = grub_errno; + goto cleanup; + } + + e_data = + grub_asn1_allocate_and_read (spk, "publicExponent", "RSA public exponent", + &e_size); + if (!e_data) + { + err = grub_errno; + goto cleanup_m_data; + } + + /* + * convert m, e to mpi + * + * nscanned is not set for FMT_USG, it's only set for FMT_PGP, + * so we can't verify it + */ + gcry_err = + gcry_mpi_scan (&certificate->mpis[0], GCRYMPI_FMT_USG, m_data, m_size, + NULL); + if (gcry_err != GPG_ERR_NO_ERROR) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error loading RSA modulus into MPI structure: %d", + gcry_err); + goto cleanup_e_data; + } + + gcry_err = + gcry_mpi_scan (&certificate->mpis[1], GCRYMPI_FMT_USG, e_data, e_size, + NULL); + if (gcry_err != GPG_ERR_NO_ERROR) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error loading RSA exponent into MPI structure: %d", + gcry_err); + goto cleanup_m_mpi; + } + + grub_free (e_data); + grub_free (m_data); + asn1_delete_structure (&spk); + return GRUB_ERR_NONE; + +cleanup_m_mpi: + gcry_mpi_release (certificate->mpis[0]); +cleanup_e_data: + grub_free (e_data); +cleanup_m_data: + grub_free (m_data); +cleanup: + asn1_delete_structure (&spk); + return err; +} + + +/* + * RFC 5280: + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + * + * AlgorithmIdentifiers come from RFC 3279, we are not strictly compilant as we + * only support RSA Encryption. + */ + +static grub_err_t +grub_x509_read_subject_public_key (asn1_node asn, + struct x509_certificate *results) +{ + int result; + grub_err_t err; + const char *algo_name = + "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm"; + const char *params_name = + "tbsCertificate.subjectPublicKeyInfo.algorithm.parameters"; + const char *pk_name = + "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey"; + char algo_oid[MAX_OID_LEN]; + int algo_size = sizeof (algo_oid); + char params_value[2]; + int params_size = sizeof (params_value); + grub_uint8_t *key_data = NULL; + int key_size = 0; + unsigned int key_type; + + /* algorithm: see notes for rsaEncryption_oid */ + result = asn1_read_value (asn, algo_name, algo_oid, &algo_size); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading x509 public key algorithm: %s", + asn1_strerror (result)); + } + + if (grub_strncmp (algo_oid, rsaEncryption_oid, sizeof (rsaEncryption_oid)) + != 0) + { + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported x509 public key algorithm: %s", + algo_oid); + } + + /* + * RFC 3279 2.3.1 + * The rsaEncryption OID is intended to be used in the algorithm field + * of a value of type AlgorithmIdentifier. The parameters field MUST + * have ASN.1 type NULL for this algorithm identifier. + */ + result = asn1_read_value (asn, params_name, params_value, ¶ms_size); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading x509 public key parameters: %s", + asn1_strerror (result)); + } + + if (params_value[0] != ASN1_TAG_NULL) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Invalid x509 public key parameters: expected NULL"); + } + + /* + * RFC 3279 2.3.1: The DER encoded RSAPublicKey is the value of the BIT + * STRING subjectPublicKey. + */ + result = asn1_read_value_type (asn, pk_name, NULL, &key_size, &key_type); + if (result != ASN1_MEM_ERROR) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading size of x509 public key: %s", + asn1_strerror (result)); + } + if (key_type != ASN1_ETYPE_BIT_STRING) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Unexpected ASN.1 type when reading x509 public key: %x", + key_type); + } + + /* length is in bits */ + key_size = (key_size + 7) / 8; + + key_data = grub_malloc (key_size); + if (!key_data) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Out of memory for x509 public key"); + } + + result = asn1_read_value (asn, pk_name, key_data, &key_size); + if (result != ASN1_SUCCESS) + { + grub_free (key_data); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading public key data"); + } + key_size = (key_size + 7) / 8; + + err = grub_parse_rsa_pubkey (key_data, key_size, results); + grub_free (key_data); + + return err; +} + +/* Decode a string as defined in Appendix A */ +static grub_err_t +decode_string (char *der, int der_size, char **string, + grub_size_t * string_size) +{ + asn1_node strasn; + int result; + char *choice; + int choice_size = 0; + int tmp_size = 0; + grub_err_t err = GRUB_ERR_NONE; + + result = + asn1_create_element (_gnutls_pkix_asn, "PKIX1.DirectoryString", &strasn); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not create ASN.1 structure for certificate: %s", + asn1_strerror (result)); + } + + result = asn1_der_decoding2 (&strasn, der, &der_size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Could not parse DER for DirectoryString: %s", + asn1_error); + goto cleanup; + } + + choice = + grub_asn1_allocate_and_read (strasn, "", "DirectoryString choice", + &choice_size); + if (!choice) + { + err = grub_errno; + goto cleanup; + } + + if (grub_strncmp ("utf8String", choice, choice_size)) + { + err = + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Only UTF-8 DirectoryStrings are supported, got %s", + choice); + goto cleanup_choice; + } + + result = asn1_read_value (strasn, "utf8String", NULL, &tmp_size); + if (result != ASN1_MEM_ERROR) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading size of UTF-8 string: %s", + asn1_strerror (result)); + goto cleanup_choice; + } + + /* read size does not include trailing null */ + tmp_size++; + + *string = grub_malloc (tmp_size); + if (!*string) + { + err = + grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Cannot allocate memory for DirectoryString contents"); + goto cleanup_choice; + } + + result = asn1_read_value (strasn, "utf8String", *string, &tmp_size); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading out UTF-8 string in DirectoryString: %s", + asn1_strerror (result)); + grub_free (*string); + goto cleanup_choice; + } + *string_size = tmp_size + 1; + (*string)[tmp_size] = '\0'; + +cleanup_choice: + grub_free (choice); +cleanup: + asn1_delete_structure (&strasn); + return err; +} + +/* + * TBSCertificate ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * ... + * + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + */ +static grub_err_t +check_version (asn1_node certificate) +{ + int rc; + const char *name = "tbsCertificate.version"; + grub_uint8_t version; + int len = 1; + + rc = asn1_read_value (certificate, name, &version, &len); + + /* require version 3 */ + if (rc != ASN1_SUCCESS || len != 1) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading certificate version"); + + if (version != 0x02) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Invalid x509 certificate version, expected v3 (0x02), got 0x%02x", + version); + + return GRUB_ERR_NONE; +} + +/* + * This is an X.501 Name, which is complex. + * + * For simplicity, we extract only the CN. + */ +static grub_err_t +read_name (asn1_node asn, const char *name_path, char **name, + grub_size_t * name_size) +{ + int seq_components, set_components; + int result; + int i, j; + char *top_path, *set_path, *type_path, *val_path; + char type[MAX_OID_LEN]; + int type_len = sizeof (type); + int string_size = 0; + char *string_der; + grub_err_t err; + + *name = NULL; + + top_path = grub_xasprintf ("%s.rdnSequence", name_path); + if (!top_path) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not allocate memory for %s name parsing path", + name_path); + + result = asn1_number_of_elements (asn, top_path, &seq_components); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error counting name components: %s", + asn1_strerror (result)); + goto cleanup; + } + + for (i = 1; i <= seq_components; i++) + { + set_path = grub_xasprintf ("%s.?%d", top_path, i); + if (!set_path) + { + err = + grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not allocate memory for %s name set parsing path", + name_path); + goto cleanup_set; + } + /* this brings us, hopefully, to a set */ + result = asn1_number_of_elements (asn, set_path, &set_components); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error counting name sub-components components (element %d): %s", + i, asn1_strerror (result)); + goto cleanup_set; + } + for (j = 1; j <= set_components; j++) + { + type_path = grub_xasprintf ("%s.?%d.?%d.type", top_path, i, j); + if (!type_path) + { + err = + grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not allocate memory for %s name component type path", + name_path); + goto cleanup_set; + } + type_len = sizeof (type); + result = asn1_read_value (asn, type_path, type, &type_len); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading %s name component type: %s", + name_path, asn1_strerror (result)); + goto cleanup_type; + } + + if (grub_strncmp (type, commonName_oid, type_len) != 0) + { + grub_free (type_path); + continue; + } + + val_path = grub_xasprintf ("%s.?%d.?%d.value", top_path, i, j); + if (!val_path) + { + err = + grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not allocate memory for %s name component value path", + name_path); + goto cleanup_set; + } + + string_der = + grub_asn1_allocate_and_read (asn, val_path, name_path, + &string_size); + if (!string_der) + { + err = grub_errno; + goto cleanup_val_path; + } + + err = decode_string (string_der, string_size, name, name_size); + if (err) + goto cleanup_string; + + grub_free (string_der); + grub_free (type_path); + grub_free (val_path); + break; + } + grub_free (set_path); + + if (*name) + break; + } + + return GRUB_ERR_NONE; + +cleanup_string: + grub_free (string_der); +cleanup_val_path: + grub_free (val_path); +cleanup_type: + grub_free (type_path); +cleanup_set: + grub_free (set_path); +cleanup: + grub_free (top_path); + return err; +} + +/* + * details here + */ +static grub_err_t +verify_key_usage (grub_uint8_t * value, int value_size) +{ + asn1_node usageasn; + int result; + grub_err_t err = GRUB_ERR_NONE; + grub_uint8_t usage = 0xff; + int usage_size = 1; + + result = + asn1_create_element (_gnutls_pkix_asn, "PKIX1.KeyUsage", &usageasn); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not create ASN.1 structure for key usage"); + } + + result = asn1_der_decoding2 (&usageasn, value, &value_size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error parsing DER for Key Usage: %s", asn1_error); + goto cleanup; + } + + result = asn1_read_value (usageasn, "", &usage, &usage_size); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading Key Usage value: %s", + asn1_strerror (result)); + goto cleanup; + } + + /* Only the first bit is permitted to be set */ + if (usage != 0x80) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unexpected Key Usage value: %x", + usage); + goto cleanup; + } + +cleanup: + asn1_delete_structure (&usageasn); + return err; +} + +/* + * BasicConstraints ::= SEQUENCE { + * cA BOOLEAN DEFAULT FALSE, + * pathLenConstraint INTEGER (0..MAX) OPTIONAL } + */ +static grub_err_t +verify_basic_constraints (grub_uint8_t * value, int value_size) +{ + asn1_node basicasn; + int result; + grub_err_t err = GRUB_ERR_NONE; + char cA[6]; /* FALSE or TRUE */ + int cA_size = sizeof (cA); + + result = + asn1_create_element (_gnutls_pkix_asn, "PKIX1.BasicConstraints", + &basicasn); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not create ASN.1 structure for Basic Constraints"); + } + + result = asn1_der_decoding2 (&basicasn, value, &value_size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error parsing DER for Basic Constraints: %s", + asn1_error); + goto cleanup; + } + + result = asn1_read_value (basicasn, "cA", cA, &cA_size); + if (result == ASN1_ELEMENT_NOT_FOUND) + { + /* Not present, default is False, so this is OK */ + err = GRUB_ERR_NONE; + goto cleanup; + } + else if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading Basic Constraints cA value: %s", + asn1_strerror (result)); + goto cleanup; + } + + /* The certificate must not be a CA certificate */ + if (grub_strncmp ("FALSE", cA, cA_size) != 0) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unexpected CA value: %s", + cA); + goto cleanup; + } + +cleanup: + asn1_delete_structure (&basicasn); + return err; +} + + +/* + * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + * + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING + * -- contains the DER encoding of an ASN.1 value + * -- corresponding to the extension type identified + * -- by extnID + * } + * + * We require that a certificate: + * - contain the Digital Signature usage only + * - not be a CA + * - MUST not contain any other critical extensions (RFC 5280 s 4.2) + */ +static grub_err_t +verify_extensions (asn1_node cert) +{ + int result; + int ext, num_extensions = 0; + int usage_present = 0, constraints_present = 0; + char *oid_path, *critical_path, *value_path; + char extnID[MAX_OID_LEN]; + int extnID_size; + grub_err_t err; + char critical[6]; /* we get either "TRUE" or "FALSE" */ + int critical_size; + grub_uint8_t *value; + int value_size; + + result = + asn1_number_of_elements (cert, "tbsCertificate.extensions", + &num_extensions); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error counting number of extensions: %s", + asn1_strerror (result)); + } + + if (num_extensions < 2) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Insufficient number of extensions for certificate, need at least 2, got %d", + num_extensions); + } + + for (ext = 1; ext <= num_extensions; ext++) + { + oid_path = grub_xasprintf ("tbsCertificate.extensions.?%d.extnID", ext); + + extnID_size = sizeof (extnID); + result = asn1_read_value (cert, oid_path, extnID, &extnID_size); + if (result != GRUB_ERR_NONE) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading extension OID: %s", + asn1_strerror (result)); + goto cleanup_oid_path; + } + + critical_path = + grub_xasprintf ("tbsCertificate.extensions.?%d.critical", ext); + critical_size = sizeof (critical); + result = + asn1_read_value (cert, critical_path, critical, &critical_size); + if (result == ASN1_ELEMENT_NOT_FOUND) + { + critical[0] = '\0'; + } + else if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading extension criticality: %s", + asn1_strerror (result)); + goto cleanup_critical_path; + } + + value_path = + grub_xasprintf ("tbsCertificate.extensions.?%d.extnValue", ext); + value = + grub_asn1_allocate_and_read (cert, value_path, + "certificate extension value", + &value_size); + if (!value) + { + err = grub_errno; + goto cleanup_value_path; + } + + /* + * Now we must see if we recognise the OID. + * If we have an unrecognised critical extension we MUST bail. + */ + if (grub_strncmp (keyUsage_oid, extnID, extnID_size) == 0) + { + err = verify_key_usage (value, value_size); + if (err != GRUB_ERR_NONE) + { + goto cleanup_value; + } + usage_present++; + } + else if (grub_strncmp (basicConstraints_oid, extnID, extnID_size) == 0) + { + err = verify_basic_constraints (value, value_size); + if (err != GRUB_ERR_NONE) + { + goto cleanup_value; + } + constraints_present++; + } + else if (grub_strncmp ("TRUE", critical, critical_size) == 0) + { + /* + * per the RFC, we must not process a certificate with + * a critical extension we do not understand. + */ + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Unhandled critical x509 extension with OID %s", + extnID); + goto cleanup_value; + } + + grub_free (value); + grub_free (value_path); + grub_free (critical_path); + grub_free (oid_path); + } + + if (usage_present != 1) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Unexpected number of Key Usage extensions - expected 1, got %d", + usage_present); + } + if (constraints_present != 1) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Unexpected number of basic constraints extensions - expected 1, got %d", + constraints_present); + } + return GRUB_ERR_NONE; + +cleanup_value: + grub_free (value); +cleanup_value_path: + grub_free (value_path); +cleanup_critical_path: + grub_free (critical_path); +cleanup_oid_path: + grub_free (oid_path); + return err; +} + +/* + * Parse a certificate whose DER-encoded form is in @data, of size @data_size. + * Return the results in @results, which must point to an allocated x509 certificate. + */ +grub_err_t +certificate_import (void *data, grub_size_t data_size, + struct x509_certificate *results) +{ + int result = 0; + asn1_node cert; + grub_err_t err; + int size; + int tmp_size; + + if (data_size > GRUB_INT_MAX) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "Cannot parse a certificate where data size > INT_MAX"); + size = (int) data_size; + + result = asn1_create_element (_gnutls_pkix_asn, "PKIX1.Certificate", &cert); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not create ASN.1 structure for certificate: %s", + asn1_strerror (result)); + } + + result = asn1_der_decoding2 (&cert, data, &size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Could not parse DER for certificate: %s", asn1_error); + goto cleanup; + } + + /* + * TBSCertificate ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1 + */ + err = check_version (cert); + if (err != GRUB_ERR_NONE) + { + goto cleanup; + } + + /* + * serialNumber CertificateSerialNumber, + * + * CertificateSerialNumber ::= INTEGER + */ + results->serial = + grub_asn1_allocate_and_read (cert, "tbsCertificate.serialNumber", + "certificate serial number", &tmp_size); + if (!results->serial) + { + err = grub_errno; + goto cleanup; + } + /* + * It's safe to cast the signed int to an unsigned here, we know + * length is non-negative + */ + results->serial_len = tmp_size; + + /* + * signature AlgorithmIdentifier, + * + * We don't load the signature or issuer at the moment, + * as we don't attempt x509 verification. + */ + + /* + * issuer Name, + * + * The RFC only requires the serial number to be unique within + * issuers, so to avoid ambiguity we _technically_ ought to make + * this available. + */ + + /* + * validity Validity, + * + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time } + * + * We can't validate this reasonably, we have no true time source on several + * platforms. For now we do not parse them. + */ + + /* + * subject Name, + * + * This is an X501 name, we parse out just the CN. + */ + err = + read_name (cert, "tbsCertificate.subject", &results->subject, + &results->subject_len); + if (err != GRUB_ERR_NONE) + goto cleanup_serial; + + /* + * TBSCertificate ::= SEQUENCE { + * ... + * subjectPublicKeyInfo SubjectPublicKeyInfo, + * ... + */ + err = grub_x509_read_subject_public_key (cert, results); + if (err != GRUB_ERR_NONE) + goto cleanup_name; + + /* + * TBSCertificate ::= SEQUENCE { + * ... + * extensions [3] EXPLICIT Extensions OPTIONAL + * -- If present, version MUST be v3 + * } + */ + + err = verify_extensions (cert); + if (err != GRUB_ERR_NONE) + goto cleanup_name; + + + /* + * We do not read or check the signature on the certificate: + * as discussed we do not try to validate the certificate but trust + * it implictly. + */ + + asn1_delete_structure (&cert); + return GRUB_ERR_NONE; + + +cleanup_name: + grub_free (results->subject); +cleanup_serial: + grub_free (results->serial); +cleanup: + asn1_delete_structure (&cert); + return err; +} + +/* + * Release all the storage associated with the x509 certificate. + * If the caller dynamically allocated the certificate, it must free it. + * The caller is also responsible for maintenance of the linked list. + */ +void +certificate_release (struct x509_certificate *cert) +{ + grub_free (cert->subject); + grub_free (cert->serial); + gcry_mpi_release (cert->mpis[0]); + gcry_mpi_release (cert->mpis[1]); +} From 63dc47622df33d9e488df10dc674a8d82949136f Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Thu, 30 Jul 2020 01:35:43 +1000 Subject: [PATCH 170/367] appended signatures: support verifying appended signatures Building on the parsers and the ability to embed x509 certificates, as well as the existing gcrypt functionality, add a module for verifying appended signatures. This includes a verifier that requires that Linux kernels and grub modules have appended signatures, and commands to manage the list of trusted certificates for verification. Verification must be enabled by setting check_appended_signatures. If GRUB is locked down when the module is loaded, verification will be enabled and locked automatically. As with the PGP verifier, it is not a complete secure-boot solution: other mechanisms, such as a password or lockdown, must be used to ensure that a user cannot drop to the grub shell and disable verification. Signed-off-by: Daniel Axtens [pjones: fix missing format specifier] Signed-off-by: Robbie Harwood --- grub-core/Makefile.core.def | 12 + grub-core/commands/appendedsig/appendedsig.c | 645 +++++++++++++++++++ include/grub/file.h | 2 + 3 files changed, 659 insertions(+) create mode 100644 grub-core/commands/appendedsig/appendedsig.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index a32e6ada59..6404384d90 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -980,6 +980,18 @@ module = { cppflags = '-I$(srcdir)/lib/posix_wrap'; }; +module = { + name = appendedsig; + common = commands/appendedsig/appendedsig.c; + common = commands/appendedsig/x509.c; + common = commands/appendedsig/pkcs7.c; + common = commands/appendedsig/asn1util.c; + common = commands/appendedsig/gnutls_asn1_tab.c; + common = commands/appendedsig/pkix_asn1_tab.c; + cflags = '$(CFLAGS_POSIX)'; + cppflags = '-I$(srcdir)/lib/posix_wrap'; +}; + module = { name = hdparm; common = commands/hdparm.c; diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c new file mode 100644 index 0000000000..bf8b18b620 --- /dev/null +++ b/grub-core/commands/appendedsig/appendedsig.c @@ -0,0 +1,645 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020-2021 IBM Corporation. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "appendedsig.h" + +GRUB_MOD_LICENSE ("GPLv3+"); + +const char magic[] = "~Module signature appended~\n"; + +/* + * This structure is extracted from scripts/sign-file.c in the linux kernel + * source. It was licensed as LGPLv2.1+, which is GPLv3+ compatible. + */ +struct module_signature +{ + grub_uint8_t algo; /* Public-key crypto algorithm [0] */ + grub_uint8_t hash; /* Digest algorithm [0] */ + grub_uint8_t id_type; /* Key identifier type [PKEY_ID_PKCS7] */ + grub_uint8_t signer_len; /* Length of signer's name [0] */ + grub_uint8_t key_id_len; /* Length of key identifier [0] */ + grub_uint8_t __pad[3]; + grub_uint32_t sig_len; /* Length of signature data */ +} GRUB_PACKED; + + +/* This represents an entire, parsed, appended signature */ +struct grub_appended_signature +{ + grub_size_t signature_len; /* Length of PKCS#7 data + + * metadata + magic */ + + struct module_signature sig_metadata; /* Module signature metadata */ + struct pkcs7_signedData pkcs7; /* Parsed PKCS#7 data */ +}; + +/* Trusted certificates for verifying appended signatures */ +struct x509_certificate *grub_trusted_key; + +/* + * Force gcry_rsa to be a module dependency. + * + * If we use grub_crypto_pk_rsa, then then the gcry_rsa module won't be built + * in if you add 'appendedsig' to grub-install --modules. You would need to + * add 'gcry_rsa' too. That's confusing and seems suboptimal, especially when + * we only support RSA. + * + * Dynamic loading also causes some concerns. We can't load gcry_rsa from the + * the filesystem after we install the verifier - we won't be able to verify + * it without having it already present. We also shouldn't load it before we + * install the verifier, because that would mean it wouldn't be verified - an + * attacker could insert any code they wanted into the module. + * + * So instead, reference the internal symbol from gcry_rsa. That creates a + * direct dependency on gcry_rsa, so it will be built in when this module + * is built in. Being built in (assuming the core image is itself signed!) + * also resolves our concerns about loading from the filesystem. + */ +extern gcry_pk_spec_t _gcry_pubkey_spec_rsa; + +static int check_sigs = 0; + +static const char * +grub_env_read_sec (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + if (check_sigs == 2) + return "forced"; + else if (check_sigs == 1) + return "enforce"; + else + return "no"; +} + +static char * +grub_env_write_sec (struct grub_env_var *var __attribute__((unused)), + const char *val) +{ + /* Do not allow the value to be changed if set to forced */ + if (check_sigs == 2) + return grub_strdup ("forced"); + + if ((*val == '2') || (*val == 'f')) + check_sigs = 2; + else if ((*val == '1') || (*val == 'e')) + check_sigs = 1; + else if ((*val == '0') || (*val == 'n')) + check_sigs = 0; + + return grub_strdup (grub_env_read_sec (NULL, NULL)); +} + +static grub_err_t +read_cert_from_file (grub_file_t f, struct x509_certificate *certificate) +{ + grub_err_t err; + grub_uint8_t *buf = NULL; + grub_ssize_t read_size; + grub_off_t total_read_size = 0; + grub_off_t file_size = grub_file_size (f); + + + if (file_size == GRUB_FILE_SIZE_UNKNOWN) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("Cannot parse a certificate file of unknown size")); + + buf = grub_zalloc (file_size); + if (!buf) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("Could not allocate buffer for certificate file contents")); + + while (total_read_size < file_size) + { + read_size = + grub_file_read (f, &buf[total_read_size], + file_size - total_read_size); + if (read_size < 0) + { + err = grub_error (GRUB_ERR_READ_ERROR, + N_("Error reading certificate file")); + goto cleanup_buf; + } + total_read_size += read_size; + } + + err = certificate_import (buf, total_read_size, certificate); + if (err != GRUB_ERR_NONE) + goto cleanup_buf; + + return GRUB_ERR_NONE; + +cleanup_buf: + grub_free (buf); + return err; +} + +static grub_err_t +extract_appended_signature (grub_uint8_t * buf, grub_size_t bufsize, + struct grub_appended_signature *sig) +{ + grub_err_t err; + grub_size_t pkcs7_size; + grub_size_t remaining_len; + grub_uint8_t *appsigdata = buf + bufsize - grub_strlen (magic); + + if (bufsize < grub_strlen (magic)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, + N_("File too short for signature magic")); + + if (grub_memcmp (appsigdata, (grub_uint8_t *) magic, grub_strlen (magic))) + return grub_error (GRUB_ERR_BAD_SIGNATURE, + N_("Missing or invalid signature magic")); + + remaining_len = bufsize - grub_strlen (magic); + + if (remaining_len < sizeof (struct module_signature)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, + N_("File too short for signature metadata")); + + appsigdata -= sizeof (struct module_signature); + + /* extract the metadata */ + grub_memcpy (&(sig->sig_metadata), appsigdata, + sizeof (struct module_signature)); + + remaining_len -= sizeof (struct module_signature); + + if (sig->sig_metadata.id_type != 2) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("Wrong signature type")); + +#ifdef GRUB_TARGET_WORDS_BIGENDIAN + pkcs7_size = sig->sig_metadata.sig_len; +#else + pkcs7_size = __builtin_bswap32 (sig->sig_metadata.sig_len); +#endif + + if (pkcs7_size > remaining_len) + return grub_error (GRUB_ERR_BAD_SIGNATURE, + N_("File too short for PKCS#7 message")); + + grub_dprintf ("appendedsig", "sig len %" PRIuGRUB_SIZE "\n", pkcs7_size); + + sig->signature_len = + grub_strlen (magic) + sizeof (struct module_signature) + pkcs7_size; + + /* rewind pointer and parse pkcs7 data */ + appsigdata -= pkcs7_size; + + err = parse_pkcs7_signedData (appsigdata, pkcs7_size, &sig->pkcs7); + if (err != GRUB_ERR_NONE) + return err; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_verify_appended_signature (grub_uint8_t * buf, grub_size_t bufsize) +{ + grub_err_t err = GRUB_ERR_NONE; + grub_size_t datasize; + void *context; + unsigned char *hash; + gcry_mpi_t hashmpi; + gcry_err_code_t rc; + struct x509_certificate *pk; + struct grub_appended_signature sig; + + if (!grub_trusted_key) + return grub_error (GRUB_ERR_BAD_SIGNATURE, + N_("No trusted keys to verify against")); + + err = extract_appended_signature (buf, bufsize, &sig); + if (err != GRUB_ERR_NONE) + return err; + + datasize = bufsize - sig.signature_len; + + context = grub_zalloc (sig.pkcs7.hash->contextsize); + if (!context) + return grub_errno; + + sig.pkcs7.hash->init (context); + sig.pkcs7.hash->write (context, buf, datasize); + sig.pkcs7.hash->final (context); + hash = sig.pkcs7.hash->read (context); + grub_dprintf ("appendedsig", + "data size %" PRIxGRUB_SIZE ", hash %02x%02x%02x%02x...\n", + datasize, hash[0], hash[1], hash[2], hash[3]); + + err = GRUB_ERR_BAD_SIGNATURE; + for (pk = grub_trusted_key; pk; pk = pk->next) + { + rc = grub_crypto_rsa_pad (&hashmpi, hash, sig.pkcs7.hash, pk->mpis[0]); + if (rc) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, + N_("Error padding hash for RSA verification: %d"), + rc); + goto cleanup; + } + + rc = _gcry_pubkey_spec_rsa.verify (0, hashmpi, &sig.pkcs7.sig_mpi, + pk->mpis, NULL, NULL); + gcry_mpi_release (hashmpi); + + if (rc == 0) + { + grub_dprintf ("appendedsig", "verify with key '%s' succeeded\n", + pk->subject); + err = GRUB_ERR_NONE; + break; + } + + grub_dprintf ("appendedsig", "verify with key '%s' failed with %d\n", + pk->subject, rc); + } + + /* If we didn't verify, provide a neat message */ + if (err != GRUB_ERR_NONE) + err = grub_error (GRUB_ERR_BAD_SIGNATURE, + N_("Failed to verify signature against a trusted key")); + +cleanup: + grub_free (context); + pkcs7_signedData_release (&sig.pkcs7); + + return err; +} + +static grub_err_t +grub_cmd_verify_signature (grub_command_t cmd __attribute__((unused)), + int argc, char **args) +{ + grub_file_t f; + grub_err_t err = GRUB_ERR_NONE; + grub_uint8_t *data; + grub_ssize_t read_size; + grub_off_t file_size, total_read_size = 0; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + grub_dprintf ("appendedsig", "verifying %s\n", args[0]); + + f = grub_file_open (args[0], GRUB_FILE_TYPE_VERIFY_SIGNATURE); + if (!f) + { + err = grub_errno; + goto cleanup; + } + + file_size = grub_file_size (f); + if (file_size == GRUB_FILE_SIZE_UNKNOWN) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("Cannot verify the signature of a file of unknown size")); + + data = grub_malloc (file_size); + if (!data) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("Could not allocate data buffer size %" + PRIuGRUB_UINT64_T " for verification"), file_size); + + while (total_read_size < file_size) + { + read_size = + grub_file_read (f, &data[total_read_size], + file_size - total_read_size); + if (read_size < 0) + { + err = grub_error (GRUB_ERR_READ_ERROR, + N_("Error reading file to verify")); + goto cleanup_data; + } + total_read_size += read_size; + } + + err = grub_verify_appended_signature (data, file_size); + +cleanup_data: + grub_free (data); +cleanup: + if (f) + grub_file_close (f); + return err; +} + +static grub_err_t +grub_cmd_distrust (grub_command_t cmd __attribute__((unused)), + int argc, char **args) +{ + unsigned long cert_num, i; + struct x509_certificate *cert, *prev; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("One argument expected")); + + grub_errno = GRUB_ERR_NONE; + cert_num = grub_strtoul (args[0], NULL, 10); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + if (cert_num < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("Certificate number too small - numbers start at 1")); + + if (cert_num == 1) + { + cert = grub_trusted_key; + grub_trusted_key = cert->next; + + certificate_release (cert); + grub_free (cert); + return GRUB_ERR_NONE; + } + i = 2; + prev = grub_trusted_key; + cert = grub_trusted_key->next; + while (cert) + { + if (i == cert_num) + { + prev->next = cert->next; + certificate_release (cert); + grub_free (cert); + return GRUB_ERR_NONE; + } + i++; + prev = cert; + cert = cert->next; + } + + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("No certificate number %d found - only %d certificates in the store"), + cert_num, i - 1); +} + +static grub_err_t +grub_cmd_trust (grub_command_t cmd __attribute__((unused)), + int argc, char **args) +{ + grub_file_t certf; + struct x509_certificate *cert = NULL; + grub_err_t err; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + certf = grub_file_open (args[0], + GRUB_FILE_TYPE_CERTIFICATE_TRUST + | GRUB_FILE_TYPE_NO_DECOMPRESS); + if (!certf) + return grub_errno; + + + cert = grub_zalloc (sizeof (struct x509_certificate)); + if (!cert) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("Could not allocate memory for certificate")); + + err = read_cert_from_file (certf, cert); + grub_file_close (certf); + if (err != GRUB_ERR_NONE) + { + grub_free (cert); + return err; + } + grub_dprintf ("appendedsig", "Loaded certificate with CN: %s\n", + cert->subject); + + cert->next = grub_trusted_key; + grub_trusted_key = cert; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_list (grub_command_t cmd __attribute__((unused)), + int argc __attribute__((unused)), + char **args __attribute__((unused))) +{ + struct x509_certificate *cert; + int cert_num = 1; + grub_size_t i; + + for (cert = grub_trusted_key; cert; cert = cert->next) + { + grub_printf (N_("Certificate %d:\n"), cert_num); + + grub_printf (N_("\tSerial: ")); + for (i = 0; i < cert->serial_len - 1; i++) + { + grub_printf ("%02x:", cert->serial[i]); + } + grub_printf ("%02x\n", cert->serial[cert->serial_len - 1]); + + grub_printf ("\tCN: %s\n\n", cert->subject); + cert_num++; + + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +appendedsig_init (grub_file_t io __attribute__((unused)), + enum grub_file_type type, + void **context __attribute__((unused)), + enum grub_verify_flags *flags) +{ + if (!check_sigs) + { + *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + return GRUB_ERR_NONE; + } + + switch (type & GRUB_FILE_TYPE_MASK) + { + case GRUB_FILE_TYPE_CERTIFICATE_TRUST: + /* + * This is a certificate to add to trusted keychain. + * + * This needs to be verified or blocked. Ideally we'd write an x509 + * verifier, but we lack the hubris required to take this on. Instead, + * require that it have an appended signature. + */ + + /* Fall through */ + + case GRUB_FILE_TYPE_LINUX_KERNEL: + case GRUB_FILE_TYPE_GRUB_MODULE: + /* + * Appended signatures are only defined for ELF binaries. + * Out of an abundance of caution, we only verify Linux kernels and + * GRUB modules at this point. + */ + *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK; + return GRUB_ERR_NONE; + + case GRUB_FILE_TYPE_ACPI_TABLE: + case GRUB_FILE_TYPE_DEVICE_TREE_IMAGE: + /* + * It is possible to use appended signature verification without + * lockdown - like the PGP verifier. When combined with an embedded + * config file in a signed grub binary, this could still be a meaningful + * secure-boot chain - so long as it isn't subverted by something like a + * rouge ACPI table or DT image. Defer them explicitly. + */ + *flags = GRUB_VERIFY_FLAGS_DEFER_AUTH; + return GRUB_ERR_NONE; + + default: + *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + return GRUB_ERR_NONE; + } +} + +static grub_err_t +appendedsig_write (void *ctxt __attribute__((unused)), + void *buf, grub_size_t size) +{ + return grub_verify_appended_signature (buf, size); +} + +struct grub_file_verifier grub_appendedsig_verifier = { + .name = "appendedsig", + .init = appendedsig_init, + .write = appendedsig_write, +}; + +static grub_ssize_t +pseudo_read (struct grub_file *file, char *buf, grub_size_t len) +{ + grub_memcpy (buf, (grub_uint8_t *) file->data + file->offset, len); + return len; +} + +/* Filesystem descriptor. */ +static struct grub_fs pseudo_fs = { + .name = "pseudo", + .fs_read = pseudo_read +}; + +static grub_command_t cmd_verify, cmd_list, cmd_distrust, cmd_trust; + +GRUB_MOD_INIT (appendedsig) +{ + int rc; + struct grub_module_header *header; + + /* If in lockdown, immediately enter forced mode */ + if (grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED) + check_sigs = 2; + + grub_trusted_key = NULL; + + grub_register_variable_hook ("check_appended_signatures", + grub_env_read_sec, + grub_env_write_sec); + grub_env_export ("check_appended_signatures"); + + rc = asn1_init (); + if (rc) + grub_fatal ("Error initing ASN.1 data structures: %d: %s\n", rc, + asn1_strerror (rc)); + + FOR_MODULES (header) + { + struct grub_file pseudo_file; + struct x509_certificate *pk = NULL; + grub_err_t err; + + /* Not an ELF module, skip. */ + if (header->type != OBJ_TYPE_X509_PUBKEY) + continue; + + grub_memset (&pseudo_file, 0, sizeof (pseudo_file)); + pseudo_file.fs = &pseudo_fs; + pseudo_file.size = header->size - sizeof (struct grub_module_header); + pseudo_file.data = (char *) header + sizeof (struct grub_module_header); + + grub_dprintf ("appendedsig", + "Found an x509 key, size=%" PRIuGRUB_UINT64_T "\n", + pseudo_file.size); + + pk = grub_zalloc (sizeof (struct x509_certificate)); + if (!pk) + { + grub_fatal ("Out of memory loading initial certificates"); + } + + err = read_cert_from_file (&pseudo_file, pk); + if (err != GRUB_ERR_NONE) + grub_fatal ("Error loading initial key: %s", grub_errmsg); + + grub_dprintf ("appendedsig", "loaded certificate CN='%s'\n", pk->subject); + + pk->next = grub_trusted_key; + grub_trusted_key = pk; + } + + cmd_trust = + grub_register_command ("trust_certificate", grub_cmd_trust, + N_("X509_CERTIFICATE"), + N_("Add X509_CERTIFICATE to trusted certificates.")); + cmd_list = + grub_register_command ("list_certificates", grub_cmd_list, 0, + N_("Show the list of trusted x509 certificates.")); + cmd_verify = + grub_register_command ("verify_appended", grub_cmd_verify_signature, + N_("FILE"), + N_("Verify FILE against the trusted x509 certificates.")); + cmd_distrust = + grub_register_command ("distrust_certificate", grub_cmd_distrust, + N_("CERT_NUMBER"), + N_("Remove CERT_NUMBER (as listed by list_certificates) from trusted certificates.")); + + grub_verifier_register (&grub_appendedsig_verifier); + grub_dl_set_persistent (mod); +} + +GRUB_MOD_FINI (appendedsig) +{ + /* + * grub_dl_set_persistent should prevent this from actually running, but + * it does still run under emu. + */ + + grub_verifier_unregister (&grub_appendedsig_verifier); + grub_unregister_command (cmd_verify); + grub_unregister_command (cmd_list); + grub_unregister_command (cmd_trust); + grub_unregister_command (cmd_distrust); +} diff --git a/include/grub/file.h b/include/grub/file.h index 31567483cc..96827a4f89 100644 --- a/include/grub/file.h +++ b/include/grub/file.h @@ -80,6 +80,8 @@ enum grub_file_type GRUB_FILE_TYPE_PUBLIC_KEY, /* File holding public key to add to trused keys. */ GRUB_FILE_TYPE_PUBLIC_KEY_TRUST, + /* File holding x509 certificiate to add to trusted keys. */ + GRUB_FILE_TYPE_CERTIFICATE_TRUST, /* File of which we intend to print a blocklist to the user. */ GRUB_FILE_TYPE_PRINT_BLOCKLIST, /* File we intend to use for test loading or testing speed. */ From 63c6f0440b4901bc20702326564fd4ce7045f7c4 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Thu, 30 Jul 2020 01:31:02 +1000 Subject: [PATCH 171/367] appended signatures: verification tests These tests are run through all_functional_test and test a range of commands and behaviours. Signed-off-by: Daniel Axtens --- grub-core/Makefile.core.def | 6 + grub-core/tests/appended_signature_test.c | 281 +++++++++++ grub-core/tests/appended_signatures.h | 557 ++++++++++++++++++++++ grub-core/tests/lib/functional_test.c | 1 + 4 files changed, 845 insertions(+) create mode 100644 grub-core/tests/appended_signature_test.c create mode 100644 grub-core/tests/appended_signatures.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 6404384d90..9ea5fb38f1 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2162,6 +2162,12 @@ module = { common = tests/setjmp_test.c; }; +module = { + name = appended_signature_test; + common = tests/appended_signature_test.c; + common = tests/appended_signatures.h; +}; + module = { name = signature_test; common = tests/signature_test.c; diff --git a/grub-core/tests/appended_signature_test.c b/grub-core/tests/appended_signature_test.c new file mode 100644 index 0000000000..88a485200d --- /dev/null +++ b/grub-core/tests/appended_signature_test.c @@ -0,0 +1,281 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 IBM Corporation. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "appended_signatures.h" + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define DEFINE_TEST_CASE(case_name) \ +static char * \ +get_ ## case_name (grub_size_t *sz) \ +{ \ + char *ret; \ + *sz = case_name ## _len; \ + ret = grub_malloc (*sz); \ + if (ret) \ + grub_memcpy (ret, case_name, *sz); \ + return ret; \ +} \ +\ +static struct grub_procfs_entry case_name ## _entry = \ +{ \ + .name = #case_name, \ + .get_contents = get_ ## case_name \ +} + +#define DO_TEST(case_name, is_valid) \ +{ \ + grub_procfs_register (#case_name, &case_name ## _entry); \ + do_verify ("(proc)/" #case_name, is_valid); \ + grub_procfs_unregister (&case_name ## _entry); \ +} + + +DEFINE_TEST_CASE (hi_signed); +DEFINE_TEST_CASE (hi_signed_sha256); +DEFINE_TEST_CASE (hj_signed); +DEFINE_TEST_CASE (short_msg); +DEFINE_TEST_CASE (unsigned_msg); +DEFINE_TEST_CASE (hi_signed_2nd); + +static char * +get_certificate_der (grub_size_t * sz) +{ + char *ret; + *sz = certificate_der_len; + ret = grub_malloc (*sz); + if (ret) + grub_memcpy (ret, certificate_der, *sz); + return ret; +} + +static struct grub_procfs_entry certificate_der_entry = { + .name = "certificate.der", + .get_contents = get_certificate_der +}; + +static char * +get_certificate2_der (grub_size_t * sz) +{ + char *ret; + *sz = certificate2_der_len; + ret = grub_malloc (*sz); + if (ret) + grub_memcpy (ret, certificate2_der, *sz); + return ret; +} + +static struct grub_procfs_entry certificate2_der_entry = { + .name = "certificate2.der", + .get_contents = get_certificate2_der +}; + +static char * +get_certificate_printable_der (grub_size_t * sz) +{ + char *ret; + *sz = certificate_printable_der_len; + ret = grub_malloc (*sz); + if (ret) + grub_memcpy (ret, certificate_printable_der, *sz); + return ret; +} + +static struct grub_procfs_entry certificate_printable_der_entry = { + .name = "certificate_printable.der", + .get_contents = get_certificate_printable_der +}; + + +static void +do_verify (const char *f, int is_valid) +{ + grub_command_t cmd; + char *args[] = { (char *) f, NULL }; + grub_err_t err; + + cmd = grub_command_find ("verify_appended"); + if (!cmd) + { + grub_test_assert (0, "can't find command `%s'", "verify_appended"); + return; + } + err = (cmd->func) (cmd, 1, args); + if (is_valid) + { + grub_test_assert (err == GRUB_ERR_NONE, + "verification of %s failed: %d: %s", f, grub_errno, + grub_errmsg); + } + else + { + grub_test_assert (err == GRUB_ERR_BAD_SIGNATURE, + "verification of %s unexpectedly succeeded", f); + } + grub_errno = GRUB_ERR_NONE; + +} + +static void +appended_signature_test (void) +{ + grub_command_t cmd_trust, cmd_distrust; + char *trust_args[] = { (char *) "(proc)/certificate.der", NULL }; + char *trust_args2[] = { (char *) "(proc)/certificate2.der", NULL }; + char *trust_args_printable[] = { (char *) "(proc)/certificate_printable.der", + NULL }; + char *distrust_args[] = { (char *) "1", NULL }; + char *distrust2_args[] = { (char *) "2", NULL }; + grub_err_t err; + + grub_procfs_register ("certificate.der", &certificate_der_entry); + grub_procfs_register ("certificate2.der", &certificate2_der_entry); + grub_procfs_register ("certificate_printable.der", + &certificate_printable_der_entry); + + cmd_trust = grub_command_find ("trust_certificate"); + if (!cmd_trust) + { + grub_test_assert (0, "can't find command `%s'", "trust_certificate"); + return; + } + err = (cmd_trust->func) (cmd_trust, 1, trust_args); + + grub_test_assert (err == GRUB_ERR_NONE, + "loading certificate failed: %d: %s", grub_errno, + grub_errmsg); + + /* If we have no certificate the remainder of the tests are meaningless */ + if (err != GRUB_ERR_NONE) + return; + + /* + * Reload the command: this works around some 'interesting' behaviour in the + * dynamic command dispatcher. The first time you call cmd->func you get a + * dispatcher that loads the module, finds the real cmd, calls it, and then + * releases some internal storage. This means it's not safe to call a second + * time and we need to reload it. + */ + cmd_trust = grub_command_find ("trust_certificate"); + + DO_TEST (hi_signed, 1); + DO_TEST (hi_signed_sha256, 1); + DO_TEST (hj_signed, 0); + DO_TEST (short_msg, 0); + DO_TEST (unsigned_msg, 0); + + /* + * in enforcing mode, we shouldn't be able to load a certificate that isn't + * signed by an existing trusted key. + * + * However, procfs files automatically skip the verification test, so we can't + * easily test this. + */ + + /* + * verify that testing with 2 trusted certs works + */ + DO_TEST (hi_signed_2nd, 0); + + err = (cmd_trust->func) (cmd_trust, 1, trust_args2); + + grub_test_assert (err == GRUB_ERR_NONE, + "loading certificate 2 failed: %d: %s", grub_errno, + grub_errmsg); + + if (err != GRUB_ERR_NONE) + return; + + DO_TEST (hi_signed_2nd, 1); + DO_TEST (hi_signed, 1); + + /* + * Check certificate removal. They're added to the _top_ of the list and + * removed by position in the list. Current the list looks like [#2, #1]. + * + * First test removing the second certificate in the list, which is + * certificate #1, giving us just [#2]. + */ + cmd_distrust = grub_command_find ("distrust_certificate"); + if (!cmd_distrust) + { + grub_test_assert (0, "can't find command `%s'", "distrust_certificate"); + return; + } + + err = (cmd_distrust->func) (cmd_distrust, 1, distrust2_args); + grub_test_assert (err == GRUB_ERR_NONE, + "distrusting certificate 1 failed: %d: %s", grub_errno, + grub_errmsg); + DO_TEST (hi_signed_2nd, 1); + DO_TEST (hi_signed, 0); + + /* + * Now reload certificate #1. This will make the list look like [#1, #2] + */ + err = (cmd_trust->func) (cmd_trust, 1, trust_args); + + grub_test_assert (err == GRUB_ERR_NONE, + "reloading certificate 1 failed: %d: %s", grub_errno, + grub_errmsg); + DO_TEST (hi_signed, 1); + + /* Remove the first certificate in the list, giving us just [#2] */ + err = (cmd_distrust->func) (cmd_distrust, 1, distrust_args); + grub_test_assert (err == GRUB_ERR_NONE, + "distrusting certificate 1 (first time) failed: %d: %s", + grub_errno, grub_errmsg); + DO_TEST (hi_signed_2nd, 1); + DO_TEST (hi_signed, 0); + + /* + * Remove the first certificate again, giving an empty list. + * + * verify_appended should fail if there are no certificates to verify against. + */ + err = (cmd_distrust->func) (cmd_distrust, 1, distrust_args); + grub_test_assert (err == GRUB_ERR_NONE, + "distrusting certificate 1 (second time) failed: %d: %s", + grub_errno, grub_errmsg); + DO_TEST (hi_signed_2nd, 0); + + /* + * Lastly, check a certificate that uses printableString rather than + * utf8String loads properly. + */ + err = (cmd_trust->func) (cmd_trust, 1, trust_args_printable); + grub_test_assert (err == GRUB_ERR_NONE, + "distrusting printable certificate failed: %d: %s", + grub_errno, grub_errmsg); + + grub_procfs_unregister (&certificate_der_entry); + grub_procfs_unregister (&certificate2_der_entry); + grub_procfs_unregister (&certificate_printable_der_entry); +} + +GRUB_FUNCTIONAL_TEST (appended_signature_test, appended_signature_test); diff --git a/grub-core/tests/appended_signatures.h b/grub-core/tests/appended_signatures.h new file mode 100644 index 0000000000..aa3dc6278e --- /dev/null +++ b/grub-core/tests/appended_signatures.h @@ -0,0 +1,557 @@ +unsigned char certificate_der[] = { + 0x30, 0x82, 0x03, 0x88, 0x30, 0x82, 0x02, 0x70, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x14, 0x25, 0x2e, 0xb8, 0xfd, 0x12, 0x62, 0x2e, 0xcd, 0x5d, + 0xa7, 0x53, 0xd2, 0x0b, 0xc2, 0x61, 0x7c, 0x14, 0xe0, 0x0f, 0x5c, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x30, 0x49, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0c, 0x1f, 0x47, 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, + 0x6e, 0x64, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1d, + 0x30, 0x1b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, + 0x01, 0x16, 0x0e, 0x64, 0x6a, 0x61, 0x40, 0x61, 0x78, 0x74, 0x65, 0x6e, + 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x30, 0x30, + 0x37, 0x30, 0x39, 0x30, 0x36, 0x32, 0x32, 0x30, 0x37, 0x5a, 0x18, 0x0f, + 0x32, 0x31, 0x32, 0x30, 0x30, 0x36, 0x31, 0x35, 0x30, 0x36, 0x32, 0x32, + 0x30, 0x37, 0x5a, 0x30, 0x52, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0c, 0x28, 0x47, 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, + 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x69, 0x67, + 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x65, 0x79, 0x31, 0x1d, 0x30, 0x1b, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, + 0x0e, 0x64, 0x6a, 0x61, 0x40, 0x61, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x2e, + 0x6e, 0x65, 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xcd, 0xe8, 0x1c, 0x08, 0x68, 0x2e, 0xcb, 0xfe, 0x8c, 0x4b, 0x3b, 0x61, + 0xe7, 0x8e, 0x80, 0x58, 0x85, 0x85, 0xea, 0xc8, 0x3b, 0x42, 0xba, 0x72, + 0x84, 0x65, 0x20, 0xbc, 0x48, 0xa2, 0x25, 0x49, 0x6e, 0x1c, 0xb9, 0x7d, + 0xeb, 0xc1, 0x0c, 0xa8, 0xb7, 0xcc, 0x13, 0x78, 0xba, 0x11, 0xa4, 0x98, + 0xd7, 0xd0, 0x7c, 0xdd, 0xf5, 0x5a, 0xb7, 0xcd, 0x31, 0x0e, 0xcd, 0x9e, + 0xa7, 0x19, 0xf0, 0xbd, 0x0f, 0xa6, 0xfe, 0x8a, 0x11, 0x97, 0xed, 0x8b, + 0xe5, 0x16, 0xa6, 0x21, 0x13, 0x36, 0xad, 0x05, 0x49, 0xec, 0x29, 0x12, + 0x38, 0xa7, 0x4b, 0x0f, 0xa1, 0xfb, 0x72, 0xc0, 0xc0, 0x09, 0x67, 0x78, + 0xa8, 0xb6, 0xd6, 0x1a, 0x39, 0xc0, 0xa8, 0xbf, 0x5f, 0x14, 0x89, 0x5c, + 0xbc, 0x41, 0x0c, 0x0c, 0x5d, 0x42, 0x2e, 0x1c, 0xdf, 0x1f, 0x1d, 0xc9, + 0x43, 0x94, 0x5b, 0x6e, 0x8f, 0x15, 0x8c, 0x8f, 0x94, 0x73, 0x4f, 0x97, + 0x54, 0xf1, 0x86, 0x8a, 0xbc, 0xe4, 0xe4, 0x93, 0xc1, 0x5e, 0xc2, 0x3e, + 0x31, 0x5e, 0xd4, 0x85, 0x57, 0x14, 0xd0, 0x11, 0x07, 0x65, 0xf4, 0x7c, + 0x8f, 0x07, 0x57, 0xe1, 0x22, 0xd4, 0x78, 0x47, 0x65, 0x4e, 0xa9, 0xb3, + 0xaa, 0xce, 0xc7, 0x36, 0xfe, 0xda, 0x66, 0x02, 0xb6, 0x8d, 0x18, 0x2f, + 0x3b, 0x41, 0x8d, 0x02, 0x08, 0x72, 0x4b, 0x69, 0xbd, 0x1e, 0x58, 0xfc, + 0x1b, 0x64, 0x04, 0x52, 0x35, 0x35, 0xe2, 0x3d, 0x3e, 0xde, 0xd6, 0x64, + 0xf4, 0xec, 0x57, 0x7e, 0x65, 0x59, 0x00, 0xa6, 0xd3, 0x4b, 0x09, 0x93, + 0x2a, 0x95, 0x0f, 0x30, 0xb6, 0xa1, 0x8c, 0xe7, 0x8b, 0x49, 0xa4, 0x1d, + 0x25, 0x2d, 0x65, 0x48, 0x8a, 0x0f, 0xcf, 0x2a, 0xa2, 0xe1, 0xef, 0x72, + 0x92, 0xc3, 0xf5, 0x21, 0x37, 0x83, 0x9b, 0x6d, 0x0b, 0x1b, 0xb3, 0xa2, + 0x32, 0x38, 0x11, 0xb1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x5d, 0x30, + 0x5b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, + 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, + 0x03, 0x02, 0x07, 0x80, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, + 0x16, 0x04, 0x14, 0xe5, 0x2a, 0x4f, 0xf2, 0x84, 0x91, 0x57, 0x91, 0xaf, + 0x12, 0xd2, 0xf1, 0xa1, 0x87, 0x73, 0x0f, 0x90, 0x25, 0xa0, 0x7a, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0x56, 0xd1, 0xfd, 0xe2, 0x1e, 0x7e, 0x1c, 0x63, 0x4f, 0x47, 0xdb, 0xe4, + 0xc4, 0x51, 0x04, 0x03, 0x9a, 0x48, 0x35, 0x6e, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x01, 0x00, 0x65, 0x82, 0xd5, 0x88, 0x30, 0xe2, 0x2c, 0x47, + 0xf3, 0x31, 0x39, 0xa1, 0x75, 0x9a, 0xb0, 0x8a, 0x6c, 0x4b, 0xac, 0xdf, + 0x09, 0x7b, 0x90, 0xb6, 0x9e, 0x76, 0x62, 0x94, 0xc1, 0x3a, 0x99, 0x49, + 0x68, 0x29, 0x47, 0x42, 0xc3, 0x06, 0xcb, 0x88, 0x75, 0xe6, 0x79, 0x13, + 0x8c, 0x4b, 0x49, 0x6a, 0xb5, 0x56, 0x95, 0xc0, 0x42, 0x21, 0x9b, 0xd4, + 0x61, 0xd0, 0x02, 0x41, 0xdd, 0x20, 0x61, 0xe5, 0x91, 0xdf, 0x75, 0x00, + 0x25, 0x0e, 0x99, 0x65, 0x5c, 0x54, 0x49, 0x32, 0xa3, 0xe2, 0xcd, 0xa1, + 0x5f, 0x40, 0xf3, 0xc5, 0x81, 0xd9, 0x3c, 0xa3, 0x63, 0x5a, 0x38, 0x79, + 0xab, 0x77, 0x98, 0xde, 0x8f, 0x4e, 0x9e, 0x26, 0xbc, 0x4e, 0x80, 0x9e, + 0x8f, 0xbe, 0xf1, 0x00, 0xb3, 0x78, 0xb9, 0x4b, 0x1d, 0xc7, 0xa4, 0x83, + 0x59, 0x56, 0x11, 0xd1, 0x11, 0x1e, 0x50, 0x39, 0xd5, 0x78, 0x14, 0xf3, + 0xb9, 0x1d, 0xda, 0xe4, 0xc4, 0x63, 0x74, 0x26, 0xab, 0xa3, 0xfd, 0x9d, + 0x58, 0xa2, 0xee, 0x7b, 0x28, 0x34, 0xa3, 0xbe, 0x85, 0x7e, 0xaa, 0x97, + 0xb7, 0x5b, 0x9d, 0xa9, 0x4d, 0x96, 0xdb, 0x6b, 0x21, 0xe1, 0x96, 0x5d, + 0xc7, 0xad, 0x23, 0x03, 0x9a, 0x16, 0xdb, 0xa4, 0x1f, 0x63, 0xef, 0xaf, + 0x1e, 0x4f, 0xf8, 0x27, 0xdc, 0x4b, 0xfc, 0x2b, 0x68, 0x2e, 0xa0, 0xd3, + 0xae, 0xf2, 0xce, 0xf5, 0xfc, 0x97, 0x92, 0xd2, 0x29, 0x0f, 0x4f, 0x4b, + 0x29, 0xeb, 0x06, 0xcb, 0xf8, 0x21, 0x6e, 0xbc, 0x8b, 0x5c, 0xc5, 0xc9, + 0xf7, 0xe2, 0x7c, 0x47, 0xcd, 0x43, 0x98, 0xc4, 0xa3, 0x9a, 0xd7, 0x3e, + 0xdc, 0x01, 0x13, 0x28, 0x96, 0xc4, 0x60, 0x83, 0xe2, 0x79, 0xa1, 0x46, + 0xef, 0xf5, 0xa4, 0x7b, 0x00, 0xe3, 0x3d, 0x7d, 0xbc, 0xa8, 0x98, 0x49, + 0xa8, 0xcf, 0x3b, 0x41, 0xb6, 0x09, 0x97, 0x07 +}; +unsigned int certificate_der_len = 908; + +unsigned char hi_signed[] = { + 0x68, 0x69, 0x0a, 0x30, 0x82, 0x01, 0xc0, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x01, 0xb1, 0x30, 0x82, + 0x01, 0xad, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x01, + 0x8a, 0x30, 0x82, 0x01, 0x86, 0x02, 0x01, 0x01, 0x30, 0x61, 0x30, 0x49, + 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1f, 0x47, + 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, + 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, + 0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0e, 0x64, + 0x6a, 0x61, 0x40, 0x61, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x2e, 0x6e, 0x65, + 0x74, 0x02, 0x14, 0x25, 0x2e, 0xb8, 0xfd, 0x12, 0x62, 0x2e, 0xcd, 0x5d, + 0xa7, 0x53, 0xd2, 0x0b, 0xc2, 0x61, 0x7c, 0x14, 0xe0, 0x0f, 0x5c, 0x30, + 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x00, 0xc7, 0x69, 0x35, 0x21, 0x66, + 0x4d, 0x50, 0xd4, 0x73, 0xde, 0xbd, 0x3a, 0xf6, 0x45, 0xe3, 0xe4, 0xd0, + 0xb6, 0xa1, 0xe7, 0xc0, 0xa2, 0xc9, 0xf4, 0xf0, 0x05, 0x8c, 0xa4, 0x16, + 0x9e, 0x81, 0x0d, 0x21, 0x68, 0xf3, 0xfe, 0x03, 0x96, 0x77, 0x31, 0x69, + 0x01, 0xd8, 0x26, 0xd9, 0x48, 0x95, 0xcf, 0xd1, 0x17, 0xb1, 0x0b, 0x6b, + 0x2c, 0xf1, 0xb0, 0xab, 0x65, 0x65, 0x56, 0xf8, 0x0c, 0xa7, 0xf7, 0xbb, + 0xf6, 0x5a, 0x55, 0x98, 0x14, 0x07, 0x8d, 0x2a, 0xbc, 0x16, 0x48, 0x94, + 0xab, 0x2f, 0x85, 0x97, 0x90, 0x51, 0x78, 0xa0, 0xda, 0x60, 0xb5, 0x41, + 0x4b, 0xe8, 0x78, 0xc5, 0xa6, 0x04, 0x9d, 0x54, 0x2a, 0x85, 0xfd, 0x86, + 0x0b, 0x6d, 0xc2, 0xd2, 0xad, 0x07, 0xff, 0x16, 0x42, 0x82, 0xe3, 0x5c, + 0xaa, 0x22, 0x59, 0x78, 0x92, 0xea, 0x94, 0xc3, 0x41, 0xb7, 0xa1, 0x86, + 0x44, 0xea, 0xd1, 0xdb, 0xe5, 0xac, 0x30, 0x32, 0xfb, 0x7d, 0x3f, 0xf7, + 0x8b, 0x11, 0x7f, 0x80, 0x3b, 0xe5, 0xc7, 0x82, 0x0f, 0x92, 0x07, 0x14, + 0x66, 0x01, 0x6e, 0x85, 0xab, 0x3a, 0x14, 0xcf, 0x76, 0xd1, 0x7e, 0x14, + 0x85, 0xca, 0x01, 0x73, 0x72, 0x38, 0xdc, 0xde, 0x30, 0x5c, 0xfb, 0xc0, + 0x3d, 0x93, 0xef, 0x9c, 0xbc, 0xf8, 0xcc, 0xd2, 0xbf, 0x47, 0xec, 0xf8, + 0x88, 0x9b, 0xe1, 0x43, 0xbe, 0xa7, 0x47, 0x96, 0xb6, 0x5d, 0x46, 0x0e, + 0x7a, 0x78, 0x38, 0x19, 0xbc, 0xb5, 0xbc, 0x9b, 0x3c, 0x39, 0x92, 0x70, + 0x0d, 0x9d, 0x8a, 0x35, 0xaf, 0xb4, 0x9e, 0xf4, 0xef, 0xc1, 0xb8, 0x25, + 0xd0, 0x14, 0x91, 0xd6, 0xc2, 0xb6, 0xc7, 0x3c, 0x72, 0x91, 0x0f, 0xad, + 0xde, 0xb2, 0x36, 0xf8, 0x4e, 0x59, 0xd4, 0xa4, 0x21, 0x9f, 0x03, 0x95, + 0x48, 0x01, 0xb4, 0x05, 0xc3, 0x39, 0x60, 0x51, 0x08, 0xd0, 0xbe, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc4, 0x7e, + 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, + 0x64, 0x7e, 0x0a +}; +unsigned int hi_signed_len = 495; + +unsigned char hj_signed[] = { + 0x68, 0x6a, 0x0a, 0x30, 0x82, 0x01, 0xc0, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x01, 0xb1, 0x30, 0x82, + 0x01, 0xad, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x01, + 0x8a, 0x30, 0x82, 0x01, 0x86, 0x02, 0x01, 0x01, 0x30, 0x61, 0x30, 0x49, + 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1f, 0x47, + 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, + 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, + 0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0e, 0x64, + 0x6a, 0x61, 0x40, 0x61, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x2e, 0x6e, 0x65, + 0x74, 0x02, 0x14, 0x25, 0x2e, 0xb8, 0xfd, 0x12, 0x62, 0x2e, 0xcd, 0x5d, + 0xa7, 0x53, 0xd2, 0x0b, 0xc2, 0x61, 0x7c, 0x14, 0xe0, 0x0f, 0x5c, 0x30, + 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x00, 0xc7, 0x69, 0x35, 0x21, 0x66, + 0x4d, 0x50, 0xd4, 0x73, 0xde, 0xbd, 0x3a, 0xf6, 0x45, 0xe3, 0xe4, 0xd0, + 0xb6, 0xa1, 0xe7, 0xc0, 0xa2, 0xc9, 0xf4, 0xf0, 0x05, 0x8c, 0xa4, 0x16, + 0x9e, 0x81, 0x0d, 0x21, 0x68, 0xf3, 0xfe, 0x03, 0x96, 0x77, 0x31, 0x69, + 0x01, 0xd8, 0x26, 0xd9, 0x48, 0x95, 0xcf, 0xd1, 0x17, 0xb1, 0x0b, 0x6b, + 0x2c, 0xf1, 0xb0, 0xab, 0x65, 0x65, 0x56, 0xf8, 0x0c, 0xa7, 0xf7, 0xbb, + 0xf6, 0x5a, 0x55, 0x98, 0x14, 0x07, 0x8d, 0x2a, 0xbc, 0x16, 0x48, 0x94, + 0xab, 0x2f, 0x85, 0x97, 0x90, 0x51, 0x78, 0xa0, 0xda, 0x60, 0xb5, 0x41, + 0x4b, 0xe8, 0x78, 0xc5, 0xa6, 0x04, 0x9d, 0x54, 0x2a, 0x85, 0xfd, 0x86, + 0x0b, 0x6d, 0xc2, 0xd2, 0xad, 0x07, 0xff, 0x16, 0x42, 0x82, 0xe3, 0x5c, + 0xaa, 0x22, 0x59, 0x78, 0x92, 0xea, 0x94, 0xc3, 0x41, 0xb7, 0xa1, 0x86, + 0x44, 0xea, 0xd1, 0xdb, 0xe5, 0xac, 0x30, 0x32, 0xfb, 0x7d, 0x3f, 0xf7, + 0x8b, 0x11, 0x7f, 0x80, 0x3b, 0xe5, 0xc7, 0x82, 0x0f, 0x92, 0x07, 0x14, + 0x66, 0x01, 0x6e, 0x85, 0xab, 0x3a, 0x14, 0xcf, 0x76, 0xd1, 0x7e, 0x14, + 0x85, 0xca, 0x01, 0x73, 0x72, 0x38, 0xdc, 0xde, 0x30, 0x5c, 0xfb, 0xc0, + 0x3d, 0x93, 0xef, 0x9c, 0xbc, 0xf8, 0xcc, 0xd2, 0xbf, 0x47, 0xec, 0xf8, + 0x88, 0x9b, 0xe1, 0x43, 0xbe, 0xa7, 0x47, 0x96, 0xb6, 0x5d, 0x46, 0x0e, + 0x7a, 0x78, 0x38, 0x19, 0xbc, 0xb5, 0xbc, 0x9b, 0x3c, 0x39, 0x92, 0x70, + 0x0d, 0x9d, 0x8a, 0x35, 0xaf, 0xb4, 0x9e, 0xf4, 0xef, 0xc1, 0xb8, 0x25, + 0xd0, 0x14, 0x91, 0xd6, 0xc2, 0xb6, 0xc7, 0x3c, 0x72, 0x91, 0x0f, 0xad, + 0xde, 0xb2, 0x36, 0xf8, 0x4e, 0x59, 0xd4, 0xa4, 0x21, 0x9f, 0x03, 0x95, + 0x48, 0x01, 0xb4, 0x05, 0xc3, 0x39, 0x60, 0x51, 0x08, 0xd0, 0xbe, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc4, 0x7e, + 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, + 0x64, 0x7e, 0x0a +}; +unsigned int hj_signed_len = 495; + +unsigned char hi_signed_sha256[] = { + 0x68, 0x69, 0x0a, 0x30, 0x82, 0x01, 0xc0, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x01, 0xb1, 0x30, 0x82, + 0x01, 0xad, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x0b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x01, + 0x8a, 0x30, 0x82, 0x01, 0x86, 0x02, 0x01, 0x01, 0x30, 0x61, 0x30, 0x49, + 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1f, 0x47, + 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, + 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, + 0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0e, 0x64, + 0x6a, 0x61, 0x40, 0x61, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x2e, 0x6e, 0x65, + 0x74, 0x02, 0x14, 0x25, 0x2e, 0xb8, 0xfd, 0x12, 0x62, 0x2e, 0xcd, 0x5d, + 0xa7, 0x53, 0xd2, 0x0b, 0xc2, 0x61, 0x7c, 0x14, 0xe0, 0x0f, 0x5c, 0x30, + 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x00, 0x7b, 0x5e, 0x82, 0x1d, 0x21, + 0xb6, 0x40, 0xd3, 0x33, 0x79, 0xa7, 0x52, 0x2b, 0xfc, 0x46, 0x51, 0x26, + 0xfe, 0x0f, 0x81, 0x90, 0x81, 0xab, 0x57, 0x5e, 0xf6, 0x45, 0x41, 0xa3, + 0x7b, 0x48, 0xdd, 0xd6, 0x59, 0x60, 0x51, 0x31, 0x14, 0x14, 0x7b, 0xb4, + 0x55, 0x7b, 0x4d, 0xfe, 0x09, 0x7a, 0x5d, 0xae, 0xc4, 0x58, 0x50, 0x80, + 0x75, 0xf2, 0x23, 0x20, 0x62, 0xe3, 0x7c, 0x26, 0x1d, 0x2a, 0x4d, 0x9f, + 0x89, 0xf0, 0x4f, 0x95, 0x8a, 0x80, 0x6e, 0x1a, 0xea, 0x87, 0xdb, 0x1f, + 0xf3, 0xda, 0x04, 0x91, 0x37, 0xea, 0x0a, 0xfb, 0x6c, 0xc9, 0x3d, 0x73, + 0xf9, 0x58, 0x7c, 0x15, 0x6b, 0xa2, 0x52, 0x5a, 0x97, 0xff, 0xd6, 0xb0, + 0xf1, 0xbf, 0xa5, 0x04, 0x6d, 0x91, 0xc1, 0x54, 0x05, 0xdc, 0x7f, 0x5d, + 0x19, 0xaf, 0x55, 0xec, 0x51, 0xfb, 0x66, 0x0a, 0xa4, 0x4e, 0x96, 0x47, + 0x43, 0x54, 0x7c, 0x64, 0xa8, 0xaa, 0xb4, 0x90, 0x02, 0xf3, 0xa7, 0x0b, + 0xb7, 0xbf, 0x06, 0xdb, 0x5e, 0x9c, 0x32, 0x6d, 0x45, 0x14, 0x1c, 0xaf, + 0x46, 0x30, 0x08, 0x55, 0x49, 0x78, 0xfa, 0x57, 0xda, 0x3d, 0xf5, 0xa0, + 0xef, 0x11, 0x0a, 0x81, 0x0d, 0x82, 0xcd, 0xaf, 0xdb, 0xda, 0x0e, 0x1a, + 0x44, 0xd1, 0xee, 0xc4, 0xb8, 0xde, 0x97, 0xb4, 0xda, 0xb4, 0x8b, 0x4f, + 0x58, 0x24, 0x59, 0xc0, 0xe0, 0x08, 0x97, 0x14, 0x68, 0xbe, 0x31, 0x09, + 0x5e, 0x67, 0x45, 0xf0, 0xcb, 0x81, 0x4f, 0x17, 0x44, 0x61, 0xe0, 0xe2, + 0xf0, 0xfc, 0x1e, 0xb9, 0x73, 0xaf, 0x42, 0xff, 0x33, 0xde, 0x61, 0x6b, + 0x7f, 0xc2, 0x69, 0x0d, 0x66, 0x54, 0xae, 0xf6, 0xde, 0x20, 0x47, 0x44, + 0x9b, 0x73, 0xd1, 0x07, 0x6e, 0x77, 0x37, 0x0a, 0xbb, 0x7f, 0xa0, 0x93, + 0x2d, 0x8d, 0x44, 0xba, 0xe2, 0xdd, 0x34, 0x32, 0xd7, 0x56, 0x71, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc4, 0x7e, + 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, + 0x64, 0x7e, 0x0a +}; +unsigned int hi_signed_sha256_len = 495; + +unsigned char short_msg[] = { + 0x68, 0x69, 0x0a +}; +unsigned int short_msg_len = 3; + +unsigned char unsigned_msg[] = { + 0x53, 0x65, 0x64, 0x20, 0x75, 0x74, 0x20, 0x70, 0x65, 0x72, 0x73, 0x70, + 0x69, 0x63, 0x69, 0x61, 0x74, 0x69, 0x73, 0x20, 0x75, 0x6e, 0x64, 0x65, + 0x20, 0x6f, 0x6d, 0x6e, 0x69, 0x73, 0x20, 0x69, 0x73, 0x74, 0x65, 0x20, + 0x6e, 0x61, 0x74, 0x75, 0x73, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, + 0x73, 0x69, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74, + 0x65, 0x6d, 0x20, 0x61, 0x63, 0x63, 0x75, 0x73, 0x61, 0x6e, 0x74, 0x69, + 0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, 0x71, 0x75, + 0x65, 0x20, 0x6c, 0x61, 0x75, 0x64, 0x61, 0x6e, 0x74, 0x69, 0x75, 0x6d, + 0x2c, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6d, 0x20, 0x72, 0x65, 0x6d, 0x20, + 0x61, 0x70, 0x65, 0x72, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x65, 0x61, 0x71, + 0x75, 0x65, 0x20, 0x69, 0x70, 0x73, 0x61, 0x20, 0x71, 0x75, 0x61, 0x65, + 0x20, 0x61, 0x62, 0x20, 0x69, 0x6c, 0x6c, 0x6f, 0x20, 0x69, 0x6e, 0x76, + 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x76, 0x65, 0x72, 0x69, 0x74, + 0x61, 0x74, 0x69, 0x73, 0x20, 0x65, 0x74, 0x20, 0x71, 0x75, 0x61, 0x73, + 0x69, 0x20, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x20, 0x62, 0x65, 0x61, 0x74, 0x61, 0x65, 0x20, 0x76, 0x69, 0x74, 0x61, + 0x65, 0x20, 0x64, 0x69, 0x63, 0x74, 0x61, 0x20, 0x73, 0x75, 0x6e, 0x74, + 0x20, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x62, 0x6f, 0x2e, 0x20, + 0x4e, 0x65, 0x6d, 0x6f, 0x20, 0x65, 0x6e, 0x69, 0x6d, 0x20, 0x69, 0x70, + 0x73, 0x61, 0x6d, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74, + 0x65, 0x6d, 0x20, 0x71, 0x75, 0x69, 0x61, 0x20, 0x76, 0x6f, 0x6c, 0x75, + 0x70, 0x74, 0x61, 0x73, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x73, 0x70, + 0x65, 0x72, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x20, 0x61, 0x75, 0x74, 0x20, + 0x6f, 0x64, 0x69, 0x74, 0x20, 0x61, 0x75, 0x74, 0x20, 0x66, 0x75, 0x67, + 0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64, 0x20, 0x71, 0x75, 0x69, 0x61, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x75, 0x6e, 0x74, 0x75, + 0x72, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, + 0x72, 0x65, 0x73, 0x20, 0x65, 0x6f, 0x73, 0x20, 0x71, 0x75, 0x69, 0x20, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x20, 0x76, 0x6f, 0x6c, 0x75, + 0x70, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x20, 0x73, 0x65, 0x71, 0x75, 0x69, + 0x20, 0x6e, 0x65, 0x73, 0x63, 0x69, 0x75, 0x6e, 0x74, 0x2e, 0x20, 0x4e, + 0x65, 0x71, 0x75, 0x65, 0x20, 0x70, 0x6f, 0x72, 0x72, 0x6f, 0x20, 0x71, + 0x75, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x65, 0x73, 0x74, 0x2c, + 0x20, 0x71, 0x75, 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, + 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x71, 0x75, 0x69, 0x61, 0x20, + 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, + 0x65, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65, + 0x74, 0x75, 0x72, 0x2c, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, 0x73, 0x63, + 0x69, 0x20, 0x76, 0x65, 0x6c, 0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64, + 0x20, 0x71, 0x75, 0x69, 0x61, 0x20, 0x6e, 0x6f, 0x6e, 0x20, 0x6e, 0x75, + 0x6d, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x65, 0x69, 0x75, 0x73, 0x20, 0x6d, + 0x6f, 0x64, 0x69, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x20, + 0x69, 0x6e, 0x63, 0x69, 0x64, 0x75, 0x6e, 0x74, 0x20, 0x75, 0x74, 0x20, + 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x65, 0x20, 0x65, 0x74, 0x20, 0x64, 0x6f, + 0x6c, 0x6f, 0x72, 0x65, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x61, 0x6d, 0x20, + 0x61, 0x6c, 0x69, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x71, 0x75, 0x61, 0x65, + 0x72, 0x61, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74, + 0x65, 0x6d, 0x2e, 0x20, 0x55, 0x74, 0x20, 0x65, 0x6e, 0x69, 0x6d, 0x20, + 0x61, 0x64, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x61, 0x20, 0x76, 0x65, + 0x6e, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x71, 0x75, 0x69, 0x73, 0x20, 0x6e, + 0x6f, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x20, 0x65, 0x78, 0x65, 0x72, 0x63, + 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x6d, 0x20, 0x75, 0x6c, + 0x6c, 0x61, 0x6d, 0x20, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x69, 0x73, + 0x20, 0x73, 0x75, 0x73, 0x63, 0x69, 0x70, 0x69, 0x74, 0x20, 0x6c, 0x61, + 0x62, 0x6f, 0x72, 0x69, 0x6f, 0x73, 0x61, 0x6d, 0x2c, 0x20, 0x6e, 0x69, + 0x73, 0x69, 0x20, 0x75, 0x74, 0x20, 0x61, 0x6c, 0x69, 0x71, 0x75, 0x69, + 0x64, 0x20, 0x65, 0x78, 0x20, 0x65, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x64, 0x69, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x61, + 0x74, 0x75, 0x72, 0x3f, 0x20, 0x51, 0x75, 0x69, 0x73, 0x20, 0x61, 0x75, + 0x74, 0x65, 0x6d, 0x20, 0x76, 0x65, 0x6c, 0x20, 0x65, 0x75, 0x6d, 0x20, + 0x69, 0x75, 0x72, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x68, 0x65, + 0x6e, 0x64, 0x65, 0x72, 0x69, 0x74, 0x20, 0x71, 0x75, 0x69, 0x20, 0x69, + 0x6e, 0x20, 0x65, 0x61, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, + 0x74, 0x65, 0x20, 0x76, 0x65, 0x6c, 0x69, 0x74, 0x20, 0x65, 0x73, 0x73, + 0x65, 0x20, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x6e, 0x69, 0x68, 0x69, 0x6c, + 0x20, 0x6d, 0x6f, 0x6c, 0x65, 0x73, 0x74, 0x69, 0x61, 0x65, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x61, 0x74, 0x75, 0x72, 0x2c, 0x20, + 0x76, 0x65, 0x6c, 0x20, 0x69, 0x6c, 0x6c, 0x75, 0x6d, 0x20, 0x71, 0x75, + 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x65, 0x75, + 0x6d, 0x20, 0x66, 0x75, 0x67, 0x69, 0x61, 0x74, 0x20, 0x71, 0x75, 0x6f, + 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x73, 0x20, 0x6e, 0x75, + 0x6c, 0x6c, 0x61, 0x20, 0x70, 0x61, 0x72, 0x69, 0x61, 0x74, 0x75, 0x72, + 0x3f, 0x0a +}; +unsigned int unsigned_msg_len = 866; + +unsigned char certificate2_der[] = { + 0x30, 0x82, 0x05, 0x52, 0x30, 0x82, 0x03, 0x3a, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, + 0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x30, 0x3a, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0c, 0x2f, 0x47, 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, + 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x74, 0x79, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x37, 0x32, 0x38, + 0x31, 0x33, 0x32, 0x34, 0x32, 0x39, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x32, + 0x30, 0x30, 0x37, 0x30, 0x34, 0x31, 0x33, 0x32, 0x34, 0x32, 0x39, 0x5a, + 0x30, 0x2b, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x20, 0x47, 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, + 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x65, 0x79, 0x30, 0x82, 0x02, + 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, + 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xb0, 0x2f, 0x50, 0x01, 0x9c, 0x0e, + 0xd6, 0x8c, 0x07, 0xca, 0xc1, 0xcf, 0xbc, 0x03, 0xdd, 0xd3, 0xfa, 0xe3, + 0x4f, 0x71, 0xc1, 0x30, 0xaa, 0x09, 0x96, 0xe4, 0xd0, 0x6c, 0x42, 0x93, + 0xdb, 0x35, 0xf6, 0x7e, 0x1b, 0x67, 0xc0, 0xc2, 0x2d, 0x5b, 0xec, 0xca, + 0x35, 0x06, 0x32, 0x6c, 0x7b, 0x2c, 0xd3, 0x71, 0x2b, 0xe9, 0x7a, 0x19, + 0xd1, 0xf2, 0xa0, 0x7f, 0xd7, 0x4d, 0x6e, 0x28, 0xbb, 0xae, 0x49, 0x4a, + 0xbc, 0xea, 0x47, 0x67, 0xb8, 0x36, 0xa6, 0xf5, 0x0d, 0x0e, 0x20, 0x14, + 0x0c, 0x66, 0x67, 0x28, 0xb5, 0x97, 0x8b, 0x1f, 0x5e, 0x32, 0x06, 0x29, + 0x9c, 0x99, 0x92, 0x0f, 0x73, 0xac, 0xfd, 0xd2, 0x1d, 0xf2, 0xa8, 0x55, + 0x9d, 0x1b, 0xd8, 0x3d, 0xb0, 0x76, 0x9a, 0xb6, 0x6c, 0x9f, 0x62, 0x37, + 0x2f, 0xc0, 0xef, 0x44, 0xb3, 0x0d, 0x4a, 0x3e, 0x4f, 0x7d, 0xbd, 0xdb, + 0xd8, 0x75, 0x5f, 0x68, 0xe3, 0xf0, 0xec, 0x82, 0x66, 0x7c, 0x31, 0x70, + 0xa9, 0xa1, 0x6f, 0x38, 0x9f, 0xdf, 0xf5, 0xf0, 0x7d, 0x23, 0x9d, 0x34, + 0xa5, 0x85, 0xd3, 0xdf, 0x68, 0x41, 0xfc, 0x4f, 0x89, 0x45, 0x3c, 0x24, + 0x81, 0xa6, 0xf2, 0x3c, 0x02, 0x26, 0x09, 0x48, 0xdd, 0xfe, 0x4b, 0xb6, + 0x66, 0xbf, 0x8f, 0xe5, 0x5f, 0xf0, 0x5d, 0x8a, 0x61, 0x2e, 0x5f, 0x9f, + 0x80, 0xd9, 0xd5, 0xe6, 0x41, 0xd8, 0x10, 0x5e, 0x7a, 0xc6, 0xdb, 0x89, + 0xc7, 0xca, 0x6c, 0x5b, 0xb1, 0x4e, 0x7d, 0x0c, 0x03, 0xfd, 0x50, 0xca, + 0xbf, 0xbb, 0xe2, 0x69, 0x4b, 0x4e, 0xc2, 0x3d, 0x75, 0xfa, 0xd1, 0xcc, + 0xd6, 0xf9, 0x39, 0xb9, 0xdc, 0x53, 0xad, 0x62, 0xfb, 0x1b, 0x94, 0x26, + 0x7f, 0x21, 0x54, 0x5c, 0xb7, 0xdc, 0xe7, 0x96, 0x8c, 0xce, 0x75, 0xe0, + 0x17, 0x01, 0x3a, 0x3c, 0x77, 0x6e, 0xa4, 0x8b, 0x7a, 0x83, 0x28, 0x7a, + 0xf7, 0xb0, 0x5f, 0xfc, 0x7f, 0x2d, 0x2e, 0xec, 0xf5, 0xeb, 0x9c, 0x63, + 0x74, 0xd0, 0xe5, 0xdc, 0x19, 0xe4, 0x71, 0xc5, 0x4a, 0x8a, 0x54, 0xa4, + 0xe0, 0x7d, 0x4e, 0xbf, 0x53, 0x30, 0xaf, 0xd0, 0xeb, 0x96, 0xc3, 0xbb, + 0x65, 0xf7, 0x67, 0xf5, 0xae, 0xd3, 0x96, 0xf2, 0x63, 0xc8, 0x69, 0xf7, + 0x47, 0xcb, 0x27, 0x79, 0xe1, 0xff, 0x2f, 0x68, 0xdf, 0x1e, 0xb3, 0xb8, + 0x0c, 0xc5, 0x58, 0x73, 0xcc, 0xfe, 0x8c, 0xda, 0x4e, 0x3b, 0x01, 0x04, + 0xcd, 0xcb, 0xb8, 0x3e, 0x06, 0xfd, 0x4c, 0x0a, 0x9f, 0x5e, 0x76, 0x8c, + 0x0c, 0x83, 0x75, 0x09, 0x08, 0xb2, 0xdb, 0xf4, 0x49, 0x4e, 0xa0, 0xf2, + 0x0c, 0x7b, 0x87, 0x38, 0x9e, 0x22, 0x67, 0xbd, 0xd1, 0x97, 0x57, 0x24, + 0xf1, 0x46, 0x07, 0xf9, 0xd2, 0x1b, 0xec, 0x25, 0x5e, 0x67, 0xd9, 0x66, + 0x23, 0x1b, 0xd3, 0xe4, 0xaa, 0xec, 0x88, 0xf0, 0x7e, 0x15, 0x83, 0x51, + 0x31, 0x67, 0x51, 0x76, 0x5f, 0x55, 0xd7, 0x36, 0xdf, 0x4a, 0x84, 0x0b, + 0x6f, 0x5c, 0xbb, 0x5b, 0x8f, 0x37, 0x23, 0x7f, 0xf8, 0x17, 0x84, 0xa2, + 0x70, 0x20, 0x07, 0x0c, 0x90, 0x3a, 0x04, 0xfd, 0xf0, 0x08, 0x4a, 0xb1, + 0x16, 0x0f, 0xe6, 0xf6, 0x40, 0x51, 0x83, 0xd2, 0x87, 0x40, 0x9c, 0x1c, + 0x9f, 0x13, 0x38, 0x17, 0xd3, 0x34, 0x58, 0xad, 0x05, 0x71, 0xa0, 0x73, + 0xca, 0x40, 0xa6, 0xa4, 0x81, 0x02, 0xee, 0xa8, 0x72, 0x41, 0xa1, 0x41, + 0x18, 0x64, 0x8a, 0x86, 0x8a, 0x5d, 0xe6, 0x4f, 0x0a, 0xc5, 0x95, 0x98, + 0xf9, 0x78, 0xfe, 0x19, 0x0d, 0xc9, 0xb3, 0x89, 0xc1, 0x2b, 0x09, 0xbe, + 0xf1, 0xd2, 0x04, 0x5d, 0xcc, 0x28, 0xf5, 0x4b, 0xd2, 0x20, 0x4f, 0xc5, + 0x41, 0x9d, 0x8c, 0x85, 0xd8, 0xb0, 0x68, 0x5e, 0xc1, 0x0c, 0xb7, 0x24, + 0x4d, 0x67, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, + 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, + 0x07, 0x80, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0xac, 0xf5, 0x47, 0x17, 0xd9, 0x7d, 0xc1, 0xb1, 0xc4, 0x41, 0xe1, + 0x41, 0x60, 0xcb, 0x37, 0x11, 0x60, 0x28, 0x78, 0x5f, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x21, 0x94, + 0xfb, 0xf9, 0xb2, 0x43, 0xe9, 0x33, 0xd7, 0x50, 0x7d, 0xc7, 0x37, 0xdb, + 0xd5, 0x82, 0x5a, 0x4e, 0xbe, 0x1b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, + 0x01, 0x00, 0x96, 0x70, 0x65, 0x26, 0x42, 0xf8, 0xdc, 0x69, 0xde, 0xcf, + 0x41, 0x3a, 0x2e, 0x7f, 0x5b, 0xf1, 0xf9, 0x3b, 0x9b, 0xd2, 0x4e, 0x64, + 0x48, 0x81, 0xe4, 0x5d, 0x1e, 0x22, 0xce, 0x68, 0x63, 0x62, 0xe5, 0x1b, + 0x9b, 0xf2, 0xc7, 0x12, 0xda, 0x1e, 0x9b, 0x90, 0x84, 0x79, 0x48, 0x12, + 0xe6, 0x21, 0x6f, 0x2f, 0x7e, 0x18, 0x77, 0xdb, 0x8c, 0xc4, 0xd1, 0x0d, + 0x91, 0xbf, 0x39, 0x22, 0x0f, 0x64, 0xcf, 0x25, 0x2e, 0x8c, 0x1f, 0x91, + 0x81, 0xb5, 0xe9, 0x6c, 0x02, 0x3a, 0xf8, 0x07, 0xa2, 0x6f, 0x46, 0x5d, + 0x7b, 0xfd, 0x43, 0xff, 0x41, 0x0f, 0xe2, 0x57, 0x1c, 0xbd, 0x48, 0x60, + 0x53, 0x11, 0x48, 0x87, 0x88, 0x9d, 0x13, 0x82, 0x40, 0x68, 0x44, 0x2c, + 0xc6, 0xc8, 0x95, 0x27, 0x4f, 0xb6, 0xb9, 0x4a, 0x22, 0x0a, 0xfd, 0xe4, + 0x46, 0x8f, 0x35, 0x12, 0x98, 0x5a, 0x34, 0x6f, 0x2b, 0x57, 0x62, 0xa1, + 0x4d, 0x8d, 0x79, 0x37, 0xe4, 0x6b, 0x8a, 0x32, 0x5b, 0xcb, 0xef, 0x79, + 0x11, 0xed, 0xa7, 0xf8, 0x7a, 0x1c, 0xbd, 0x86, 0xdc, 0x0e, 0x2e, 0xfd, + 0xd3, 0x51, 0xbb, 0x73, 0xad, 0x00, 0xa0, 0x1b, 0xf9, 0x1d, 0xd1, 0x4a, + 0xe4, 0xd4, 0x02, 0x63, 0x2b, 0x39, 0x5f, 0x18, 0x08, 0x2f, 0x42, 0xb7, + 0x23, 0x4b, 0x48, 0x46, 0x1f, 0x63, 0x87, 0xae, 0x6d, 0xd5, 0xdb, 0x60, + 0xf8, 0x5f, 0xd3, 0x13, 0xec, 0xca, 0xdd, 0x60, 0x60, 0x79, 0x52, 0x70, + 0x47, 0xae, 0x1d, 0x38, 0x78, 0x71, 0xcf, 0xb3, 0x04, 0x03, 0xbe, 0xba, + 0x81, 0xba, 0x74, 0xb1, 0x30, 0x35, 0xdc, 0xea, 0x21, 0x4a, 0x9b, 0x70, + 0xfb, 0xd6, 0x60, 0x59, 0x78, 0x0c, 0x4d, 0x39, 0x19, 0x1d, 0xe5, 0x75, + 0xba, 0x07, 0xf4, 0x22, 0x37, 0x64, 0xb7, 0xf2, 0x9a, 0xc9, 0x11, 0x2d, + 0x8e, 0x58, 0xa6, 0xcf, 0x83, 0xf1, 0xcb, 0x6c, 0x7f, 0x02, 0xbd, 0xda, + 0x03, 0x92, 0xa9, 0x45, 0x24, 0x56, 0xc5, 0xbd, 0x41, 0xd1, 0x20, 0x86, + 0xc0, 0xb6, 0xb7, 0xe8, 0xa7, 0xb2, 0x46, 0xf7, 0x8e, 0xa9, 0x38, 0x0e, + 0x23, 0x77, 0x3c, 0x0d, 0x66, 0x83, 0x6a, 0x1a, 0x6b, 0x7f, 0x54, 0x11, + 0x58, 0x0d, 0x4a, 0xb5, 0x74, 0x60, 0xca, 0xed, 0xff, 0x91, 0x47, 0xd9, + 0x29, 0xe0, 0xaa, 0x8c, 0xa8, 0x8f, 0x10, 0x4c, 0x15, 0x7d, 0xce, 0x95, + 0xf9, 0x87, 0x1e, 0x18, 0x38, 0x18, 0xfc, 0xcc, 0xaf, 0x91, 0x17, 0x3f, + 0xfa, 0xf0, 0x8a, 0x09, 0x6f, 0xba, 0x4e, 0x53, 0xf7, 0xfa, 0x4f, 0x20, + 0xa3, 0xf4, 0x4a, 0x5a, 0xde, 0x17, 0x1c, 0x29, 0x6a, 0x6f, 0x03, 0x48, + 0xdf, 0xad, 0x4f, 0xe4, 0xbc, 0x71, 0xc4, 0x72, 0x32, 0x11, 0x84, 0xac, + 0x09, 0xd2, 0x18, 0x44, 0x35, 0xf1, 0xcd, 0xaf, 0xa8, 0x98, 0xe0, 0x8b, + 0xec, 0xa0, 0x83, 0x37, 0xc3, 0x35, 0x85, 0xd6, 0xd8, 0x1b, 0xe0, 0x75, + 0xdc, 0xfd, 0xde, 0xc9, 0xeb, 0xd5, 0x18, 0x0f, 0xd3, 0x4c, 0x2f, 0x71, + 0xdc, 0x48, 0xe3, 0x14, 0xeb, 0xda, 0x00, 0x24, 0x24, 0x9e, 0xa3, 0x8e, + 0x3e, 0x08, 0x6f, 0x22, 0x24, 0xd6, 0xc4, 0x85, 0x8f, 0x68, 0x00, 0x4a, + 0x82, 0x4c, 0x33, 0x6e, 0xa5, 0x35, 0x7b, 0xeb, 0x4b, 0xdc, 0xa0, 0xa6, + 0x65, 0x6f, 0x5a, 0x7a, 0xdf, 0x8a, 0x01, 0x52, 0xa1, 0x6c, 0xff, 0x59, + 0x22, 0x7f, 0xe1, 0x96, 0x1b, 0x19, 0xb8, 0xf9, 0x5d, 0x44, 0x9f, 0x91, + 0x03, 0x3c, 0x3d, 0xa1, 0x2a, 0xb6, 0x5a, 0x51, 0xa0, 0xce, 0x4a, 0x88, + 0x22, 0x72, 0x9c, 0xdc, 0xc0, 0x47, 0x76, 0x35, 0x84, 0x75, 0x9b, 0x87, + 0x5c, 0xd3, 0xcf, 0xe7, 0xdd, 0xa3, 0x57, 0x14, 0xdf, 0x00, 0xfd, 0x19, + 0x2a, 0x7d, 0x89, 0x27, 0x1c, 0x78, 0x97, 0x04, 0x58, 0x48 +}; +unsigned int certificate2_der_len = 1366; + +unsigned char hi_signed_2nd[] = { + 0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb1, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa2, 0x30, 0x82, + 0x02, 0x9e, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02, + 0x7b, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a, + 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47, + 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, + 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14, + 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07, + 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, + 0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90, + 0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99, + 0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07, + 0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe, + 0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f, + 0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f, + 0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f, + 0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53, + 0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2, + 0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0, + 0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf, + 0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05, + 0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d, + 0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d, + 0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81, + 0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36, + 0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5, + 0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc, + 0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70, + 0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94, + 0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3, + 0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20, + 0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2, + 0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac, + 0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb, + 0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49, + 0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0, + 0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb, + 0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76, + 0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5, + 0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49, + 0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b, + 0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25, + 0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e, + 0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0, + 0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98, + 0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b, + 0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83, + 0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e, + 0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a, + 0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7, + 0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26, + 0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xb5, + 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, + 0x65, 0x64, 0x7e, 0x0a +}; +unsigned int hi_signed_2nd_len = 736; + +unsigned char certificate_printable_der[] = { + 0x30, 0x82, 0x03, 0x39, 0x30, 0x82, 0x02, 0x21, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0xde, 0xf6, 0x22, 0xc4, 0xf2, 0xf1, 0x86, 0x02, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x30, 0x2a, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x1f, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, + 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, + 0x43, 0x41, 0x20, 0x32, 0x20, 0x28, 0x62, 0x65, 0x74, 0x61, 0x29, 0x30, + 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x31, 0x30, 0x33, 0x31, 0x31, 0x34, 0x31, + 0x39, 0x32, 0x33, 0x5a, 0x17, 0x0d, 0x33, 0x37, 0x31, 0x30, 0x32, 0x35, + 0x31, 0x34, 0x31, 0x39, 0x32, 0x33, 0x5a, 0x30, 0x2f, 0x31, 0x2d, 0x30, + 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x52, 0x65, 0x64, 0x20, + 0x48, 0x61, 0x74, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, + 0x6f, 0x6f, 0x74, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, + 0x33, 0x20, 0x28, 0x62, 0x65, 0x74, 0x61, 0x29, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xbd, 0xda, 0xa1, 0xed, 0x8d, 0x8e, 0x15, + 0x5c, 0xf8, 0x01, 0x77, 0x48, 0x4a, 0x60, 0x96, 0xf9, 0x27, 0xfa, 0xe2, + 0xb1, 0x69, 0x0f, 0x51, 0x19, 0x52, 0x7e, 0xc4, 0x34, 0x8e, 0xe1, 0x9b, + 0x9c, 0xa4, 0xb1, 0x5c, 0xd6, 0x81, 0x98, 0x78, 0xfe, 0xa9, 0xe5, 0x0b, + 0x00, 0xba, 0x9c, 0x64, 0x7e, 0xc7, 0xcc, 0x72, 0xb1, 0x73, 0x4b, 0x11, + 0x07, 0x52, 0xf0, 0x20, 0x96, 0x8b, 0x99, 0x39, 0xde, 0xdb, 0xfa, 0x3d, + 0x45, 0xe2, 0x98, 0x7b, 0x0c, 0x41, 0xe4, 0x0c, 0xb5, 0x5d, 0x92, 0x74, + 0x39, 0x96, 0xe1, 0x97, 0x97, 0xa1, 0xad, 0x2e, 0xcc, 0xd0, 0x1b, 0x4d, + 0x9d, 0xbd, 0x3e, 0xa9, 0x36, 0x8e, 0xcc, 0xc7, 0x5f, 0x6a, 0x7d, 0x39, + 0x5e, 0x0b, 0x8d, 0xca, 0xe4, 0x83, 0xe9, 0x3b, 0x5c, 0x86, 0x47, 0xd4, + 0xba, 0x7d, 0x98, 0x26, 0xa1, 0xf4, 0xe8, 0x90, 0x6b, 0x0f, 0xf1, 0x6b, + 0x8c, 0xe3, 0xa2, 0x80, 0x3c, 0x96, 0xf1, 0x0a, 0xb6, 0x66, 0xc0, 0x4b, + 0x61, 0xf7, 0x74, 0xcd, 0xd3, 0x7b, 0x8e, 0x5e, 0x39, 0xda, 0x99, 0x20, + 0x33, 0x93, 0xd3, 0xf0, 0x7f, 0xad, 0x35, 0xe9, 0x88, 0x8d, 0x9c, 0xbf, + 0x65, 0xf1, 0x47, 0x02, 0xf9, 0x7c, 0xed, 0x27, 0x5f, 0x4a, 0x65, 0x3c, + 0xcf, 0x5f, 0x0e, 0x88, 0x95, 0x74, 0xde, 0xfb, 0x9e, 0x2e, 0x91, 0x9b, + 0x45, 0x37, 0xc8, 0x85, 0xff, 0xe3, 0x41, 0x70, 0xfe, 0xd5, 0xef, 0x0e, + 0x82, 0x22, 0x08, 0xb7, 0x3b, 0x44, 0x3e, 0xdc, 0x5b, 0x7f, 0xba, 0xbf, + 0xe6, 0x58, 0x9d, 0x02, 0x6e, 0x75, 0xbf, 0x50, 0xec, 0xcf, 0x3f, 0xa5, + 0x91, 0x0a, 0xe2, 0x59, 0x2c, 0xc3, 0xe7, 0x05, 0x03, 0xe8, 0xf2, 0x6f, + 0x2a, 0x04, 0x68, 0x9a, 0x31, 0x32, 0x8f, 0x04, 0x35, 0xcd, 0x1f, 0x34, + 0xcc, 0x4f, 0x79, 0x5a, 0x99, 0x8d, 0x9d, 0x5c, 0xf5, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x1d, 0x06, + 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x65, 0xc5, 0xbe, 0xca, + 0xe6, 0x59, 0x6a, 0xfd, 0x6c, 0x71, 0xc4, 0xa7, 0x98, 0xc6, 0x25, 0x8d, + 0x7b, 0x67, 0x05, 0xd0, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x18, 0x30, 0x16, 0x80, 0x14, 0x81, 0xf8, 0xee, 0x47, 0x5c, 0x3e, 0xed, + 0xfb, 0xce, 0xa5, 0x84, 0xbe, 0xd7, 0xae, 0xdb, 0xd3, 0x7d, 0x64, 0xb3, + 0x2a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x66, 0x1e, 0x3d, + 0x1d, 0x53, 0x33, 0xde, 0x4e, 0xc7, 0xc4, 0xf4, 0xdf, 0xda, 0x18, 0x19, + 0x8a, 0xa9, 0xff, 0xe2, 0x63, 0x2b, 0xbe, 0xf2, 0x61, 0x63, 0xe2, 0xf6, + 0xed, 0x47, 0x1a, 0x71, 0x02, 0xec, 0x2a, 0xef, 0x89, 0x77, 0xe3, 0xfd, + 0x86, 0x69, 0xf1, 0x3f, 0x0d, 0xf9, 0x6e, 0xf9, 0x3b, 0xad, 0x26, 0x47, + 0xb7, 0xf2, 0x0d, 0xad, 0x23, 0xa3, 0x67, 0x3b, 0xcb, 0x6d, 0x9e, 0x03, + 0x0f, 0xbc, 0x69, 0x73, 0x9f, 0xd4, 0xa5, 0x0f, 0x6f, 0xf8, 0xab, 0x4d, + 0x36, 0xd1, 0xe0, 0xe0, 0x5d, 0x20, 0x43, 0x90, 0xc4, 0x65, 0x61, 0x93, + 0xe2, 0x0f, 0x51, 0x59, 0x0a, 0xf7, 0x88, 0x70, 0x57, 0xb9, 0x04, 0xa9, + 0x32, 0x57, 0x9c, 0xb3, 0x57, 0x38, 0x8b, 0x8e, 0x46, 0xc8, 0x32, 0x6c, + 0xb4, 0xf3, 0x96, 0x7f, 0x4b, 0xf0, 0x88, 0xf9, 0x7f, 0xe2, 0x71, 0xe1, + 0x8b, 0xe2, 0x14, 0xf1, 0x4b, 0x25, 0x00, 0x48, 0x1c, 0x7e, 0xe5, 0x8d, + 0x65, 0x2d, 0xeb, 0x72, 0x4f, 0x92, 0x44, 0xf3, 0xe6, 0xe0, 0xd0, 0xdf, + 0x85, 0xa8, 0x13, 0x4a, 0xfb, 0x99, 0xca, 0x14, 0x2c, 0x97, 0x80, 0x93, + 0x27, 0xd3, 0x20, 0xf8, 0x6d, 0x29, 0x28, 0x2c, 0xb9, 0x77, 0xea, 0xb1, + 0x63, 0xbd, 0x7d, 0x53, 0xfd, 0x4a, 0x62, 0x64, 0x0b, 0x98, 0xa8, 0xae, + 0x11, 0xfc, 0x6e, 0x8d, 0x63, 0xd4, 0x15, 0x55, 0xc6, 0x4c, 0x74, 0xf5, + 0x5f, 0xa0, 0xb9, 0x2c, 0x2d, 0x9a, 0x7a, 0x87, 0x6e, 0xf0, 0x5e, 0x25, + 0xed, 0xfc, 0xd8, 0xc4, 0x34, 0x33, 0x32, 0xad, 0x01, 0xd4, 0x4b, 0x49, + 0x51, 0xc2, 0x07, 0x7f, 0x90, 0x6d, 0xea, 0xf5, 0x4c, 0x41, 0x71, 0x64, + 0xeb, 0x1f, 0x29, 0xa3, 0x1f, 0x64, 0xa2, 0x1e, 0x0e, 0x6f, 0xa1, 0x67, + 0x99, 0x8d, 0x98, 0x1c, 0xb8, 0x53, 0x9d, 0x30, 0x1d, 0xae, 0x32, 0x56, + 0xd2 +}; +unsigned int certificate_printable_der_len = 829; diff --git a/grub-core/tests/lib/functional_test.c b/grub-core/tests/lib/functional_test.c index 96781fb39b..403fa5c789 100644 --- a/grub-core/tests/lib/functional_test.c +++ b/grub-core/tests/lib/functional_test.c @@ -73,6 +73,7 @@ grub_functional_all_tests (grub_extcmd_context_t ctxt __attribute__ ((unused)), grub_dl_load ("xnu_uuid_test"); grub_dl_load ("pbkdf2_test"); grub_dl_load ("signature_test"); + grub_dl_load ("appended_signature_test"); grub_dl_load ("sleep_test"); grub_dl_load ("bswap_test"); grub_dl_load ("ctz_test"); From 33403633cd7a81f55974348c047d32c9719514c0 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Thu, 1 Oct 2020 13:02:09 +1000 Subject: [PATCH 172/367] appended signatures: documentation This explains how appended signatures can be used to form part of a secure boot chain, and documents the commands and variables introduced. Signed-off-by: Daniel Axtens --- docs/grub.texi | 199 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 182 insertions(+), 17 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index afbde7c1f7..4816be8561 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -3214,6 +3214,7 @@ These variables have special meaning to GRUB. @menu * biosnum:: +* check_appended_signatures:: * check_signatures:: * chosen:: * cmdpath:: @@ -3273,11 +3274,18 @@ For an alternative approach which also changes BIOS drive mappings for the chain-loaded system, @pxref{drivemap}. +@node check_appended_signatures +@subsection check_appended_signatures + +This variable controls whether GRUB enforces appended signature validation on +certain loaded files. @xref{Using appended signatures}. + + @node check_signatures @subsection check_signatures -This variable controls whether GRUB enforces digital signature -validation on loaded files. @xref{Using digital signatures}. +This variable controls whether GRUB enforces GPG-style digital signature +validation on loaded files. @xref{Using GPG-style digital signatures}. @node chosen @subsection chosen @@ -3994,6 +4002,7 @@ you forget a command, you can run the command @command{help} * date:: Display or set current date and time * devicetree:: Load a device tree blob * distrust:: Remove a pubkey from trusted keys +* distrust_certificate:: Remove a certificate from the list of trusted certificates * drivemap:: Map a drive to another * echo:: Display a line of text * eval:: Evaluate agruments as GRUB commands @@ -4010,6 +4019,7 @@ you forget a command, you can run the command @command{help} * keystatus:: Check key modifier status * linux:: Load a Linux kernel * linux16:: Load a Linux kernel (16-bit mode) +* list_certificates:: List trusted certificates * list_env:: List variables in environment block * list_trusted:: List trusted public keys * load_env:: Load variables from environment block @@ -4047,8 +4057,10 @@ you forget a command, you can run the command @command{help} * test:: Check file types and compare values * true:: Do nothing, successfully * trust:: Add public key to list of trusted keys +* trust_certificate:: Add an x509 certificate to the list of trusted certificates * unset:: Unset an environment variable @comment * vbeinfo:: List available video modes +* verify_appended:: Verify appended digital signature * verify_detached:: Verify detached digital signature * videoinfo:: List available video modes @comment * xen_*:: Xen boot commands for AArch64 @@ -4376,9 +4388,28 @@ These keys are used to validate signatures when environment variable @code{check_signatures} is set to @code{enforce} (@pxref{check_signatures}), and by some invocations of @command{verify_detached} (@pxref{verify_detached}). @xref{Using -digital signatures}, for more information. +GPG-style digital signatures}, for more information. @end deffn + +@node distrust_certificate +@subsection distrust_certificate + +@deffn Command distrust_certificate cert_number +Remove the x509 certificate numbered @var{cert_number} from GRUB's keyring of +trusted x509 certificates for verifying appended signatures. + +@var{cert_number} is the certificate number as listed by +@command{list_certificates} (@pxref{list_certificates}). + +These certificates are used to validate appended signatures when environment +variable @code{check_appended_signatures} is set to @code{enforce} or +@code{forced} (@pxref{check_appended_signatures}), and by +@command{verify_appended} (@pxref{verify_appended}). See +@xref{Using appended signatures} for more information. +@end deffn + + @node drivemap @subsection drivemap @@ -4636,6 +4667,21 @@ This command is only available on x86 systems. @end deffn +@node list_certificates +@subsection list_certificates + +@deffn Command list_certificates +List all x509 certificates trusted by GRUB for validating appended signatures. +The output is a numbered list of certificates, showing the certificate's serial +number and Common Name. + +The certificate number can be used as an argument to +@command{distrust_certificate} (@pxref{distrust_certificate}). + +See @xref{Using appended signatures} for more information. +@end deffn + + @node list_env @subsection list_env @@ -4655,7 +4701,7 @@ The output is in GPG's v4 key fingerprint format (i.e., the output of @code{gpg --fingerprint}). The least significant four bytes (last eight hexadecimal digits) can be used as an argument to @command{distrust} (@pxref{distrust}). -@xref{Using digital signatures}, for more information about uses for +@xref{Using GPG-style digital signatures}, for more information about uses for these keys. @end deffn @@ -4690,8 +4736,13 @@ When used with care, @option{--skip-sig} and the whitelist enable an administrator to configure a system to boot only signed configurations, but to allow the user to select from among multiple configurations, and to enable ``one-shot'' boot attempts and -``savedefault'' behavior. @xref{Using digital signatures}, for more +``savedefault'' behavior. @xref{Using GPG-style digital signatures}, for more information. + +Extra care should be taken when combining this command with appended signatures +(@pxref{Using appended signatures}), as this file is not validated by an +appended signature and could set @code{check_appended_signatures=no} if GRUB is +not in @pxref{Lockdown} mode. @end deffn @@ -4987,7 +5038,7 @@ read. It is possible to modify a digitally signed environment block file from within GRUB using this command, such that its signature will no longer be valid on subsequent boots. Care should be taken in such advanced configurations to avoid rendering the system -unbootable. @xref{Using digital signatures}, for more information. +unbootable. @xref{Using GPG-style digital signatures}, for more information. @end deffn @@ -5387,11 +5438,32 @@ signatures when environment variable @code{check_signatures} is set to must itself be properly signed. The @option{--skip-sig} option can be used to disable signature-checking when reading @var{pubkey_file} itself. It is expected that @option{--skip-sig} is useful for testing -and manual booting. @xref{Using digital signatures}, for more +and manual booting. @xref{Using GPG-style digital signatures}, for more information. @end deffn +@node trust_certificate +@subsection trust_certificate + +@deffn Command trust_certificate x509_certificate +Read an DER-formatted x509 certificate from the file @var{x509_certificate} +and add it to GRUB's internal list of trusted x509 certificates. These +certificates are used to validate appended signatures when the environment +variable @code{check_appended_signatures} is set to @code{enforce} or +@code{forced}. + +Note that if @code{check_appended_signatures} is set to @code{enforce} or +@code{forced} when @command{trust_certificate} is executed, then +@var{x509_certificate} must itself bear an appended signature. (It is not +sufficient that @var{x509_certificate} be signed by a trusted certificate +according to the x509 rules: grub does not include support for validating +signatures within x509 certificates themselves.) + +See @xref{Using appended signatures} for more information. +@end deffn + + @node unset @subsection unset @@ -5410,6 +5482,18 @@ only on PC BIOS platforms. @end deffn @end ignore +@node verify_appended +@subsection verify_appended + +@deffn Command verify_appended file +Verifies an appended signature on @var{file} against the trusted certificates +known to GRUB (See @pxref{list_certificates}, @pxref{trust_certificate}, and +@pxref{distrust_certificate}). + +Exit code @code{$?} is set to 0 if the signature validates +successfully. If validation fails, it is set to a non-zero value. +See @xref{Using appended signatures}, for more information. +@end deffn @node verify_detached @subsection verify_detached @@ -5428,7 +5512,7 @@ tried. Exit code @code{$?} is set to 0 if the signature validates successfully. If validation fails, it is set to a non-zero value. -@xref{Using digital signatures}, for more information. +@xref{Using GPG-style digital signatures}, for more information. @end deffn @node videoinfo @@ -5811,13 +5895,14 @@ environment variables and commands are listed in the same order. @chapter Security @menu -* Authentication and authorisation:: Users and access control -* Using digital signatures:: Booting digitally signed code -* UEFI secure boot and shim:: Booting digitally signed PE files -* Secure Boot Advanced Targeting:: Embedded information for generation number based revocation -* Measured Boot:: Measuring boot components -* Lockdown:: Lockdown when booting on a secure setup -* Signing GRUB itself:: Ensuring the integrity of the GRUB core image +* Authentication and authorisation:: Users and access control +* Using GPG-style digital signatures:: Booting digitally signed code +* Using appended signatures:: An alternative approach to booting digitally signed code +* UEFI secure boot and shim:: Booting digitally signed PE files +* Secure Boot Advanced Targeting:: Embedded information for generation number based revocation +* Measured Boot:: Measuring boot components +* Lockdown:: Lockdown when booting on a secure setup +* Signing GRUB itself:: Ensuring the integrity of the GRUB core image @end menu @node Authentication and authorisation @@ -5891,8 +5976,8 @@ generating configuration files with authentication. You can use adding @kbd{set superusers=} and @kbd{password} or @kbd{password_pbkdf2} commands. -@node Using digital signatures -@section Using digital signatures in GRUB +@node Using GPG-style digital signatures +@section Using GPG-style digital signatures in GRUB GRUB's @file{core.img} can optionally provide enforcement that all files subsequently read from disk are covered by a valid digital signature. @@ -5985,6 +6070,86 @@ or BIOS) configuration to cause the machine to boot from a different (attacker-controlled) device. GRUB is at best only one link in a secure boot chain. +@node Using appended signatures +@section Using appended signatures in GRUB + +GRUB supports verifying Linux-style 'appended signatures' for secure boot. +Appended signatures are PKCS#7 messages containing a signature over the +contents of a file, plus some metadata, appended to the end of a file. A file +with an appended signature ends with the magic string: + +@example +~Module signature appended~\n +@end example + +where @code{\n} represents the line-feed character, @code{0x0a}. + +Certificates can be managed at boot time using the @pxref{trust_certificate}, +@pxref{distrust_certificate} and @pxref{list_certificates} commands. +Certificates can also be built in to the core image using the @code{--x509} +parameter to @command{grub-install} or @command{grub-mkimage}. + +A file can be explictly verified using the @pxref{verify_appended} command. + +Only signatures made with the SHA-256 or SHA-512 hash algorithm are supported, +and only RSA signatures are supported. + +A file can be signed with the @command{sign-file} utility supplied with the +Linux kernel source. For example, if you have @code{signing.key} as the private +key and @code{certificate.der} as the x509 certificate containing the public key: + +@example +sign-file SHA256 signing.key certificate.der vmlinux vmlinux.signed +@end example + +Enforcement of signature verification is controlled by the +@code{check_appended_signatures} variable. + +@itemize +@item @samp{no}: no verification is performed. This is the default when GRUB + is not in @pxref{Lockdown} mode. +@item @samp{enforce}: verification is performed. Verification can be disabled + by setting the variable back to @samp{no}. +@item @samp{forced}: verification is performed and cannot be disabled. This is + set when GRUB is in Lockdown when the appendedsig module is loaded. +@end itemize + +Unlike GPG-style signatures, not all files loaded by GRUB are required to be +signed. Once verification is turned on, the following file types will have +appended signatures verified: + +@itemize +@item Linux kernels +@item GRUB modules, except those built into the core image +@item Any new certificate files to be trusted +@end itemize + +ACPI tables and Device Tree images will not be checked for appended signatures +but must be verified by another mechanism such as GPG-style signatures before +they will be loaded. + +Unless lockdown mode is enabled, signature checking does @strong{not} +stop an attacker with console access from dropping manually to the GRUB +console and executing: + +@example +set check_appended_signatures=no +@end example + +Refer to the section on password-protecting GRUB (@pxref{Authentication +and authorisation}) for more information on preventing this. + +Additionally, unless lockdown mode is enabled: + +@itemize +@item Special care must be taken around the @command{loadenv} command, which + can be used to turn off @code{check_appended_signature}. + +@item If the grub configuration file is loaded from the disk, anyone who can + modify the file on disk can turn off @code{check_appended_signature}. + Consider embedding the configuration into the core grub image. +@end itemize + @node UEFI secure boot and shim @section UEFI secure boot and shim support From a986f596ebdd849a195943bc3e02570abaa20ca5 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Mon, 28 Sep 2020 11:11:17 +1000 Subject: [PATCH 173/367] ieee1275: enter lockdown based on /ibm,secure-boot If the 'ibm,secure-boot' property of the root node is 2 or greater, enter lockdown. Signed-off-by: Daniel Axtens --- docs/grub.texi | 4 ++-- grub-core/Makefile.core.def | 1 + grub-core/kern/ieee1275/init.c | 27 +++++++++++++++++++++++++++ include/grub/lockdown.h | 3 ++- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index 4816be8561..a4da9c2a1b 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -6227,8 +6227,8 @@ Measured boot is currently only supported on EFI platforms. @section Lockdown when booting on a secure setup The GRUB can be locked down when booted on a secure boot environment, for example -if the UEFI secure boot is enabled. On a locked down configuration, the GRUB will -be restricted and some operations/commands cannot be executed. +if UEFI or Power secure boot is enabled. On a locked down configuration, the +GRUB will be restricted and some operations/commands cannot be executed. The @samp{lockdown} variable is set to @samp{y} when the GRUB is locked down. Otherwise it does not exit. diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 9ea5fb38f1..4a57de975e 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -323,6 +323,7 @@ kernel = { powerpc_ieee1275 = kern/powerpc/cache.S; powerpc_ieee1275 = kern/powerpc/dl.c; powerpc_ieee1275 = kern/powerpc/compiler-rt.S; + powerpc_ieee1275 = kern/lockdown.c; sparc64_ieee1275 = kern/sparc64/cache.S; sparc64_ieee1275 = kern/sparc64/dl.c; diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 937c1bc44c..fc7d971272 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -44,6 +44,7 @@ #ifdef __sparc__ #include #endif +#include /* The minimal heap size we can live with. */ #define HEAP_MIN_SIZE (unsigned long) (2 * 1024 * 1024) @@ -271,6 +272,30 @@ grub_parse_cmdline (void) } } +static void +grub_get_ieee1275_secure_boot (void) +{ + grub_ieee1275_phandle_t root; + int rc; + grub_uint32_t is_sb; + + grub_ieee1275_finddevice ("/", &root); + + rc = grub_ieee1275_get_integer_property (root, "ibm,secure-boot", &is_sb, + sizeof (is_sb), 0); + + /* ibm,secure-boot: + * 0 - disabled + * 1 - audit + * 2 - enforce + * 3 - enforce + OS-specific behaviour + * + * We only support enforce. + */ + if (rc >= 0 && is_sb >= 2) + grub_lockdown (); +} + grub_addr_t grub_modbase; void @@ -296,6 +321,8 @@ grub_machine_init (void) #else grub_install_get_time_ms (grub_rtc_get_time_ms); #endif + + grub_get_ieee1275_secure_boot (); } void diff --git a/include/grub/lockdown.h b/include/grub/lockdown.h index 40531fa823..ebfee4bf06 100644 --- a/include/grub/lockdown.h +++ b/include/grub/lockdown.h @@ -24,7 +24,8 @@ #define GRUB_LOCKDOWN_DISABLED 0 #define GRUB_LOCKDOWN_ENABLED 1 -#ifdef GRUB_MACHINE_EFI +#if defined(GRUB_MACHINE_EFI) || \ + (defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275)) extern void EXPORT_FUNC (grub_lockdown) (void); extern int From 9531565a65d6bd8ec5cdc476c8cd273e5ef751eb Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Wed, 14 Apr 2021 20:10:23 +1000 Subject: [PATCH 174/367] ieee1275: drop HEAP_MAX_ADDR, HEAP_MIN_SIZE HEAP_MAX_ADDR is confusing. Currently it is set to 32MB, except on ieee1275 on x86, where it is 64MB. There is a comment which purports to explain it: /* If possible, we will avoid claiming heap above this address, because it seems to cause relocation problems with OSes that link at 4 MiB */ This doesn't make a lot of sense when the constants are well above 4MB already. It was not always this way. Prior to commit 7b5d0fe4440c ("Increase heap limit") in 2010, HEAP_MAX_SIZE and HEAP_MAX_ADDR were indeed 4MB. However, when the constants were increased the comment was left unchanged. It's been over a decade. It doesn't seem like we have problems with claims over 4MB on powerpc or x86 ieee1275. (sparc does things completely differently and never used the constant.) Drop the constant and the check. The only use of HEAP_MIN_SIZE was to potentially override the HEAP_MAX_ADDR check. It is now unused. Remove it. Signed-off-by: Daniel Axtens --- grub-core/kern/ieee1275/init.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index fc7d971272..0dcd114ce5 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -46,9 +46,6 @@ #endif #include -/* The minimal heap size we can live with. */ -#define HEAP_MIN_SIZE (unsigned long) (2 * 1024 * 1024) - /* The maximum heap size we're going to claim */ #ifdef __i386__ #define HEAP_MAX_SIZE (unsigned long) (64 * 1024 * 1024) @@ -56,14 +53,6 @@ #define HEAP_MAX_SIZE (unsigned long) (32 * 1024 * 1024) #endif -/* If possible, we will avoid claiming heap above this address, because it - seems to cause relocation problems with OSes that link at 4 MiB */ -#ifdef __i386__ -#define HEAP_MAX_ADDR (unsigned long) (64 * 1024 * 1024) -#else -#define HEAP_MAX_ADDR (unsigned long) (32 * 1024 * 1024) -#endif - extern char _end[]; #ifdef __sparc__ @@ -185,12 +174,6 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, if (*total + len > HEAP_MAX_SIZE) len = HEAP_MAX_SIZE - *total; - /* Avoid claiming anything above HEAP_MAX_ADDR, if possible. */ - if ((addr < HEAP_MAX_ADDR) && /* if it's too late, don't bother */ - (addr + len > HEAP_MAX_ADDR) && /* if it wasn't available anyway, don't bother */ - (*total + (HEAP_MAX_ADDR - addr) > HEAP_MIN_SIZE)) /* only limit ourselves when we can afford to */ - len = HEAP_MAX_ADDR - addr; - /* In theory, firmware should already prevent this from happening by not listing our own image in /memory/available. The check below is intended as a safeguard in case that doesn't happen. However, it doesn't protect From 74774aff49a0d2744fc7d6a5ce59666009bb05ec Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Sat, 8 May 2021 02:27:58 +0200 Subject: [PATCH 175/367] appendedsig/x509: Also handle the Extended Key Usage extension Red Hat certificates have both Key Usage and Extended Key Usage extensions present, but the appended signatures x509 parser doesn't handle the latter and so buils due finding an unrecognised critical extension: Error loading initial key: ../../grub-core/commands/appendedsig/x509.c:780:Unhandled critical x509 extension with OID 2.5.29.37 Fix this by also parsing the Extended Key Usage extension and handle it by verifying that the certificate has a single purpose, that is code signing. Signed-off-by: Javier Martinez Canillas Signed-off-by: Daniel Axtens --- grub-core/commands/appendedsig/x509.c | 94 ++++++++++++++++++++++- grub-core/tests/appended_signature_test.c | 29 ++++++- grub-core/tests/appended_signatures.h | 81 +++++++++++++++++++ 3 files changed, 201 insertions(+), 3 deletions(-) diff --git a/grub-core/commands/appendedsig/x509.c b/grub-core/commands/appendedsig/x509.c index 2b38b3670a..42ec65c54a 100644 --- a/grub-core/commands/appendedsig/x509.c +++ b/grub-core/commands/appendedsig/x509.c @@ -47,6 +47,12 @@ const char *keyUsage_oid = "2.5.29.15"; */ const char *basicConstraints_oid = "2.5.29.19"; +/* + * RFC 5280 4.2.1.12 Extended Key Usage + */ +const char *extendedKeyUsage_oid = "2.5.29.37"; +const char *codeSigningUsage_oid = "1.3.6.1.5.5.7.3.3"; + /* * RFC 3279 2.3.1 * @@ -637,6 +643,77 @@ verify_basic_constraints (grub_uint8_t * value, int value_size) return err; } +/* + * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + * + * KeyPurposeId ::= OBJECT IDENTIFIER + */ +static grub_err_t +verify_extended_key_usage (grub_uint8_t * value, int value_size) +{ + asn1_node extendedasn; + int result, count; + grub_err_t err = GRUB_ERR_NONE; + char usage[MAX_OID_LEN]; + int usage_size = sizeof (usage); + + result = + asn1_create_element (_gnutls_pkix_asn, "PKIX1.ExtKeyUsageSyntax", + &extendedasn); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not create ASN.1 structure for Extended Key Usage"); + } + + result = asn1_der_decoding2 (&extendedasn, value, &value_size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error parsing DER for Extended Key Usage: %s", + asn1_error); + goto cleanup; + } + + /* + * If EKUs are present, there must be exactly 1 and it must be a + * codeSigning usage. + */ + result = asn1_number_of_elements(extendedasn, "", &count); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error counting number of Extended Key Usages: %s", + asn1_strerror (result)); + goto cleanup; + } + + result = asn1_read_value (extendedasn, "?1", usage, &usage_size); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading Extended Key Usage: %s", + asn1_strerror (result)); + goto cleanup; + } + + if (grub_strncmp (codeSigningUsage_oid, usage, usage_size) != 0) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Unexpected Extended Key Usage OID, got: %s", + usage); + goto cleanup; + } + +cleanup: + asn1_delete_structure (&extendedasn); + return err; +} /* * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension @@ -660,7 +737,7 @@ verify_extensions (asn1_node cert) { int result; int ext, num_extensions = 0; - int usage_present = 0, constraints_present = 0; + int usage_present = 0, constraints_present = 0, extended_usage_present = 0; char *oid_path, *critical_path, *value_path; char extnID[MAX_OID_LEN]; int extnID_size; @@ -754,6 +831,15 @@ verify_extensions (asn1_node cert) } constraints_present++; } + else if (grub_strncmp (extendedKeyUsage_oid, extnID, extnID_size) == 0) + { + err = verify_extended_key_usage (value, value_size); + if (err != GRUB_ERR_NONE) + { + goto cleanup_value; + } + extended_usage_present++; + } else if (grub_strncmp ("TRUE", critical, critical_size) == 0) { /* @@ -785,6 +871,12 @@ verify_extensions (asn1_node cert) "Unexpected number of basic constraints extensions - expected 1, got %d", constraints_present); } + if (extended_usage_present > 1) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Unexpected number of Extended Key Usage extensions - expected 0 or 1, got %d", + extended_usage_present); + } return GRUB_ERR_NONE; cleanup_value: diff --git a/grub-core/tests/appended_signature_test.c b/grub-core/tests/appended_signature_test.c index 88a485200d..dbba061662 100644 --- a/grub-core/tests/appended_signature_test.c +++ b/grub-core/tests/appended_signature_test.c @@ -111,6 +111,22 @@ static struct grub_procfs_entry certificate_printable_der_entry = { .get_contents = get_certificate_printable_der }; +static char * +get_certificate_eku_der (grub_size_t * sz) +{ + char *ret; + *sz = certificate_eku_der_len; + ret = grub_malloc (*sz); + if (ret) + grub_memcpy (ret, certificate_eku_der, *sz); + return ret; +} + +static struct grub_procfs_entry certificate_eku_der_entry = { + .name = "certificate_eku.der", + .get_contents = get_certificate_eku_der +}; + static void do_verify (const char *f, int is_valid) @@ -149,6 +165,7 @@ appended_signature_test (void) char *trust_args2[] = { (char *) "(proc)/certificate2.der", NULL }; char *trust_args_printable[] = { (char *) "(proc)/certificate_printable.der", NULL }; + char *trust_args_eku[] = { (char *) "(proc)/certificate_eku.der", NULL }; char *distrust_args[] = { (char *) "1", NULL }; char *distrust2_args[] = { (char *) "2", NULL }; grub_err_t err; @@ -157,6 +174,7 @@ appended_signature_test (void) grub_procfs_register ("certificate2.der", &certificate2_der_entry); grub_procfs_register ("certificate_printable.der", &certificate_printable_der_entry); + grub_procfs_register ("certificate_eku.der", &certificate_eku_der_entry); cmd_trust = grub_command_find ("trust_certificate"); if (!cmd_trust) @@ -266,16 +284,23 @@ appended_signature_test (void) /* * Lastly, check a certificate that uses printableString rather than - * utf8String loads properly. + * utf8String loads properly, and that a certificate with an appropriate + * extended key usage loads. */ err = (cmd_trust->func) (cmd_trust, 1, trust_args_printable); grub_test_assert (err == GRUB_ERR_NONE, - "distrusting printable certificate failed: %d: %s", + "trusting printable certificate failed: %d: %s", + grub_errno, grub_errmsg); + + err = (cmd_trust->func) (cmd_trust, 1, trust_args_eku); + grub_test_assert (err == GRUB_ERR_NONE, + "trusting certificate with extended key usage failed: %d: %s", grub_errno, grub_errmsg); grub_procfs_unregister (&certificate_der_entry); grub_procfs_unregister (&certificate2_der_entry); grub_procfs_unregister (&certificate_printable_der_entry); + grub_procfs_unregister (&certificate_eku_der_entry); } GRUB_FUNCTIONAL_TEST (appended_signature_test, appended_signature_test); diff --git a/grub-core/tests/appended_signatures.h b/grub-core/tests/appended_signatures.h index aa3dc6278e..2e5ebd7d8b 100644 --- a/grub-core/tests/appended_signatures.h +++ b/grub-core/tests/appended_signatures.h @@ -555,3 +555,84 @@ unsigned char certificate_printable_der[] = { 0xd2 }; unsigned int certificate_printable_der_len = 829; + +unsigned char certificate_eku_der[] = { + 0x30, 0x82, 0x03, 0x90, 0x30, 0x82, 0x02, 0x78, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0xd3, 0x9c, 0x41, 0x33, 0xdd, 0x6b, 0x5f, 0x45, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x30, 0x47, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0c, 0x18, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, + 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, + 0x43, 0x41, 0x20, 0x36, 0x31, 0x22, 0x30, 0x20, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x13, 0x73, 0x65, 0x63, + 0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65, 0x64, 0x68, 0x61, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x32, + 0x31, 0x35, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a, 0x17, 0x0d, 0x33, + 0x38, 0x30, 0x31, 0x31, 0x37, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a, + 0x30, 0x4e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x1f, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, 0x53, 0x65, 0x63, + 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, 0x53, 0x69, 0x67, + 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x36, 0x30, 0x32, 0x31, 0x22, 0x30, 0x20, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, + 0x13, 0x73, 0x65, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65, + 0x64, 0x68, 0x61, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xaa, 0x6f, 0xbb, 0x92, 0x77, 0xd7, 0x15, + 0xef, 0x88, 0x80, 0x88, 0xc0, 0xe7, 0x89, 0xeb, 0x35, 0x76, 0xf4, 0x85, + 0x05, 0x0f, 0x19, 0xe4, 0x5f, 0x25, 0xdd, 0xc1, 0xa2, 0xe5, 0x5c, 0x06, + 0xfb, 0xf1, 0x06, 0xb5, 0x65, 0x45, 0xcb, 0xbd, 0x19, 0x33, 0x54, 0xb5, + 0x1a, 0xcd, 0xe4, 0xa8, 0x35, 0x2a, 0xfe, 0x9c, 0x53, 0xf4, 0xc6, 0x76, + 0xdb, 0x1f, 0x8a, 0xd4, 0x7b, 0x18, 0x11, 0xaf, 0xa3, 0x90, 0xd4, 0xdd, + 0x4d, 0xd5, 0x42, 0xcc, 0x14, 0x9a, 0x64, 0x6b, 0xc0, 0x7f, 0xaa, 0x1c, + 0x94, 0x47, 0x4d, 0x79, 0xbd, 0x57, 0x9a, 0xbf, 0x99, 0x4e, 0x96, 0xa9, + 0x31, 0x2c, 0xa9, 0xe7, 0x14, 0x65, 0x86, 0xc8, 0xac, 0x79, 0x5e, 0x78, + 0xa4, 0x3c, 0x00, 0x24, 0xd3, 0xf7, 0xe1, 0xf5, 0x12, 0xad, 0xa0, 0x29, + 0xe5, 0xfe, 0x80, 0xae, 0xf8, 0xaa, 0x60, 0x36, 0xe7, 0xe8, 0x94, 0xcb, + 0xe9, 0xd1, 0xcc, 0x0b, 0x4d, 0xf7, 0xde, 0xeb, 0x52, 0xd2, 0x73, 0x09, + 0x28, 0xdf, 0x48, 0x99, 0x53, 0x9f, 0xc5, 0x9a, 0xd4, 0x36, 0xa3, 0xc6, + 0x5e, 0x8d, 0xbe, 0xd5, 0xdc, 0x76, 0xb4, 0x74, 0xb8, 0x26, 0x18, 0x27, + 0xfb, 0xf2, 0xfb, 0xd0, 0x9b, 0x3d, 0x7f, 0x10, 0xe2, 0xab, 0x44, 0xc7, + 0x88, 0x7f, 0xb4, 0x3d, 0x3e, 0xa3, 0xff, 0x6d, 0x06, 0x4b, 0x3e, 0x55, + 0xb2, 0x84, 0xf4, 0xad, 0x54, 0x88, 0x81, 0xc3, 0x9c, 0xf8, 0xb6, 0x68, + 0x96, 0x38, 0x8b, 0xcd, 0x90, 0x6d, 0x25, 0x4b, 0xbf, 0x0c, 0x44, 0x90, + 0xa5, 0x5b, 0x98, 0xd0, 0x40, 0x2f, 0xbb, 0x0d, 0xa8, 0x4b, 0x8a, 0x62, + 0x82, 0x46, 0x46, 0x18, 0x38, 0xae, 0x82, 0x07, 0xd0, 0xb4, 0x2f, 0x16, + 0x79, 0x55, 0x9f, 0x1b, 0xc5, 0x08, 0x6d, 0x85, 0xdf, 0x3f, 0xa9, 0x9b, + 0x4b, 0xc6, 0x28, 0xd3, 0x58, 0x72, 0x3d, 0x37, 0x11, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x78, 0x30, 0x76, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0e, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, + 0x30, 0x16, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x01, 0x01, 0xff, 0x04, 0x0c, + 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03, + 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x6c, + 0xe4, 0x6c, 0x27, 0xaa, 0xcd, 0x0d, 0x4b, 0x74, 0x21, 0xa4, 0xf6, 0x5f, + 0x87, 0xb5, 0x31, 0xfe, 0x10, 0xbb, 0xa7, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe8, 0x6a, 0x1c, 0xab, + 0x2c, 0x48, 0xf9, 0x60, 0x36, 0xa2, 0xf0, 0x7b, 0x8e, 0xd2, 0x9d, 0xb4, + 0x2a, 0x28, 0x98, 0xc8, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0x55, 0x34, 0xe2, 0xfa, 0xf6, 0x89, 0x86, 0xad, 0x92, 0x21, 0xec, 0xb9, + 0x54, 0x0e, 0x18, 0x47, 0x0d, 0x1b, 0xa7, 0x58, 0xad, 0x69, 0xe4, 0xef, + 0x3b, 0xe6, 0x8d, 0xdd, 0xda, 0x0c, 0x45, 0xf6, 0xe8, 0x96, 0xa4, 0x29, + 0x0f, 0xbb, 0xcf, 0x16, 0xae, 0x93, 0xd0, 0xcb, 0x2a, 0x26, 0x1a, 0x7b, + 0xfc, 0x51, 0x22, 0x76, 0x98, 0x31, 0xa7, 0x0f, 0x29, 0x35, 0x79, 0xbf, + 0xe2, 0x4f, 0x0f, 0x14, 0xf5, 0x1f, 0xcb, 0xbf, 0x87, 0x65, 0x13, 0x32, + 0xa3, 0x19, 0x4a, 0xd1, 0x3f, 0x45, 0xd4, 0x4b, 0xe2, 0x00, 0x26, 0xa9, + 0x3e, 0xd7, 0xa5, 0x37, 0x9f, 0xf5, 0xad, 0x61, 0xe2, 0x40, 0xa9, 0x74, + 0x24, 0x53, 0xf2, 0x78, 0xeb, 0x10, 0x9b, 0x2c, 0x27, 0x88, 0x46, 0xcb, + 0xe4, 0x60, 0xca, 0xf5, 0x06, 0x24, 0x40, 0x2a, 0x97, 0x3a, 0xcc, 0xd0, + 0x81, 0xb1, 0x15, 0xa3, 0x4f, 0xd0, 0x2b, 0x4f, 0xca, 0x6e, 0xaa, 0x24, + 0x31, 0xb3, 0xac, 0xa6, 0x75, 0x05, 0xfe, 0x8a, 0xf4, 0x41, 0xc4, 0x06, + 0x8a, 0xc7, 0x0a, 0x83, 0x4e, 0x49, 0xd4, 0x3f, 0x83, 0x50, 0xec, 0x57, + 0x04, 0x97, 0x14, 0x49, 0xf5, 0xe1, 0xb1, 0x7a, 0x9c, 0x09, 0x4f, 0x61, + 0x87, 0xc3, 0x97, 0x22, 0x17, 0xc2, 0xeb, 0xcc, 0x32, 0x81, 0x31, 0x21, + 0x3f, 0x10, 0x57, 0x5b, 0x43, 0xbe, 0xcd, 0x68, 0x82, 0xbe, 0xe5, 0xc1, + 0x65, 0x94, 0x7e, 0xc2, 0x34, 0x76, 0x2b, 0xcf, 0x89, 0x3c, 0x2b, 0x81, + 0x23, 0x72, 0x95, 0xcf, 0xc9, 0x67, 0x19, 0x2a, 0xd5, 0x5c, 0xca, 0xa3, + 0x46, 0xbd, 0x48, 0x06, 0x0b, 0xa6, 0xa3, 0x96, 0x50, 0x28, 0xc7, 0x7e, + 0xcf, 0x62, 0xf2, 0xfa, 0xc4, 0xf2, 0x53, 0xe3, 0xc9, 0xe8, 0x2e, 0xdd, + 0x29, 0x37, 0x07, 0x47, 0xff, 0xff, 0x8a, 0x32, 0xbd, 0xa2, 0xb7, 0x21, + 0x89, 0xa0, 0x55, 0xf7 +}; +unsigned int certificate_eku_der_len = 916; From 7baa6fd4f8a2134535505b922b4d4b62c9a2070a Mon Sep 17 00:00:00 2001 From: Diego Domingos Date: Wed, 10 Mar 2021 14:17:52 -0500 Subject: [PATCH 176/367] ieee1275/ofdisk: retry on open failure This patch aims to make grub more robust when booting from SAN/Multipath disks. If a path is failing intermittently so grub will retry the OPEN and READ the disk (grub_ieee1275_open and grub_ieee1275_read) until the total amount of times specified in MAX_RETRIES. Signed-off-by: Diego Domingos --- grub-core/disk/ieee1275/ofdisk.c | 25 ++++++++++++++++++++----- include/grub/ieee1275/ofdisk.h | 8 ++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/grub-core/disk/ieee1275/ofdisk.c b/grub-core/disk/ieee1275/ofdisk.c index ea7f78ac7d..55346849d3 100644 --- a/grub-core/disk/ieee1275/ofdisk.c +++ b/grub-core/disk/ieee1275/ofdisk.c @@ -225,7 +225,9 @@ dev_iterate (const struct grub_ieee1275_devalias *alias) char *buf, *bufptr; unsigned i; - if (grub_ieee1275_open (alias->path, &ihandle)) + + RETRY_IEEE1275_OFDISK_OPEN(alias->path, &ihandle) + if (! ihandle) return; /* This method doesn't need memory allocation for the table. Open @@ -305,7 +307,9 @@ dev_iterate (const struct grub_ieee1275_devalias *alias) return; } - if (grub_ieee1275_open (alias->path, &ihandle)) + RETRY_IEEE1275_OFDISK_OPEN(alias->path, &ihandle); + + if (! ihandle) { grub_free (buf); grub_free (table); @@ -495,7 +499,7 @@ grub_ofdisk_open (const char *name, grub_disk_t disk) last_ihandle = 0; last_devpath = NULL; - grub_ieee1275_open (op->open_path, &last_ihandle); + RETRY_IEEE1275_OFDISK_OPEN(op->open_path, &last_ihandle); if (! last_ihandle) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); last_devpath = op->open_path; @@ -571,7 +575,7 @@ grub_ofdisk_prepare (grub_disk_t disk, grub_disk_addr_t sector) last_ihandle = 0; last_devpath = NULL; - grub_ieee1275_open (disk->data, &last_ihandle); + RETRY_IEEE1275_OFDISK_OPEN(disk->data, &last_ihandle); if (! last_ihandle) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); last_devpath = disk->data; @@ -598,12 +602,23 @@ grub_ofdisk_read (grub_disk_t disk, grub_disk_addr_t sector, return err; grub_ieee1275_read (last_ihandle, buf, size << disk->log_sector_size, &actual); - if (actual != (grub_ssize_t) (size << disk->log_sector_size)) + int i = 0; + while(actual != (grub_ssize_t) (size << disk->log_sector_size)){ + if (i>MAX_RETRIES){ return grub_error (GRUB_ERR_READ_ERROR, N_("failure reading sector 0x%llx " "from `%s'"), (unsigned long long) sector, disk->name); + } + last_devpath = NULL; + err = grub_ofdisk_prepare (disk, sector); + if (err) + return err; + grub_ieee1275_read (last_ihandle, buf, size << disk->log_sector_size, + &actual); + i++; + } return 0; } diff --git a/include/grub/ieee1275/ofdisk.h b/include/grub/ieee1275/ofdisk.h index 2f69e3f191..7d2d540930 100644 --- a/include/grub/ieee1275/ofdisk.h +++ b/include/grub/ieee1275/ofdisk.h @@ -22,4 +22,12 @@ extern void grub_ofdisk_init (void); extern void grub_ofdisk_fini (void); +#define MAX_RETRIES 20 + + +#define RETRY_IEEE1275_OFDISK_OPEN(device, last_ihandle) unsigned retry_i=0;for(retry_i=0; retry_i < MAX_RETRIES; retry_i++){ \ + if(!grub_ieee1275_open(device, last_ihandle)) \ + break; \ + grub_dprintf("ofdisk","Opening disk %s failed. Retrying...\n",device); } + #endif /* ! GRUB_INIT_HEADER */ From 2051b97df2e75b1e10f63fd6deb25f152b8e9d43 Mon Sep 17 00:00:00 2001 From: Dimitri John Ledkov Date: Fri, 11 Jun 2021 13:51:20 +0200 Subject: [PATCH 177/367] Allow chainloading EFI apps from loop mounts. Signed-off-by: Dimitri John Ledkov Signed-off-by: Robbie Harwood --- grub-core/disk/loopback.c | 9 +-------- grub-core/loader/efi/chainloader.c | 23 +++++++++++++++++++++++ include/grub/loopback.h | 30 ++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 include/grub/loopback.h diff --git a/grub-core/disk/loopback.c b/grub-core/disk/loopback.c index 41bebd14fe..99f47924ec 100644 --- a/grub-core/disk/loopback.c +++ b/grub-core/disk/loopback.c @@ -21,20 +21,13 @@ #include #include #include +#include #include #include #include GRUB_MOD_LICENSE ("GPLv3+"); -struct grub_loopback -{ - char *devname; - grub_file_t file; - struct grub_loopback *next; - unsigned long id; -}; - static struct grub_loopback *loopback_list; static unsigned long last_id = 0; diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index d41e8ea14a..3af6b12292 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -901,6 +902,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_efi_status_t status; grub_efi_boot_services_t *b; grub_device_t dev = 0; + grub_device_t orig_dev = 0; grub_efi_device_path_t *dp = 0; char *filename; void *boot_image = 0; @@ -958,6 +960,15 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), if (! dev) goto fail; + /* if device is loopback, use underlying dev */ + if (dev->disk->dev->id == GRUB_DISK_DEVICE_LOOPBACK_ID) + { + struct grub_loopback *d; + orig_dev = dev; + d = dev->disk->data; + dev = d->file->device; + } + if (dev->disk) dev_handle = grub_efidisk_get_device_handle (dev->disk); else if (dev->net && dev->net->server) @@ -1065,6 +1076,12 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), } #endif + if (orig_dev) + { + dev = orig_dev; + orig_dev = 0; + } + rc = grub_linuxefi_secure_validate((void *)(unsigned long)address, fsize); grub_dprintf ("chain", "linuxefi_secure_validate: %d\n", rc); if (rc > 0) @@ -1087,6 +1104,12 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), // -1 fall-through to fail fail: + if (orig_dev) + { + dev = orig_dev; + orig_dev = 0; + } + if (dev) grub_device_close (dev); diff --git a/include/grub/loopback.h b/include/grub/loopback.h new file mode 100644 index 0000000000..3b9a9e32e8 --- /dev/null +++ b/include/grub/loopback.h @@ -0,0 +1,30 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2019 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_LOOPBACK_HEADER +#define GRUB_LOOPBACK_HEADER 1 + +struct grub_loopback +{ + char *devname; + grub_file_t file; + struct grub_loopback *next; + unsigned long id; +}; + +#endif /* ! GRUB_LOOPBACK_HEADER */ From 1794ae0ca23180a950d4cc1739e593a3dd4789bf Mon Sep 17 00:00:00 2001 From: Ian Page Hands Date: Tue, 8 Jun 2021 13:48:56 -0400 Subject: [PATCH 178/367] efinet: Add DHCP proxy support If a proxyDHCP configuration is used, the server name, server IP and boot file values should be taken from the DHCP proxy offer instead of the DHCP server ack packet. Currently that case is not handled, add support for it. Signed-off-by: Ian Page Hands Signed-off-by: Robbie Harwood --- grub-core/net/drivers/efi/efinet.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c index e11d759f19..1a24f38a21 100644 --- a/grub-core/net/drivers/efi/efinet.c +++ b/grub-core/net/drivers/efi/efinet.c @@ -850,10 +850,31 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, else { grub_dprintf ("efinet", "using ipv4 and dhcp\n"); + + struct grub_net_bootp_packet *dhcp_ack = &pxe_mode->dhcp_ack; + + if (pxe_mode->proxy_offer_received) + { + grub_dprintf ("efinet", "proxy offer receive"); + struct grub_net_bootp_packet *proxy_offer = &pxe_mode->proxy_offer; + + if (proxy_offer && dhcp_ack->boot_file[0] == '\0') + { + grub_dprintf ("efinet", "setting values from proxy offer"); + /* Here we got a proxy offer and the dhcp_ack has a nil boot_file + * Copy the proxy DHCP offer details into the bootp_packet we are + * sending forward as they are the deatils we need. + */ + *dhcp_ack->server_name = *proxy_offer->server_name; + *dhcp_ack->boot_file = *proxy_offer->boot_file; + dhcp_ack->server_ip = proxy_offer->server_ip; + } + } + grub_net_configure_by_dhcp_ack (card->name, card, 0, (struct grub_net_bootp_packet *) - packet_buf, - packet_bufsz, + &pxe_mode->dhcp_ack, + sizeof (pxe_mode->dhcp_ack), 1, device, path); grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path); } From 6e677b7dcd740bc259c3ccc20b623a7821aa72f3 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 11 Jun 2021 00:01:29 +0200 Subject: [PATCH 179/367] fs/ext2: Ignore checksum seed incompat feature This incompat feature is used to denote that the filesystem stored its metadata checksum seed in the superblock. This is used to allow tune2fs to change the UUID on a mounted metadata_csum filesystem without having to rewrite all the disk metadata. But GRUB doesn't use the metadata checksum in anyway, so can just ignore this feature if is enabled. This is consistent with GRUB filesystem code in general which just does a best effort to access the filesystem's data. It may be removed from the ignored list in the future if supports to do metadata checksumming verification is added to the read-only FS driver. Suggested-by: Eric Sandeen Suggested-by: Lukas Czerner Signed-off-by: Javier Martinez Canillas --- grub-core/fs/ext2.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/ext2.c b/grub-core/fs/ext2.c index e7dd78e663..731d346f88 100644 --- a/grub-core/fs/ext2.c +++ b/grub-core/fs/ext2.c @@ -103,6 +103,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 #define EXT4_FEATURE_INCOMPAT_MMP 0x0100 #define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 +#define EXT4_FEATURE_INCOMPAT_CSUM_SEED 0x2000 #define EXT4_FEATURE_INCOMPAT_ENCRYPT 0x10000 /* The set of back-incompatible features this driver DOES support. Add (OR) @@ -123,9 +124,16 @@ GRUB_MOD_LICENSE ("GPLv3+"); * mmp: Not really back-incompatible - was added as such to * avoid multiple read-write mounts. Safe to ignore for this * RO driver. + * checksum seed: Not really back-incompatible - was added to allow tools + * such as tune2fs to change the UUID on a mounted metadata + * checksummed filesystem. Safe to ignore for now since the + * driver doesn't support checksum verification. But it must + * be removed from this list if that support is added later. + * */ #define EXT2_DRIVER_IGNORED_INCOMPAT ( EXT3_FEATURE_INCOMPAT_RECOVER \ - | EXT4_FEATURE_INCOMPAT_MMP) + | EXT4_FEATURE_INCOMPAT_MMP \ + | EXT4_FEATURE_INCOMPAT_CSUM_SEED) #define EXT3_JOURNAL_MAGIC_NUMBER 0xc03b3998U From 96d376f88c4213a8f698aa8dbc5614ec7ca37586 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 17 Jun 2021 14:31:42 +0200 Subject: [PATCH 180/367] Don't update the cmdline when generating legacy menuentry commands On OPAL ppc64le machines with an old petitboot version that doesn't have support to parse BLS snippets, the grub2-mkconfig script is executed to generate menuentry commands from the BLS snippets. In this case, the script is executed with the --no-grubenv-update option that indicates that no side effects should happen when running the script. But the options field in the BLS snippets are updated regardless, only do the update if --no-grubenv-update was not used. Signed-off-by: Javier Martinez Canillas --- util/grub.d/10_linux.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index e490e1a43a..865af3d6c4 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -256,7 +256,9 @@ if [ -z "\${kernelopts}" ]; then fi EOF - update_bls_cmdline + if [ "x${GRUB_GRUBENV_UPDATE}" = "xyes" ]; then + update_bls_cmdline + fi if [ "x${BLS_POPULATE_MENU}" = "xtrue" ]; then populate_menu From ebffb6e321acd8724f6d00f1487d7f73a9787d12 Mon Sep 17 00:00:00 2001 From: Paulo Flabiano Smorigo Date: Tue, 29 Jun 2021 13:17:42 +0200 Subject: [PATCH 181/367] Suppress gettext error message Colin Watson's patch from comment #11 on the upstream bug: https://savannah.gnu.org/bugs/?35880#comment11 Resolves: rhbz#1592124 Signed-off-by: Paulo Flabiano Smorigo --- grub-core/gettext/gettext.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/grub-core/gettext/gettext.c b/grub-core/gettext/gettext.c index 4d02e62c10..7ec81ca0b4 100644 --- a/grub-core/gettext/gettext.c +++ b/grub-core/gettext/gettext.c @@ -424,6 +424,13 @@ grub_gettext_init_ext (struct grub_gettext_context *ctx, grub_free (lang); } + /* If no translations are available, fall back to untranslated text. */ + if (err == GRUB_ERR_FILE_NOT_FOUND) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (locale[0] == 'e' && locale[1] == 'n' && (locale[2] == '\0' || locale[2] == '_')) grub_errno = err = GRUB_ERR_NONE; From 72393411dc9df8de57564cdf353b2bc03aec4dca Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 5 Jul 2021 18:24:22 +0200 Subject: [PATCH 182/367] grub-set-password: Always use /boot/grub2/user.cfg as password default The GRUB configuration file is always placed in /boot/grub2/ now, even for EFI. But the tool is still creating the user.cfg in the ESP and not there. Resolves: rhbz#1955294 Signed-off-by: Javier Martinez Canillas --- util/grub-set-password.in | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/util/grub-set-password.in b/util/grub-set-password.in index c0b5ebbfdc..d8005e5a14 100644 --- a/util/grub-set-password.in +++ b/util/grub-set-password.in @@ -1,11 +1,6 @@ #!/bin/sh -e -EFIDIR=$(grep ^ID= /etc/os-release | sed -e 's/^ID=//' -e 's/rhel/redhat/' -e 's/\"//g') -if [ -d /sys/firmware/efi/efivars/ ]; then - grubdir=`echo "/@bootdirname@/efi/EFI/${EFIDIR}/" | sed 's,//*,/,g'` -else - grubdir=`echo "/@bootdirname@/@grubdirname@" | sed 's,//*,/,g'` -fi +grubdir=`echo "/@bootdirname@/@grubdirname@" | sed 's,//*,/,g'` PACKAGE_VERSION="@PACKAGE_VERSION@" PACKAGE_NAME="@PACKAGE_NAME@" @@ -116,8 +111,6 @@ if [ -z "${MYPASS}" ]; then exit 1 fi -# on the ESP, these will fail to set the permissions, but it's okay because -# the directory is protected. install -m 0600 /dev/null "${OUTPUT_PATH}/user.cfg" 2>/dev/null || : chmod 0600 "${OUTPUT_PATH}/user.cfg" 2>/dev/null || : echo "GRUB2_PASSWORD=${MYPASS}" > "${OUTPUT_PATH}/user.cfg" From 2ea932939de526367aece8a4f3718d773500af02 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 6 Jul 2021 00:38:40 +0200 Subject: [PATCH 183/367] templates: Check for EFI at runtime instead of config generation time The 30_uefi-firmware template checks if an OsIndicationsSupported UEFI var exists and EFI_OS_INDICATIONS_BOOT_TO_FW_UI bit is set, to decide whether a "fwsetup" menu entry would be added or not to the GRUB menu. But this has the problem that it will only work if the configuration file was created on an UEFI machine that supports booting to a firmware UI. This for example doesn't support creating GRUB config files when executing on systems that support both UEFI and legacy BIOS booting. Since creating the config file from legacy BIOS wouldn't allow to access the firmware UI. To prevent this, make the template to unconditionally create the grub.cfg snippet but check at runtime if was booted through UEFI to decide if this entry should be added. That way it won't be added when booting with BIOS. There's no need to check if EFI_OS_INDICATIONS_BOOT_TO_FW_UI bit is set, since that's already done by the "fwsetup" command when is executed. Resolves: rhbz#1823864 Signed-off-by: Javier Martinez Canillas --- util/grub.d/30_uefi-firmware.in | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/util/grub.d/30_uefi-firmware.in b/util/grub.d/30_uefi-firmware.in index d344d3883d..b6041b55e2 100644 --- a/util/grub.d/30_uefi-firmware.in +++ b/util/grub.d/30_uefi-firmware.in @@ -26,19 +26,14 @@ export TEXTDOMAINDIR="@localedir@" . "$pkgdatadir/grub-mkconfig_lib" -EFI_VARS_DIR=/sys/firmware/efi/efivars -EFI_GLOBAL_VARIABLE=8be4df61-93ca-11d2-aa0d-00e098032b8c -OS_INDICATIONS="$EFI_VARS_DIR/OsIndicationsSupported-$EFI_GLOBAL_VARIABLE" +LABEL="UEFI Firmware Settings" -if [ -e "$OS_INDICATIONS" ] && \ - [ "$(( $(printf 0x%x \'"$(cat $OS_INDICATIONS | cut -b5)"\') & 1 ))" = 1 ]; then - LABEL="UEFI Firmware Settings" +gettext_printf "Adding boot menu entry for UEFI Firmware Settings ...\n" >&2 - gettext_printf "Adding boot menu entry for UEFI Firmware Settings ...\n" >&2 - - cat << EOF -menuentry '$LABEL' \$menuentry_id_option 'uefi-firmware' { - fwsetup -} -EOF +cat << EOF +if [ "\$grub_platform" = "efi" ]; then + menuentry '$LABEL' \$menuentry_id_option 'uefi-firmware' { + fwsetup + } fi +EOF From 7966a53ce9890b84cb8d11054b56cc06685213cc Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 6 Jul 2021 01:10:18 +0200 Subject: [PATCH 184/367] efi: Print an error if boot to firmware setup is not supported The "fwsetup" command is only registered if the firmware supports booting to the firmware setup UI. But it could be possible that the GRUB config already contains a "fwsetup" entry, because it was generated in a machine that has support for this feature. To prevent users getting a "can't find command `fwsetup`" error if it is not supported by the firmware, let's just always register the command but print a more accurate message if the firmware doesn't support this option. Resolves: rhbz#1823864 Signed-off-by: Javier Martinez Canillas --- grub-core/commands/efi/efifwsetup.c | 43 +++++++++++++++-------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/grub-core/commands/efi/efifwsetup.c b/grub-core/commands/efi/efifwsetup.c index eaca032838..328c45e82e 100644 --- a/grub-core/commands/efi/efifwsetup.c +++ b/grub-core/commands/efi/efifwsetup.c @@ -27,6 +27,25 @@ GRUB_MOD_LICENSE ("GPLv3+"); +static grub_efi_boolean_t +efifwsetup_is_supported (void) +{ + grub_efi_uint64_t *os_indications_supported = NULL; + grub_size_t oi_size = 0; + grub_efi_guid_t global = GRUB_EFI_GLOBAL_VARIABLE_GUID; + + grub_efi_get_variable ("OsIndicationsSupported", &global, &oi_size, + (void **) &os_indications_supported); + + if (!os_indications_supported) + return 0; + + if (*os_indications_supported & GRUB_EFI_OS_INDICATIONS_BOOT_TO_FW_UI) + return 1; + + return 0; +} + static grub_err_t grub_cmd_fwsetup (grub_command_t cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), @@ -38,6 +57,10 @@ grub_cmd_fwsetup (grub_command_t cmd __attribute__ ((unused)), grub_size_t oi_size; grub_efi_guid_t global = GRUB_EFI_GLOBAL_VARIABLE_GUID; + if (!efifwsetup_is_supported ()) + return grub_error (GRUB_ERR_INVALID_COMMAND, + N_("Reboot to firmware setup is not supported")); + grub_efi_get_variable ("OsIndications", &global, &oi_size, (void **) &old_os_indications); @@ -56,28 +79,8 @@ grub_cmd_fwsetup (grub_command_t cmd __attribute__ ((unused)), static grub_command_t cmd = NULL; -static grub_efi_boolean_t -efifwsetup_is_supported (void) -{ - grub_efi_uint64_t *os_indications_supported = NULL; - grub_size_t oi_size = 0; - grub_efi_guid_t global = GRUB_EFI_GLOBAL_VARIABLE_GUID; - - grub_efi_get_variable ("OsIndicationsSupported", &global, &oi_size, - (void **) &os_indications_supported); - - if (!os_indications_supported) - return 0; - - if (*os_indications_supported & GRUB_EFI_OS_INDICATIONS_BOOT_TO_FW_UI) - return 1; - - return 0; -} - GRUB_MOD_INIT (efifwsetup) { - if (efifwsetup_is_supported ()) cmd = grub_register_command ("fwsetup", grub_cmd_fwsetup, NULL, N_("Reboot into firmware setup menu.")); From 24b32a62bb129cf2b06d79b98c1ce5901c36b576 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 2 Aug 2021 23:10:01 +1000 Subject: [PATCH 185/367] arm64: Fix EFI loader kernel image allocation We are currently allocating just enough memory for the file size, which means that the kernel BSS is in limbo (and not even zeroed). We are also not honoring the alignment specified in the image PE header. This makes us use the PE optional header in which the kernel puts the actual size it needs, including BSS, and make sure we clear it, and honors the specified alignment for the image. Signed-off-by: Benjamin Herrenschmidt [pjones: arm: check for the PE magic for the compiled arch] Signed-off-by: Peter Jones Signed-off-by: Robbie Harwood --- grub-core/loader/arm64/linux.c | 100 ++++++++++++++++++++++----------- include/grub/arm/linux.h | 1 + include/grub/arm64/linux.h | 1 + 3 files changed, 68 insertions(+), 34 deletions(-) diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c index 47f8cf0d84..f18d90bd74 100644 --- a/grub-core/loader/arm64/linux.c +++ b/grub-core/loader/arm64/linux.c @@ -41,6 +41,8 @@ GRUB_MOD_LICENSE ("GPLv3+"); static grub_dl_t my_mod; static int loaded; +static void *kernel_alloc_addr; +static grub_uint32_t kernel_alloc_pages; static void *kernel_addr; static grub_uint64_t kernel_size; static grub_uint32_t handover_offset; @@ -204,9 +206,8 @@ grub_linux_unload (void) GRUB_EFI_BYTES_TO_PAGES (initrd_end - initrd_start)); initrd_start = initrd_end = 0; grub_free (linux_args); - if (kernel_addr) - grub_efi_free_pages ((grub_addr_t) kernel_addr, - GRUB_EFI_BYTES_TO_PAGES (kernel_size)); + if (kernel_alloc_addr) + grub_efi_free_pages ((grub_addr_t) kernel_alloc_addr, kernel_alloc_pages); grub_fdt_unload (); return GRUB_ERR_NONE; } @@ -311,14 +312,35 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), return grub_errno; } +static grub_err_t +parse_pe_header (void *kernel, grub_uint64_t *total_size, + grub_uint32_t *entry_offset, + grub_uint32_t *alignment) +{ + struct linux_arch_kernel_header *lh = kernel; + struct grub_armxx_linux_pe_header *pe; + + pe = (void *)((unsigned long)kernel + lh->hdr_offset); + + if (pe->opt.magic != GRUB_PE32_PEXX_MAGIC) + return grub_error(GRUB_ERR_BAD_OS, "Invalid PE optional header magic"); + + *total_size = pe->opt.image_size; + *entry_offset = pe->opt.entry_addr; + *alignment = pe->opt.section_alignment; + + return GRUB_ERR_NONE; +} + static grub_err_t grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { grub_file_t file = 0; - struct linux_arch_kernel_header lh; - struct grub_armxx_linux_pe_header *pe; grub_err_t err; + grub_off_t filelen; + grub_uint32_t align; + void *kernel = NULL; int rc; grub_dl_ref (my_mod); @@ -333,40 +355,24 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), if (!file) goto fail; - kernel_size = grub_file_size (file); - - if (grub_file_read (file, &lh, sizeof (lh)) < (long) sizeof (lh)) - return grub_errno; - - if (grub_arch_efi_linux_check_image (&lh) != GRUB_ERR_NONE) - goto fail; - - grub_loader_unset(); - - grub_dprintf ("linux", "kernel file size: %lld\n", (long long) kernel_size); - kernel_addr = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (kernel_size)); - grub_dprintf ("linux", "kernel numpages: %lld\n", - (long long) GRUB_EFI_BYTES_TO_PAGES (kernel_size)); - if (!kernel_addr) + filelen = grub_file_size (file); + kernel = grub_malloc(filelen); + if (!kernel) { - grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel load buffer")); goto fail; } - grub_file_seek (file, 0); - if (grub_file_read (file, kernel_addr, kernel_size) - < (grub_int64_t) kernel_size) + if (grub_file_read (file, kernel, filelen) < (grub_ssize_t)filelen) { - if (!grub_errno) - grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]); + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"), + argv[0]); goto fail; } - grub_dprintf ("linux", "kernel @ %p\n", kernel_addr); - if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) { - rc = grub_linuxefi_secure_validate (kernel_addr, kernel_size); + rc = grub_linuxefi_secure_validate (kernel, filelen); if (rc <= 0) { grub_error (GRUB_ERR_INVALID_COMMAND, @@ -375,8 +381,32 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } } - pe = (void *)((unsigned long)kernel_addr + lh.hdr_offset); - handover_offset = pe->opt.entry_addr; + if (grub_arch_efi_linux_check_image (kernel) != GRUB_ERR_NONE) + goto fail; + if (parse_pe_header (kernel, &kernel_size, &handover_offset, &align) != GRUB_ERR_NONE) + goto fail; + grub_dprintf ("linux", "kernel mem size : %lld\n", (long long) kernel_size); + grub_dprintf ("linux", "kernel entry offset : %d\n", handover_offset); + grub_dprintf ("linux", "kernel alignment : 0x%x\n", align); + + grub_loader_unset(); + + kernel_alloc_pages = GRUB_EFI_BYTES_TO_PAGES (kernel_size + align - 1); + kernel_alloc_addr = grub_efi_allocate_any_pages (kernel_alloc_pages); + grub_dprintf ("linux", "kernel numpages: %d\n", kernel_alloc_pages); + if (!kernel_alloc_addr) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto fail; + } + kernel_addr = (void *)ALIGN_UP((grub_uint64_t)kernel_alloc_addr, align); + + grub_dprintf ("linux", "kernel @ %p\n", kernel_addr); + grub_memcpy (kernel_addr, kernel, grub_min(filelen, kernel_size)); + if (kernel_size > filelen) + grub_memset ((char *)kernel_addr + filelen, 0, kernel_size - filelen); + grub_free(kernel); + kernel = NULL; cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE); linux_args = grub_malloc (cmdline_size); @@ -400,6 +430,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } fail: + if (kernel) + grub_free (kernel); + if (file) grub_file_close (file); @@ -412,9 +445,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), if (linux_args && !loaded) grub_free (linux_args); - if (kernel_addr && !loaded) - grub_efi_free_pages ((grub_addr_t) kernel_addr, - GRUB_EFI_BYTES_TO_PAGES (kernel_size)); + if (kernel_alloc_addr && !loaded) + grub_efi_free_pages ((grub_addr_t) kernel_alloc_addr, kernel_alloc_pages); return grub_errno; } diff --git a/include/grub/arm/linux.h b/include/grub/arm/linux.h index b582f67f66..966a5074f5 100644 --- a/include/grub/arm/linux.h +++ b/include/grub/arm/linux.h @@ -44,6 +44,7 @@ struct grub_arm_linux_pe_header #if defined(__arm__) # define GRUB_LINUX_ARMXX_MAGIC_SIGNATURE GRUB_LINUX_ARM_MAGIC_SIGNATURE +# define GRUB_PE32_PEXX_MAGIC GRUB_PE32_PE32_MAGIC # define linux_arch_kernel_header linux_arm_kernel_header # define grub_armxx_linux_pe_header grub_arm_linux_pe_header #endif diff --git a/include/grub/arm64/linux.h b/include/grub/arm64/linux.h index ea030312df..422bf2bf24 100644 --- a/include/grub/arm64/linux.h +++ b/include/grub/arm64/linux.h @@ -48,6 +48,7 @@ struct grub_arm64_linux_pe_header #if defined(__aarch64__) # define GRUB_LINUX_ARMXX_MAGIC_SIGNATURE GRUB_LINUX_ARM64_MAGIC_SIGNATURE +# define GRUB_PE32_PEXX_MAGIC GRUB_PE32_PE64_MAGIC # define linux_arch_kernel_header linux_arm64_kernel_header # define grub_armxx_linux_pe_header grub_arm64_linux_pe_header #endif From 9fa2ef9af7c4bcdb45e8d112aa36037a46f2b3b3 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 30 Aug 2021 12:31:18 +0200 Subject: [PATCH 186/367] normal/main: Discover the device to read the config from as a fallback The GRUB core.img is generated locally, when this is done the grub2-probe tool figures out the device and partition that needs to be read to parse the GRUB configuration file. But in some cases the core.img can't be generated on the host and instead has to be done at package build time. For example, if needs to get signed with a key that's only available on the package building infrastructure. If that's the case, the prefix variable won't have a device and partition but only a directory path. So there's no way for GRUB to know from which device has to read the configuration file. To allow GRUB to continue working on that scenario, fallback to iterating over all the available devices, if reading the config failed when using the prefix and fw_path variables. Signed-off-by: Javier Martinez Canillas --- grub-core/normal/main.c | 58 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 7de9e4c36d..8f5fd81003 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -337,18 +337,13 @@ grub_enter_normal_mode (const char *config) } static grub_err_t -grub_try_normal (const char *variable) +grub_try_normal_prefix (const char *prefix) { char *config; - const char *prefix; grub_err_t err = GRUB_ERR_FILE_NOT_FOUND; const char *net_search_cfg; int disable_net_search = 0; - prefix = grub_env_get (variable); - if (!prefix) - return GRUB_ERR_FILE_NOT_FOUND; - net_search_cfg = grub_env_get ("feature_net_search_cfg"); if (net_search_cfg && net_search_cfg[0] == 'n') disable_net_search = 1; @@ -362,7 +357,7 @@ grub_try_normal (const char *variable) config = grub_malloc (config_len); if (! config) - return GRUB_ERR_FILE_NOT_FOUND; + return err; grub_snprintf (config, config_len, "%s/grub.cfg", prefix); err = grub_net_search_config_file (config); @@ -391,6 +386,53 @@ grub_try_normal (const char *variable) return err; } +static int +grub_try_normal_dev (const char *name, void *data) +{ + grub_err_t err; + const char *prefix = grub_xasprintf ("(%s)%s", name, (char *)data); + + if (!prefix) + return 0; + + err = grub_try_normal_prefix (prefix); + if (err == GRUB_ERR_NONE) + return 1; + + return 0; +} + +static grub_err_t +grub_try_normal_discover (void) +{ + char *prefix = grub_env_get ("prefix"); + grub_err_t err = GRUB_ERR_FILE_NOT_FOUND; + + if (!prefix) + return err; + + if (grub_device_iterate (grub_try_normal_dev, (void *)prefix)) + return GRUB_ERR_NONE; + + return err; +} + +static grub_err_t +grub_try_normal (const char *variable) +{ + grub_err_t err = GRUB_ERR_FILE_NOT_FOUND; + const char *prefix; + + if (!variable) + return err; + + prefix = grub_env_get (variable); + if (!prefix) + return err; + + return grub_try_normal_prefix (prefix); +} + /* Enter normal mode from rescue mode. */ static grub_err_t grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)), @@ -405,6 +447,8 @@ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)), err = grub_try_normal ("fw_path"); if (err == GRUB_ERR_FILE_NOT_FOUND) err = grub_try_normal ("prefix"); + if (err == GRUB_ERR_FILE_NOT_FOUND) + err = grub_try_normal_discover (); if (err == GRUB_ERR_FILE_NOT_FOUND) grub_enter_normal_mode (0); } From 8de4079522faf5a962c9581a7b78fb6332ec35c0 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Mon, 19 Jul 2021 14:35:55 +1000 Subject: [PATCH 187/367] powerpc: adjust setting of prefix for signed binary case On RHEL-signed powerpc grub, we sign a grub with -p /grub2 and expect that there's a boot partition. Unfortunately grub_set_prefix_and_root tries to convert this to ($fwdevice)/grub2. This ends up being (ieee1275/disk)/grub2 and that falls apart pretty quickly - there's no file-system on ieee1275/disk, and it makes the search routine try things like (ieee1275/disk,msdos2)(ieee1275/disk)/grub2 which also doesn't work. Detect if we would be about to create (ieee1275/disk)/path and don't: preserve a prefix of /path instead and hope the search later finds us. Related: rhbz#1899864 Signed-off-by: Daniel Axtens [rharwood@redhat.com: squash in fixup commit] Signed-off-by: Robbie Harwood --- grub-core/kern/main.c | 49 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c index b573be6650..3fc3401472 100644 --- a/grub-core/kern/main.c +++ b/grub-core/kern/main.c @@ -216,13 +216,52 @@ grub_set_prefix_and_root (void) if (device) { char *prefix_set; - - prefix_set = grub_xasprintf ("(%s)%s", device, path ? : ""); - if (prefix_set) + +#ifdef __powerpc__ + /* We have to be careful here on powerpc-ieee1275 + signed grub. We + will have signed something with a prefix that doesn't have a device + because we cannot know in advance what partition we're on. + + We will have had !device earlier, so we will have set device=fwdevice + However, we want to make sure we do not end up setting prefix to be + ($fwdevice)/path, because we will then end up trying to boot or search + based on a prefix of (ieee1275/disk)/path, which will not work because + it's missing a partition. + + Also: + - You can end up with a device with an FS directly on it, without + a partition, e.g. ieee1275/cdrom. + + - powerpc-ieee1275 + grub-install sets e.g. prefix=(,gpt2)/path, + which will have now been extended to device=$fwdisk,partition + and path=/path + + - PowerVM will give us device names like + ieee1275//vdevice/v-scsi@3000006c/disk@8100000000000000 + and we don't want to try to encode some sort of truth table about + what sorts of paths represent disks with partition tables and those + without partition tables. + + So we act unless there is a comma in the device, which would indicate + a partition has already been specified. + + (If we only have a path, the code in normal to discover config files + will try both without partitions and then with any partitions so we + will cover both CDs and HDs.) + */ + if (grub_strchr (device, ',') == NULL) + grub_env_set ("prefix", path); + else +#endif { - grub_env_set ("prefix", prefix_set); - grub_free (prefix_set); + prefix_set = grub_xasprintf ("(%s)%s", device, path ? : ""); + if (prefix_set) + { + grub_env_set ("prefix", prefix_set); + grub_free (prefix_set); + } } + grub_env_set ("root", device); } From dcf581ba340f45337c6067220464a42456d79d04 Mon Sep 17 00:00:00 2001 From: Erwan Velu Date: Wed, 25 Aug 2021 15:31:52 +0200 Subject: [PATCH 188/367] fs/xfs: Fix unreadable filesystem with v4 superblock The commit 8b1e5d193 (fs/xfs: Add bigtime incompat feature support) introduced the bigtime support by adding some features in v3 inodes. This change extended grub_xfs_inode struct by 76 bytes but also changed the computation of XFS_V2_INODE_SIZE and XFS_V3_INODE_SIZE. Prior this commit, XFS_V2_INODE_SIZE was 100 bytes. After the commit it's 84 bytes XFS_V2_INODE_SIZE becomes 16 bytes too small. As a result, the data structures aren't properly aligned and the GRUB generates "attempt to read or write outside of partition" errors when trying to read the XFS filesystem: GNU GRUB version 2.11 .... grub> set debug=efi,gpt,xfs grub> insmod part_gpt grub> ls (hd0,gpt1)/ partmap/gpt.c:93: Read a valid GPT header partmap/gpt.c:115: GPT entry 0: start=4096, length=1953125 fs/xfs.c:931: Reading sb fs/xfs.c:270: Validating superblock fs/xfs.c:295: XFS v4 superblock detected fs/xfs.c:962: Reading root ino 128 fs/xfs.c:515: Reading inode (128) - 64, 0 fs/xfs.c:515: Reading inode (739521961424144223) - 344365866970255880, 3840 error: attempt to read or write outside of partition. This commit change the XFS_V2_INODE_SIZE computation by subtracting 76 bytes instead of 92 bytes from the actual size of grub_xfs_inode struct. This 76 bytes value comes from added members: 20 grub_uint8_t unused5 1 grub_uint64_t flags2 48 grub_uint8_t unused6 This patch explicitly splits the v2 and v3 parts of the structure. The unused4 is still ending of the v2 structures and the v3 starts at unused5. Thanks to this we will avoid future corruptions of v2 or v3 inodes. The XFS_V2_INODE_SIZE is returning to its expected size and the filesystem is back to a readable state: GNU GRUB version 2.11 .... grub> set debug=efi,gpt,xfs grub> insmod part_gpt grub> ls (hd0,gpt1)/ partmap/gpt.c:93: Read a valid GPT header partmap/gpt.c:115: GPT entry 0: start=4096, length=1953125 fs/xfs.c:931: Reading sb fs/xfs.c:270: Validating superblock fs/xfs.c:295: XFS v4 superblock detected fs/xfs.c:962: Reading root ino 128 fs/xfs.c:515: Reading inode (128) - 64, 0 fs/xfs.c:515: Reading inode (128) - 64, 0 fs/xfs.c:931: Reading sb fs/xfs.c:270: Validating superblock fs/xfs.c:295: XFS v4 superblock detected fs/xfs.c:962: Reading root ino 128 fs/xfs.c:515: Reading inode (128) - 64, 0 fs/xfs.c:515: Reading inode (128) - 64, 0 fs/xfs.c:515: Reading inode (128) - 64, 0 fs/xfs.c:515: Reading inode (131) - 64, 768 efi/ fs/xfs.c:515: Reading inode (3145856) - 1464904, 0 grub2/ fs/xfs.c:515: Reading inode (132) - 64, 1024 grub/ fs/xfs.c:515: Reading inode (139) - 64, 2816 grub> Fixes: 8b1e5d193 (fs/xfs: Add bigtime incompat feature support) Signed-off-by: Erwan Velu Tested-by: Carlos Maiolino Reviewed-by: Daniel Kiper (cherry picked from commit a4b495520e4dc41a896a8b916a64eda9970c50ea) --- grub-core/fs/xfs.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index 0f524c3a8a..e3816d1ec4 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -192,6 +192,11 @@ struct grub_xfs_time_legacy grub_uint32_t nanosec; } GRUB_PACKED; +/* + * The struct grub_xfs_inode layout was taken from the + * struct xfs_dinode_core which is described here: + * https://mirrors.edge.kernel.org/pub/linux/utils/fs/xfs/docs/xfs_filesystem_structure.pdf + */ struct grub_xfs_inode { grub_uint8_t magic[2]; @@ -208,14 +213,15 @@ struct grub_xfs_inode grub_uint32_t nextents; grub_uint16_t unused3; grub_uint8_t fork_offset; - grub_uint8_t unused4[37]; + grub_uint8_t unused4[17]; /* Last member of inode v2. */ + grub_uint8_t unused5[20]; /* First member of inode v3. */ grub_uint64_t flags2; - grub_uint8_t unused5[48]; + grub_uint8_t unused6[48]; /* Last member of inode v3. */ } GRUB_PACKED; #define XFS_V3_INODE_SIZE sizeof(struct grub_xfs_inode) -/* Size of struct grub_xfs_inode until fork_offset (included). */ -#define XFS_V2_INODE_SIZE (XFS_V3_INODE_SIZE - 92) +/* Size of struct grub_xfs_inode v2, up to unused4 member included. */ +#define XFS_V2_INODE_SIZE (XFS_V3_INODE_SIZE - 76) struct grub_xfs_dirblock_tail { From 1ecd05680a11c22e51897ddc23ff15e76281ca70 Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Tue, 12 Oct 2021 12:34:23 -0400 Subject: [PATCH 189/367] Print module name on license check failure At the very least, this will make it easier to track down the problem module - or, if something else has gone wrong, provide more information for debugging. Signed-off-by: Robbie Harwood --- grub-core/kern/dl.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index 9557254035..f304494574 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -528,14 +528,16 @@ grub_dl_find_section_index (Elf_Ehdr *e, const char *name) Be sure to understand your license obligations. */ static grub_err_t -grub_dl_check_license (Elf_Ehdr *e) +grub_dl_check_license (grub_dl_t mod, Elf_Ehdr *e) { Elf_Shdr *s = grub_dl_find_section (e, ".module_license"); if (s && (grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv3") == 0 || grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv3+") == 0 || grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv2+") == 0)) return GRUB_ERR_NONE; - return grub_error (GRUB_ERR_BAD_MODULE, "incompatible license"); + return grub_error (GRUB_ERR_BAD_MODULE, + "incompatible license in module %s: %s", mod->name, + (char *) e + s->sh_offset); } static grub_err_t @@ -743,8 +745,8 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size) constitutes linking) and GRUB core being licensed under GPLv3+. Be sure to understand your license obligations. */ - if (grub_dl_check_license (e) - || grub_dl_resolve_name (mod, e) + if (grub_dl_resolve_name (mod, e) + || grub_dl_check_license (mod, e) || grub_dl_resolve_dependencies (mod, e) || grub_dl_load_segments (mod, e) || grub_dl_resolve_symbols (mod, e) From b35e3604c3886364d88c6c967772a4bcb244ccee Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 22 Oct 2021 09:53:15 +1100 Subject: [PATCH 190/367] powerpc-ieee1275: load grub at 4MB, not 2MB This was first reported under PFW but reproduces under SLOF. - The core.elf was 2126152 = 0x207148 bytes in size with the following program headers (per readelf): Entry point 0x200000 There are 4 program headers, starting at offset 52 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000160 0x00200000 0x00200000 0x21f98 0x2971c RWE 0x8 GNU_STACK 0x0220f8 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4 LOAD 0x0220f8 0x00232000 0x00232000 0x1e4e50 0x1e4e50 RWE 0x4 NOTE 0x206f48 0x00000000 0x00000000 0x00200 0x00000 R 0x4 - SLOF places the ELF file at 0x4000 (after the reserved space for interrupt handlers etc.) upwards. The image was 2126152 = 0x207148 bytes in size, so it runs from 0x4000 - 0x20b148. We'll call 0x4000 the load address. 0x0 0x4000 0x20b148 |----------|--------------| | reserved | ELF contents | - SLOF then copies the first LOAD program header (for .text). That runs for 0x21f98 bytes. It runs from (load addr + 0x160) to (load addr + 0x160 + 0x21f98) = 0x4160 to 0x260f8 and we copy it to 0x200000 to 0x221f98. This overwrites the end of the image: 0x0 0x4000 0x200000 0x221f98 |----------|------------|---------------| | reserved | ELF cont.. | .text section | - SLOF zeros the bss up to PhysAddr + MemSize = 0x22971c 0x0 0x4000 0x200000 0x221f98 0x22971c |----------|------------|---------------|--------| | reserved | ELF cont.. | .text section | bss 0s | - SLOF then goes to fulfil the next LOAD header (for mods), which is for 0x1e4e50 bytes. We copy from (load addr + 0x220f8) to (load addr + 0x220f8 + 0x1e4e50) = 0x260f8 to 0x20af48 and we copy it to 0x232000 to 0x416e50: 0x0 0x4000 0x200000 0x221f98 0x22971c |----------|------------|---------------|--------| | reserved | ELF cont.. | .text section | bss 0s | |-------------| | copied area | 0x260f8 0x20af48 This goes poorly: 0x0 0x4000 0x200000 0x221f98 0x22971c 0x232000 0x40bf08 0x416e50 |----------|------------|---------------|--------|-----|-----------|-------------| | reserved | ELF cont.. | .text section | bss 0s | pad | some mods | .text start | This matches the observations on the running system - 0x40bf08 was where the contents of memory no longer matched the contents of the ELF file. This was reported as a license verification failure on SLOF as the last module's .module_license section fell past where the corruption began. Signed-off-by: Daniel Axtens [rharwood@redhat.com: trim very detailed commit message] Signed-off-by: Robbie Harwood --- grub-core/Makefile.core.def | 2 +- include/grub/offsets.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 4a57de975e..08ac0fb15f 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -89,7 +89,7 @@ kernel = { i386_xen_pvh_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x100000'; mips_loongson_ldflags = '-Wl,-Ttext,0x80200000'; - powerpc_ieee1275_ldflags = '-Wl,-Ttext,0x200000'; + powerpc_ieee1275_ldflags = '-Wl,-Ttext,0x400000'; sparc64_ieee1275_ldflags = '-Wl,-Ttext,0x4400'; mips_arc_ldflags = '-Wl,-Ttext,$(TARGET_LINK_ADDR)'; mips_qemu_mips_ldflags = '-Wl,-Ttext,0x80200000'; diff --git a/include/grub/offsets.h b/include/grub/offsets.h index 871e1cd4c3..69211aa798 100644 --- a/include/grub/offsets.h +++ b/include/grub/offsets.h @@ -63,7 +63,7 @@ #define GRUB_KERNEL_SPARC64_IEEE1275_LINK_ADDR 0x4400 #define GRUB_KERNEL_POWERPC_IEEE1275_LINK_ALIGN 4 -#define GRUB_KERNEL_POWERPC_IEEE1275_LINK_ADDR 0x200000 +#define GRUB_KERNEL_POWERPC_IEEE1275_LINK_ADDR 0x400000 #define GRUB_KERNEL_MIPS_LOONGSON_LINK_ADDR 0x80200000 From 31dd7ffa4f417f1b650c9ef977850504d27a7b58 Mon Sep 17 00:00:00 2001 From: Michael Chang via Grub-devel Date: Fri, 3 Dec 2021 16:13:28 +0800 Subject: [PATCH 191/367] grub-mkconfig: restore umask for grub.cfg Since commit: ab2e53c8a grub-mkconfig: Honor a symlink when generating configuration by grub-mkconfig has inadvertently discarded umask for creating grub.cfg in the process of grub-mkconfig. The resulting wrong permission (0644) would allow unprivileged users to read grub's configuration file content. This presents a low confidentiality risk as grub.cfg may contain non-secured plain-text passwords. This patch restores the missing umask and set the file mode of creation to 0600 preventing unprivileged access. Fixes: CVE-2021-3981 Signed-off-by: Michael Chang --- util/grub-mkconfig.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index f55339a3f6..520a672cd2 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -311,7 +311,9 @@ and /etc/grub.d/* files or please file a bug report with exit 1 else # none of the children aborted with error, install the new grub.cfg + oldumask=$(umask); umask 077 cat ${grub_cfg}.new > ${grub_cfg} + umask $oldumask rm -f ${grub_cfg}.new fi fi From b612cdc1a4cb075cf2fa10e62c9d892f6e1aebc1 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Mon, 13 Dec 2021 14:25:49 +0800 Subject: [PATCH 192/367] fs/btrfs: Use full btrfs bootloader area Up to now GRUB can only embed to the first 64 KiB before primary superblock of btrfs, effectively limiting the GRUB core size. That could consequently pose restrictions to feature enablement like advanced zstd compression. This patch attempts to utilize full unused area reserved by btrfs for the bootloader outlined in the document [1]: The first 1MiB on each device is unused with the exception of primary superblock that is on the offset 64KiB and spans 4KiB. Apart from that, adjacent sectors to superblock and first block group are not used for embedding in case of overflow and logged access to adjacent sectors could be useful for tracing it up. This patch has been tested to provide out of the box support for btrfs zstd compression with which GRUB has been installed to the partition. [1] https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#BOOTLOADER_SUPPORT Signed-off-by: Michael Chang Reviewed-by: Daniel Kiper (cherry picked from commit b0f06a81c6f31b6fa20be67a96b6683bba8210c9) --- grub-core/fs/btrfs.c | 90 +++++++++++++++++++++++++++++++++++++------- include/grub/disk.h | 2 + 2 files changed, 79 insertions(+), 13 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 4cc86e9b79..07c0ff874b 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -2476,6 +2476,33 @@ grub_btrfs_label (grub_device_t device, char **label) } #ifdef GRUB_UTIL + +struct embed_region { + unsigned int start; + unsigned int secs; +}; + +/* + * https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#BOOTLOADER_SUPPORT + * The first 1 MiB on each device is unused with the exception of primary + * superblock that is on the offset 64 KiB and spans 4 KiB. + */ + +static const struct { + struct embed_region available; + struct embed_region used[6]; +} btrfs_head = { + .available = {0, GRUB_DISK_KiB_TO_SECTORS (1024)}, /* The first 1 MiB. */ + .used = { + {0, 1}, /* boot.S. */ + {GRUB_DISK_KiB_TO_SECTORS (64) - 1, 1}, /* Overflow guard. */ + {GRUB_DISK_KiB_TO_SECTORS (64), GRUB_DISK_KiB_TO_SECTORS (4)}, /* 4 KiB superblock. */ + {GRUB_DISK_KiB_TO_SECTORS (68), 1}, /* Overflow guard. */ + {GRUB_DISK_KiB_TO_SECTORS (1024) - 1, 1}, /* Overflow guard. */ + {0, 0} /* Array terminator. */ + } +}; + static grub_err_t grub_btrfs_embed (grub_device_t device __attribute__ ((unused)), unsigned int *nsectors, @@ -2483,25 +2510,62 @@ grub_btrfs_embed (grub_device_t device __attribute__ ((unused)), grub_embed_type_t embed_type, grub_disk_addr_t **sectors) { - unsigned i; + unsigned int i, j, n = 0; + const struct embed_region *u; + grub_disk_addr_t *map; if (embed_type != GRUB_EMBED_PCBIOS) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "BtrFS currently supports only PC-BIOS embedding"); - if (64 * 2 - 1 < *nsectors) - return grub_error (GRUB_ERR_OUT_OF_RANGE, - N_("your core.img is unusually large. " - "It won't fit in the embedding area")); - - *nsectors = 64 * 2 - 1; - if (*nsectors > max_nsectors) - *nsectors = max_nsectors; - *sectors = grub_calloc (*nsectors, sizeof (**sectors)); - if (!*sectors) + map = grub_calloc (btrfs_head.available.secs, sizeof (*map)); + if (map == NULL) return grub_errno; - for (i = 0; i < *nsectors; i++) - (*sectors)[i] = i + 1; + + /* + * Populating the map array so that it can be used to index if a disk + * address is available to embed: + * - 0: available, + * - 1: unavailable. + */ + for (u = btrfs_head.used; u->secs; ++u) + { + unsigned int end = u->start + u->secs; + + if (end > btrfs_head.available.secs) + end = btrfs_head.available.secs; + for (i = u->start; i < end; ++i) + map[i] = 1; + } + + /* Adding up n until it matches total size of available embedding area. */ + for (i = 0; i < btrfs_head.available.secs; ++i) + if (map[i] == 0) + n++; + + if (n < *nsectors) + { + grub_free (map); + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("your core.img is unusually large. " + "It won't fit in the embedding area")); + } + + if (n > max_nsectors) + n = max_nsectors; + + /* + * Populating the array so that it can used to index disk block address for + * an image file's offset to be embedded on disk (the unit is in sectors): + * - i: The disk block address relative to btrfs_head.available.start, + * - j: The offset in image file. + */ + for (i = 0, j = 0; i < btrfs_head.available.secs && j < n; ++i) + if (map[i] == 0) + map[j++] = btrfs_head.available.start + i; + + *nsectors = n; + *sectors = map; return GRUB_ERR_NONE; } diff --git a/include/grub/disk.h b/include/grub/disk.h index f95aca929a..06210a7049 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -182,6 +182,8 @@ typedef struct grub_disk_memberlist *grub_disk_memberlist_t; /* Return value of grub_disk_native_sectors() in case disk size is unknown. */ #define GRUB_DISK_SIZE_UNKNOWN 0xffffffffffffffffULL +#define GRUB_DISK_KiB_TO_SECTORS(x) ((x) << (10 - GRUB_DISK_SECTOR_BITS)) + /* Convert sector number from one sector size to another. */ static inline grub_disk_addr_t grub_convert_sector (grub_disk_addr_t sector, From b712a758241199ef2a2e5ec2ed067723a5837c24 Mon Sep 17 00:00:00 2001 From: fluteze Date: Sat, 27 Nov 2021 10:54:44 -0600 Subject: [PATCH 193/367] Add Fedora location of DejaVu SANS font In Fedora 35, and possibly earlier, grub would fail to configure with a complaint about DejaVu being "not found" even though it was installed. The DejaVu sans font search path is updated to reflect the distribution's current install path. Signed-off-by: Erik Edwards [rharwood@redhat.com: slight commit message edits] Signed-off-by: Robbie Harwood --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index ab0d326f00..40c4338bce 100644 --- a/configure.ac +++ b/configure.ac @@ -1784,7 +1784,7 @@ fi if test x"$starfield_excuse" = x; then for ext in pcf pcf.gz bdf bdf.gz ttf ttf.gz; do - for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/truetype/ttf-dejavu /usr/share/fonts/dejavu /usr/share/fonts/truetype; do + for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/truetype/ttf-dejavu /usr/share/fonts/dejavu /usr/share/fonts/truetype /usr/share/fonts/dejavu-sans-fonts; do if test -f "$dir/DejaVuSans.$ext"; then DJVU_FONT_SOURCE="$dir/DejaVuSans.$ext" break 2 From ebdef63d98264ea159eb0def99db0a0f4dacf644 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 28 Jan 2022 11:30:32 +0100 Subject: [PATCH 194/367] normal/menu: Don't show "Booting `%s'" msg when auto-booting with TIMEOUT_STYLE_HIDDEN When the user has asked the menu code to be hidden/quiet and the current entry is being autobooted because the timeout has expired don't show the "Booting `%s'" msg. This is necessary to let flicker-free boots really be flicker free, otherwise the "Booting `%s'" msg will kick the EFI fb into text mode and show the msg, breaking the flicker-free experience. Signed-off-by: Hans de Goede --- grub-core/normal/menu.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c index ec0c92bade..c8516a5a08 100644 --- a/grub-core/normal/menu.c +++ b/grub-core/normal/menu.c @@ -606,13 +606,15 @@ print_countdown (struct grub_term_coordinate *pos, int n) entry to be executed is a result of an automatic default selection because of the timeout. */ static int -run_menu (grub_menu_t menu, int nested, int *auto_boot) +run_menu (grub_menu_t menu, int nested, int *auto_boot, int *notify_boot) { grub_uint64_t saved_time; int default_entry, current_entry; int timeout; enum timeout_style timeout_style; + *notify_boot = 1; + default_entry = get_entry_number (menu, "default"); /* If DEFAULT_ENTRY is not within the menu entries, fall back to @@ -687,6 +689,7 @@ run_menu (grub_menu_t menu, int nested, int *auto_boot) if (timeout == 0) { *auto_boot = 1; + *notify_boot = timeout_style != TIMEOUT_STYLE_HIDDEN; return default_entry; } @@ -840,12 +843,16 @@ run_menu (grub_menu_t menu, int nested, int *auto_boot) /* Callback invoked immediately before a menu entry is executed. */ static void -notify_booting (grub_menu_entry_t entry, - void *userdata __attribute__((unused))) +notify_booting (grub_menu_entry_t entry, void *userdata) { - grub_printf (" "); - grub_printf_ (N_("Booting `%s'"), entry->title); - grub_printf ("\n\n"); + int *notify_boot = userdata; + + if (*notify_boot) + { + grub_printf (" "); + grub_printf_ (N_("Booting `%s'"), entry->title); + grub_printf ("\n\n"); + } } /* Callback invoked when a default menu entry executed because of a timeout @@ -893,8 +900,9 @@ show_menu (grub_menu_t menu, int nested, int autobooted) int boot_entry; grub_menu_entry_t e; int auto_boot; + int notify_boot; - boot_entry = run_menu (menu, nested, &auto_boot); + boot_entry = run_menu (menu, nested, &auto_boot, ¬ify_boot); if (boot_entry < 0) break; @@ -906,7 +914,7 @@ show_menu (grub_menu_t menu, int nested, int autobooted) if (auto_boot) grub_menu_execute_with_fallback (menu, e, autobooted, - &execution_callback, 0); + &execution_callback, ¬ify_boot); else grub_menu_execute_entry (e, 0); if (autobooted) From 728a82cf438fdc3f917e8718a72dacc7807b0be6 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 28 Jan 2022 11:30:33 +0100 Subject: [PATCH 195/367] EFI: suppress the "Welcome to GRUB!" message in EFI builds Grub EFI builds are now often used in combination with flicker-free boot, but this breaks with upstream grub because the "Welcome to GRUB!" message will kick the EFI fb into text mode and show the msg, breaking the flicker-free experience. EFI systems are so fast, that when the menu or the countdown are enabled the message will be immediately overwritten, so in these cases not printing the message does not matter. And in case when the timeout_style is set to TIMEOUT_STYLE_HIDDEN, the user has asked grub to be quiet (for example to allow flickfree boot) annd thus the message should not be printed. Signed-off-by: Hans de Goede --- grub-core/kern/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c index 3fc3401472..993b8a8598 100644 --- a/grub-core/kern/main.c +++ b/grub-core/kern/main.c @@ -317,10 +317,13 @@ grub_main (void) grub_boot_time ("After machine init."); + /* This breaks flicker-free boot on EFI systems, so disable it there. */ +#ifndef GRUB_MACHINE_EFI /* Hello. */ grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT); grub_printf ("Welcome to GRUB!\n\n"); grub_setcolorstate (GRUB_TERM_COLOR_STANDARD); +#endif /* Init verifiers API. */ grub_verifiers_init (); From ae658706e61152013dd0f62d458e3e79905dd026 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 28 Jan 2022 12:43:48 +0100 Subject: [PATCH 196/367] EFI: console: Do not set colorstate until the first text output GRUB_MOD_INIT(normal) does an unconditional: grub_env_set ("color_normal", "light-gray/black"); which triggers a grub_term_setcolorstate() call. The original version of the "efi/console: Do not set text-mode until we actually need it" patch: https://lists.gnu.org/archive/html/grub-devel/2018-03/msg00125.html Protected against this by caching the requested state in grub_console_setcolorstate () and then only applying it when the first text output actually happens. During refactoring to move the grub_console_setcolorstate () up higher in the grub-core/term/efi/console.c file the code to cache the color-state + bail early was accidentally dropped. Restore the cache the color-state + bail early behavior from the original. Cc: Javier Martinez Canillas Fixes: 2d7c3abd871f ("efi/console: Do not set text-mode until we actually need it") Signed-off-by: Hans de Goede --- grub-core/term/efi/console.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/grub-core/term/efi/console.c b/grub-core/term/efi/console.c index 2f1ae85ba7..c44b2ac318 100644 --- a/grub-core/term/efi/console.c +++ b/grub-core/term/efi/console.c @@ -82,6 +82,16 @@ grub_console_setcolorstate (struct grub_term_output *term { grub_efi_simple_text_output_interface_t *o; + if (grub_efi_is_finished || text_mode != GRUB_TEXT_MODE_AVAILABLE) + { + /* + * Cache colorstate changes before the first text-output, this avoids + * "color_normal" environment writes causing a switch to textmode. + */ + text_colorstate = state; + return; + } + if (grub_efi_is_finished) return; From ec91b20c5a5298da69b7188897405aaa5a810c48 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 28 Jan 2022 12:43:49 +0100 Subject: [PATCH 197/367] EFI: console: Do not set cursor until the first text output To allow flickerfree boot the EFI console code does not call grub_efi_set_text_mode (1) until some text is actually output. Depending on if the output text is because of an error loading e.g. the .cfg file; or because of showing the menu the cursor needs to be on or off when the first text is shown. So far the cursor was hardcoded to being on, but this is causing drawing artifacts + slow drawing of the menu as reported here: https://bugzilla.redhat.com/show_bug.cgi?id=1946969 Handle the cursorstate in the same way as the colorstate to fix this, when no text has been output yet, just cache the cursorstate and then use the last set value when the first text is output. Fixes: 2d7c3abd871f ("efi/console: Do not set text-mode until we actually need it") Signed-off-by: Hans de Goede --- grub-core/term/efi/console.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/grub-core/term/efi/console.c b/grub-core/term/efi/console.c index c44b2ac318..a3622e4fe5 100644 --- a/grub-core/term/efi/console.c +++ b/grub-core/term/efi/console.c @@ -31,7 +31,15 @@ typedef enum { } grub_text_mode; +typedef enum { + GRUB_CURSOR_MODE_UNDEFINED = -1, + GRUB_CURSOR_MODE_OFF = 0, + GRUB_CURSUR_MODE_ON +} +grub_cursor_mode; + static grub_text_mode text_mode = GRUB_TEXT_MODE_UNDEFINED; +static grub_cursor_mode cursor_mode = GRUB_CURSOR_MODE_UNDEFINED; static grub_term_color_state text_colorstate = GRUB_TERM_COLOR_UNDEFINED; static grub_uint32_t @@ -119,8 +127,12 @@ grub_console_setcursor (struct grub_term_output *term __attribute__ ((unused)), { grub_efi_simple_text_output_interface_t *o; - if (grub_efi_is_finished) - return; + if (grub_efi_is_finished || text_mode != GRUB_TEXT_MODE_AVAILABLE) + { + /* Cache cursor changes before the first text-output */ + cursor_mode = on; + return; + } o = grub_efi_system_table->con_out; efi_call_2 (o->enable_cursor, o, on); @@ -143,7 +155,8 @@ grub_prepare_for_text_output (struct grub_term_output *term) return GRUB_ERR_BAD_DEVICE; } - grub_console_setcursor (term, 1); + if (cursor_mode != GRUB_CURSOR_MODE_UNDEFINED) + grub_console_setcursor (term, cursor_mode); if (text_colorstate != GRUB_TERM_COLOR_UNDEFINED) grub_console_setcolorstate (term, text_colorstate); text_mode = GRUB_TEXT_MODE_AVAILABLE; From 256d6f0fbd9fe14b66b6efae48066b9cdb408076 Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Wed, 15 Dec 2021 15:46:13 -0500 Subject: [PATCH 198/367] Use visual indentation in config.h.in Signed-off-by: Robbie Harwood (cherry picked from commit de8051f34de0aa55c921a510974e5bb27e39c17b) [rharwood: GRUB_RPM_CONFIG presence] --- config.h.in | 58 ++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/config.h.in b/config.h.in index c80e3e0aba..f2ed0066ec 100644 --- a/config.h.in +++ b/config.h.in @@ -23,47 +23,47 @@ #define MINILZO_CFG_SKIP_LZO1X_DECOMPRESS 1 #if defined (GRUB_BUILD) -#undef ENABLE_NLS -#define BUILD_SIZEOF_LONG @BUILD_SIZEOF_LONG@ -#define BUILD_SIZEOF_VOID_P @BUILD_SIZEOF_VOID_P@ -#if defined __APPLE__ -# if defined __BIG_ENDIAN__ -# define BUILD_WORDS_BIGENDIAN 1 -# else -# define BUILD_WORDS_BIGENDIAN 0 -# endif -#else -#define BUILD_WORDS_BIGENDIAN @BUILD_WORDS_BIGENDIAN@ -#endif +# undef ENABLE_NLS +# define BUILD_SIZEOF_LONG @BUILD_SIZEOF_LONG@ +# define BUILD_SIZEOF_VOID_P @BUILD_SIZEOF_VOID_P@ +# if defined __APPLE__ +# if defined __BIG_ENDIAN__ +# define BUILD_WORDS_BIGENDIAN 1 +# else +# define BUILD_WORDS_BIGENDIAN 0 +# endif +# else /* !defined __APPLE__ */ +# define BUILD_WORDS_BIGENDIAN @BUILD_WORDS_BIGENDIAN@ +# endif /* !defined __APPLE__ */ #elif defined (GRUB_UTIL) || !defined (GRUB_MACHINE) -#include -#else -#define HAVE_FONT_SOURCE @HAVE_FONT_SOURCE@ +# include +#else /* !defined GRUB_UTIL && defined GRUB_MACHINE */ +# define HAVE_FONT_SOURCE @HAVE_FONT_SOURCE@ /* Define if C symbols get an underscore after compilation. */ -#define HAVE_ASM_USCORE @HAVE_ASM_USCORE@ +# define HAVE_ASM_USCORE @HAVE_ASM_USCORE@ /* Define it to one of __bss_start, edata and _edata. */ -#define BSS_START_SYMBOL @BSS_START_SYMBOL@ +# define BSS_START_SYMBOL @BSS_START_SYMBOL@ /* Define it to either end or _end. */ -#define END_SYMBOL @END_SYMBOL@ +# define END_SYMBOL @END_SYMBOL@ /* Name of package. */ -#define PACKAGE "@PACKAGE@" +# define PACKAGE "@PACKAGE@" /* Version number of package. */ -#define VERSION "@VERSION@" +# define VERSION "@VERSION@" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "@PACKAGE_STRING@" +# define PACKAGE_STRING "@PACKAGE_STRING@" /* Define to the version of this package. */ -#define PACKAGE_VERSION "@PACKAGE_VERSION@" +# define PACKAGE_VERSION "@PACKAGE_VERSION@" /* Define to the full name of this package. */ -#define PACKAGE_NAME "@PACKAGE_NAME@" +# define PACKAGE_NAME "@PACKAGE_NAME@" /* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@" +# define PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@" -#define GRUB_TARGET_CPU "@GRUB_TARGET_CPU@" -#define GRUB_PLATFORM "@GRUB_PLATFORM@" -#define GRUB_RPM_VERSION "@GRUB_RPM_VERSION@" +# define GRUB_TARGET_CPU "@GRUB_TARGET_CPU@" +# define GRUB_PLATFORM "@GRUB_PLATFORM@" +# define GRUB_RPM_VERSION "@GRUB_RPM_VERSION@" -#define RE_ENABLE_I18N 1 +# define RE_ENABLE_I18N 1 -#define _GNU_SOURCE 1 +# define _GNU_SOURCE 1 #endif From 779e2a61da0ecb66324296902c63a89e19b2e93b Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Tue, 22 Feb 2022 16:57:54 -0500 Subject: [PATCH 199/367] Where present, ensure config-util.h precedes config.h gnulib defines go in config-util.h, and we need to know whether to provide duplicates in config.h or not. Signed-off-by: Robbie Harwood (cherry picked from commit 46e82b28e1a75703d0424c7e13d009171310c6cd) [rharwood: gensymlist isn't part of tarballs] --- grub-core/disk/host.c | 2 +- grub-core/kern/emu/argp_common.c | 2 +- grub-core/kern/emu/main.c | 2 +- grub-core/osdep/aros/config.c | 2 +- grub-core/osdep/basic/emunet.c | 2 +- grub-core/osdep/basic/init.c | 2 +- grub-core/osdep/haiku/getroot.c | 2 +- grub-core/osdep/linux/emunet.c | 2 +- grub-core/osdep/unix/config.c | 2 +- grub-core/osdep/unix/cputime.c | 2 +- grub-core/osdep/unix/dl.c | 2 +- grub-core/osdep/unix/emuconsole.c | 2 +- grub-core/osdep/unix/getroot.c | 2 +- grub-core/osdep/windows/config.c | 2 +- grub-core/osdep/windows/cputime.c | 2 +- grub-core/osdep/windows/dl.c | 2 +- grub-core/osdep/windows/emuconsole.c | 2 +- grub-core/osdep/windows/init.c | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/grub-core/disk/host.c b/grub-core/disk/host.c index c151d225df..f34529f86a 100644 --- a/grub-core/disk/host.c +++ b/grub-core/disk/host.c @@ -20,8 +20,8 @@ /* When using the disk, make a reference to this module. Otherwise the user will end up with a useless module :-). */ -#include #include +#include #include #include diff --git a/grub-core/kern/emu/argp_common.c b/grub-core/kern/emu/argp_common.c index 1668858703..8cb4608c3d 100644 --- a/grub-core/kern/emu/argp_common.c +++ b/grub-core/kern/emu/argp_common.c @@ -17,8 +17,8 @@ * along with GRUB. If not, see . */ -#include #include +#include #pragma GCC diagnostic ignored "-Wmissing-prototypes" #pragma GCC diagnostic ignored "-Wmissing-declarations" diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c index 55ea5a11cc..12277c34d2 100644 --- a/grub-core/kern/emu/main.c +++ b/grub-core/kern/emu/main.c @@ -16,8 +16,8 @@ * along with GRUB. If not, see . */ -#include #include +#include #include #include diff --git a/grub-core/osdep/aros/config.c b/grub-core/osdep/aros/config.c index c82d0ea8e7..55f5728efc 100644 --- a/grub-core/osdep/aros/config.c +++ b/grub-core/osdep/aros/config.c @@ -16,8 +16,8 @@ * along with GRUB. If not, see . */ -#include #include +#include #include #include diff --git a/grub-core/osdep/basic/emunet.c b/grub-core/osdep/basic/emunet.c index 6362e5cfbb..dbfd316d61 100644 --- a/grub-core/osdep/basic/emunet.c +++ b/grub-core/osdep/basic/emunet.c @@ -16,8 +16,8 @@ * along with GRUB. If not, see . */ -#include #include +#include #include #include diff --git a/grub-core/osdep/basic/init.c b/grub-core/osdep/basic/init.c index c54c710dbc..b104c7e162 100644 --- a/grub-core/osdep/basic/init.c +++ b/grub-core/osdep/basic/init.c @@ -16,8 +16,8 @@ * along with GRUB. If not, see . */ -#include #include +#include #include #include diff --git a/grub-core/osdep/haiku/getroot.c b/grub-core/osdep/haiku/getroot.c index 4e123c0903..927a1ebc94 100644 --- a/grub-core/osdep/haiku/getroot.c +++ b/grub-core/osdep/haiku/getroot.c @@ -1,5 +1,5 @@ -#include #include +#include #include #include #include diff --git a/grub-core/osdep/linux/emunet.c b/grub-core/osdep/linux/emunet.c index 19b188f09e..d5a6417355 100644 --- a/grub-core/osdep/linux/emunet.c +++ b/grub-core/osdep/linux/emunet.c @@ -16,8 +16,8 @@ * along with GRUB. If not, see . */ -#include #include +#include #include #include diff --git a/grub-core/osdep/unix/config.c b/grub-core/osdep/unix/config.c index 46a881530c..0ce0e309ac 100644 --- a/grub-core/osdep/unix/config.c +++ b/grub-core/osdep/unix/config.c @@ -16,8 +16,8 @@ * along with GRUB. If not, see . */ -#include #include +#include #include #include diff --git a/grub-core/osdep/unix/cputime.c b/grub-core/osdep/unix/cputime.c index cff359a3b9..fb6ff55a1a 100644 --- a/grub-core/osdep/unix/cputime.c +++ b/grub-core/osdep/unix/cputime.c @@ -1,5 +1,5 @@ -#include #include +#include #include #include diff --git a/grub-core/osdep/unix/dl.c b/grub-core/osdep/unix/dl.c index 562b101a28..99b189bc1c 100644 --- a/grub-core/osdep/unix/dl.c +++ b/grub-core/osdep/unix/dl.c @@ -16,8 +16,8 @@ * along with GRUB. If not, see . */ -#include #include +#include #include #include diff --git a/grub-core/osdep/unix/emuconsole.c b/grub-core/osdep/unix/emuconsole.c index 7308798efe..cac159424d 100644 --- a/grub-core/osdep/unix/emuconsole.c +++ b/grub-core/osdep/unix/emuconsole.c @@ -17,8 +17,8 @@ * along with GRUB. If not, see . */ -#include #include +#include #include #include diff --git a/grub-core/osdep/unix/getroot.c b/grub-core/osdep/unix/getroot.c index 46d7116c6e..4f436284ce 100644 --- a/grub-core/osdep/unix/getroot.c +++ b/grub-core/osdep/unix/getroot.c @@ -16,8 +16,8 @@ * along with GRUB. If not, see . */ -#include #include +#include #include #include diff --git a/grub-core/osdep/windows/config.c b/grub-core/osdep/windows/config.c index 928ab1a49b..2bb8a2fd88 100644 --- a/grub-core/osdep/windows/config.c +++ b/grub-core/osdep/windows/config.c @@ -16,8 +16,8 @@ * along with GRUB. If not, see . */ -#include #include +#include #include #include diff --git a/grub-core/osdep/windows/cputime.c b/grub-core/osdep/windows/cputime.c index 3568aa2d35..5d06d79dd5 100644 --- a/grub-core/osdep/windows/cputime.c +++ b/grub-core/osdep/windows/cputime.c @@ -1,5 +1,5 @@ -#include #include +#include #include #include diff --git a/grub-core/osdep/windows/dl.c b/grub-core/osdep/windows/dl.c index eec6a24ad7..8eab7057e4 100644 --- a/grub-core/osdep/windows/dl.c +++ b/grub-core/osdep/windows/dl.c @@ -16,8 +16,8 @@ * along with GRUB. If not, see . */ -#include #include +#include #include #include diff --git a/grub-core/osdep/windows/emuconsole.c b/grub-core/osdep/windows/emuconsole.c index 4fb3693cc0..17a44de469 100644 --- a/grub-core/osdep/windows/emuconsole.c +++ b/grub-core/osdep/windows/emuconsole.c @@ -16,8 +16,8 @@ * along with GRUB. If not, see . */ -#include #include +#include #include #include diff --git a/grub-core/osdep/windows/init.c b/grub-core/osdep/windows/init.c index 6297de6326..51a9647dde 100644 --- a/grub-core/osdep/windows/init.c +++ b/grub-core/osdep/windows/init.c @@ -16,8 +16,8 @@ * along with GRUB. If not, see . */ -#include #include +#include #include #include #include From 2c722a8b31190bbe87660b7a154d7f03c17eb6a2 Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Thu, 28 Oct 2021 15:07:50 -0400 Subject: [PATCH 200/367] Drop gnulib fix-base64.patch Originally added in 9fbdec2f6b4fa8b549daa4d49134d1fe89d95ef9 and subsequently modified in 552c9fd08122a3036c724ce96dfe68aa2f75705f, fix-base64.patch handled two problems we have using gnulib, which are exerciesd by the base64 module but not directly caused by it. First, grub2 defines its own bool type, while gnulib expects the equivalent of stdbool.h to be present. Rather than patching gnulib, instead use gnulib's stdbool module to provide a bool type if needed. (Suggested by Simon Josefsson.) Second, our config.h doesn't always inherit config-util.h, which is where gnulib-related options like _GL_ATTRIBUTE_CONST end up. fix-base64.h worked around this by defining the attribute away, but this workaround is better placed in config.h itself, not a gnulib patch. Signed-off-by: Robbie Harwood (cherry picked from commit 54fd1c3301dd15f6b6212c12887265e8a6cbc076) --- bootstrap.conf | 3 ++- conf/Makefile.extra-dist | 1 - config.h.in | 4 ++++ grub-core/lib/gnulib-patches/fix-base64.patch | 21 ------------------- grub-core/lib/posix_wrap/sys/types.h | 7 +++---- grub-core/lib/xzembed/xz.h | 5 +---- 6 files changed, 10 insertions(+), 31 deletions(-) delete mode 100644 grub-core/lib/gnulib-patches/fix-base64.patch diff --git a/bootstrap.conf b/bootstrap.conf index 52d4af44be..645e3a459c 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -35,6 +35,7 @@ gnulib_modules=" realloc-gnu regex save-cwd + stdbool " gnulib_tool_option_extras="\ @@ -79,7 +80,7 @@ cp -a INSTALL INSTALL.grub bootstrap_post_import_hook () { set -e - for patchname in fix-base64 fix-null-deref fix-null-state-deref fix-regcomp-uninit-token \ + for patchname in fix-null-deref fix-null-state-deref fix-regcomp-uninit-token \ fix-regexec-null-deref fix-uninit-structure fix-unused-value fix-width no-abort; do patch -d grub-core/lib/gnulib -p2 \ < "grub-core/lib/gnulib-patches/$patchname.patch" diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index ad235de7fc..f4791dc6ca 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -31,7 +31,6 @@ EXTRA_DIST += grub-core/gensymlist.sh EXTRA_DIST += grub-core/genemuinit.sh EXTRA_DIST += grub-core/genemuinitheader.sh -EXTRA_DIST += grub-core/lib/gnulib-patches/fix-base64.patch EXTRA_DIST += grub-core/lib/gnulib-patches/fix-null-deref.patch EXTRA_DIST += grub-core/lib/gnulib-patches/fix-null-state-deref.patch EXTRA_DIST += grub-core/lib/gnulib-patches/fix-regcomp-uninit-token.patch diff --git a/config.h.in b/config.h.in index f2ed0066ec..9c7b4afaaa 100644 --- a/config.h.in +++ b/config.h.in @@ -66,4 +66,8 @@ # define _GNU_SOURCE 1 +# ifndef _GL_INLINE_HEADER_BEGIN +# define _GL_ATTRIBUTE_CONST __attribute__ ((const)) +# endif /* !_GL_INLINE_HEADER_BEGIN */ + #endif diff --git a/grub-core/lib/gnulib-patches/fix-base64.patch b/grub-core/lib/gnulib-patches/fix-base64.patch deleted file mode 100644 index 985db12797..0000000000 --- a/grub-core/lib/gnulib-patches/fix-base64.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff --git a/lib/base64.h b/lib/base64.h -index 9cd0183b8..185a2afa1 100644 ---- a/lib/base64.h -+++ b/lib/base64.h -@@ -21,8 +21,14 @@ - /* Get size_t. */ - # include - --/* Get bool. */ --# include -+#ifndef GRUB_POSIX_BOOL_DEFINED -+typedef enum { false = 0, true = 1 } bool; -+#define GRUB_POSIX_BOOL_DEFINED 1 -+#endif -+ -+#ifndef _GL_ATTRIBUTE_CONST -+# define _GL_ATTRIBUTE_CONST /* empty */ -+#endif - - # ifdef __cplusplus - extern "C" { diff --git a/grub-core/lib/posix_wrap/sys/types.h b/grub-core/lib/posix_wrap/sys/types.h index f63412c8da..2f3e865495 100644 --- a/grub-core/lib/posix_wrap/sys/types.h +++ b/grub-core/lib/posix_wrap/sys/types.h @@ -23,11 +23,10 @@ #include +/* Provided by gnulib if not present. */ +#include + typedef grub_ssize_t ssize_t; -#ifndef GRUB_POSIX_BOOL_DEFINED -typedef enum { false = 0, true = 1 } bool; -#define GRUB_POSIX_BOOL_DEFINED 1 -#endif typedef grub_uint8_t uint8_t; typedef grub_uint16_t uint16_t; diff --git a/grub-core/lib/xzembed/xz.h b/grub-core/lib/xzembed/xz.h index f7b32d8003..d1417039aa 100644 --- a/grub-core/lib/xzembed/xz.h +++ b/grub-core/lib/xzembed/xz.h @@ -29,10 +29,7 @@ #include #include #include - -#ifndef GRUB_POSIX_BOOL_DEFINED -typedef enum { false = 0, true = 1 } bool; -#endif +#include /** * enum xz_ret - Return codes From dc8044fc231cfd442a1e166c1e201d88850558e5 Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Wed, 5 Jan 2022 16:42:11 -0500 Subject: [PATCH 201/367] Drop gnulib no-abort.patch Originally added in db7337a3d353a817ffe9eb4a3702120527100be9, this patched out all relevant invocations of abort() in gnulib. While it was not documented why at the time, testing suggests that there's no abort() implementation available for gnulib to use. gnulib's position is that the use of abort() is correct here, since it happens when input violates a "shall" from POSIX. Additionally, the code in question is probably not reachable. Since abort() is more friendly to user-space, they prefer to make no change, so we can just carry a define instead. (Suggested by Paul Eggert.) Signed-off-by: Robbie Harwood (cherry picked from commit 5137c8eb3ec11c3217acea1a93a3f88f3fa4cbca) --- bootstrap.conf | 2 +- conf/Makefile.extra-dist | 1 - config.h.in | 3 +++ grub-core/lib/gnulib-patches/no-abort.patch | 26 --------------------- 4 files changed, 4 insertions(+), 28 deletions(-) delete mode 100644 grub-core/lib/gnulib-patches/no-abort.patch diff --git a/bootstrap.conf b/bootstrap.conf index 645e3a459c..71ce943c7d 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -81,7 +81,7 @@ cp -a INSTALL INSTALL.grub bootstrap_post_import_hook () { set -e for patchname in fix-null-deref fix-null-state-deref fix-regcomp-uninit-token \ - fix-regexec-null-deref fix-uninit-structure fix-unused-value fix-width no-abort; do + fix-regexec-null-deref fix-uninit-structure fix-unused-value fix-width; do patch -d grub-core/lib/gnulib -p2 \ < "grub-core/lib/gnulib-patches/$patchname.patch" done diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index f4791dc6ca..5eef708338 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -38,7 +38,6 @@ EXTRA_DIST += grub-core/lib/gnulib-patches/fix-regexec-null-deref.patch EXTRA_DIST += grub-core/lib/gnulib-patches/fix-uninit-structure.patch EXTRA_DIST += grub-core/lib/gnulib-patches/fix-unused-value.patch EXTRA_DIST += grub-core/lib/gnulib-patches/fix-width.patch -EXTRA_DIST += grub-core/lib/gnulib-patches/no-abort.patch EXTRA_DIST += grub-core/lib/libgcrypt EXTRA_DIST += grub-core/lib/libgcrypt-grub/mpi/generic diff --git a/config.h.in b/config.h.in index 9c7b4afaaa..c3134309c6 100644 --- a/config.h.in +++ b/config.h.in @@ -68,6 +68,9 @@ # ifndef _GL_INLINE_HEADER_BEGIN # define _GL_ATTRIBUTE_CONST __attribute__ ((const)) + +/* We don't have an abort() for gnulib to call in regexp. */ +# define abort __builtin_unreachable # endif /* !_GL_INLINE_HEADER_BEGIN */ #endif diff --git a/grub-core/lib/gnulib-patches/no-abort.patch b/grub-core/lib/gnulib-patches/no-abort.patch deleted file mode 100644 index e469c4762e..0000000000 --- a/grub-core/lib/gnulib-patches/no-abort.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/lib/regcomp.c b/lib/regcomp.c -index cc85f35ac..de45ebb5c 100644 ---- a/lib/regcomp.c -+++ b/lib/regcomp.c -@@ -528,9 +528,9 @@ regerror (int errcode, const regex_t *__restrict preg, char *__restrict errbuf, - to this routine. If we are given anything else, or if other regex - code generates an invalid error code, then the program has a bug. - Dump core so we can fix it. */ -- abort (); -- -- msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]); -+ msg = gettext ("unknown regexp error"); -+ else -+ msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]); - - msg_size = strlen (msg) + 1; /* Includes the null. */ - -@@ -1136,7 +1136,7 @@ optimize_utf8 (re_dfa_t *dfa) - } - break; - default: -- abort (); -+ break; - } - - if (mb_chars || has_period) From c6cf390c6e9b0e9c22c244e3ea9ec79ca1c942e3 Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Wed, 15 Dec 2021 15:07:50 -0500 Subject: [PATCH 202/367] Update gnulib version and drop most gnulib patches In addition to the changes carried in our gnulib patches, several Coverity and code hygiene fixes that were previously downstream are also included in this 3-year gnulib increment. Unfortunately, fix-width.patch is retained. Bump minimum autoconf version from 2.63 to 2.64 and automake from 1.11 to 1.14, as required by gnulib. Sync bootstrap script itself with gnulib. Update regexp module for new dynarray dependency. Fix various new warnings. Signed-off-by: Robbie Harwood (cherry picked from commit deb18ff931c3133c2aa536a92bd92e50d6615303) [rharwood: backport around requirements in INSTALL] --- INSTALL | 2 +- bootstrap | 291 ++++++++++-------- bootstrap.conf | 22 +- conf/Makefile.extra-dist | 6 - config.h.in | 68 ++++ configure.ac | 2 +- grub-core/Makefile.core.def | 3 + grub-core/disk/luks2.c | 4 +- .../lib/gnulib-patches/fix-null-deref.patch | 13 - .../gnulib-patches/fix-null-state-deref.patch | 12 - .../fix-regcomp-uninit-token.patch | 15 - .../fix-regexec-null-deref.patch | 12 - .../gnulib-patches/fix-uninit-structure.patch | 11 - .../lib/gnulib-patches/fix-unused-value.patch | 14 - grub-core/lib/posix_wrap/limits.h | 6 +- include/grub/compiler.h | 4 +- include/grub/list.h | 2 +- 17 files changed, 262 insertions(+), 225 deletions(-) delete mode 100644 grub-core/lib/gnulib-patches/fix-null-deref.patch delete mode 100644 grub-core/lib/gnulib-patches/fix-null-state-deref.patch delete mode 100644 grub-core/lib/gnulib-patches/fix-regcomp-uninit-token.patch delete mode 100644 grub-core/lib/gnulib-patches/fix-regexec-null-deref.patch delete mode 100644 grub-core/lib/gnulib-patches/fix-uninit-structure.patch delete mode 100644 grub-core/lib/gnulib-patches/fix-unused-value.patch diff --git a/INSTALL b/INSTALL index 79a0af7d93..ee9f536f76 100644 --- a/INSTALL +++ b/INSTALL @@ -42,7 +42,7 @@ If you use a development snapshot or want to hack on GRUB you may need the following. * Python 2.6 or later -* Autoconf 2.63 or later +* Autoconf 2.64 or later * Automake 1.11 or later Prerequisites for make-check: diff --git a/bootstrap b/bootstrap index 5b08e7e2d4..dc2238f4ad 100755 --- a/bootstrap +++ b/bootstrap @@ -1,10 +1,10 @@ #! /bin/sh # Print a version string. -scriptversion=2019-01-04.17; # UTC +scriptversion=2022-01-26.05; # UTC # Bootstrap this package from checked-out sources. -# Copyright (C) 2003-2019 Free Software Foundation, Inc. +# Copyright (C) 2003-2022 Free Software Foundation, Inc. # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -47,7 +47,7 @@ PERL="${PERL-perl}" me=$0 -default_gnulib_url=git://git.sv.gnu.org/gnulib +default_gnulib_url=https://git.savannah.gnu.org/git/gnulib.git usage() { cat </dev/null) +if test -z "$package"; then + package=$(sed -n "$extract_package_name" configure.ac) \ + || die 'cannot find package name in configure.ac' +fi gnulib_name=lib$package build_aux=build-aux @@ -290,6 +313,116 @@ find_tool () eval "export $find_tool_envvar" } +# Strip blank and comment lines to leave significant entries. +gitignore_entries() { + sed '/^#/d; /^$/d' "$@" +} + +# If $STR is not already on a line by itself in $FILE, insert it at the start. +# Entries are inserted at the start of the ignore list to ensure existing +# entries starting with ! are not overridden. Such entries support +# whitelisting exceptions after a more generic blacklist pattern. +insert_if_absent() { + file=$1 + str=$2 + test -f $file || touch $file + test -r $file || die "Error: failed to read ignore file: $file" + duplicate_entries=$(gitignore_entries $file | sort | uniq -d) + if [ "$duplicate_entries" ] ; then + die "Error: Duplicate entries in $file: " $duplicate_entries + fi + linesold=$(gitignore_entries $file | wc -l) + linesnew=$( { echo "$str"; cat $file; } | gitignore_entries | sort -u | wc -l) + if [ $linesold != $linesnew ] ; then + { echo "$str" | cat - $file > $file.bak && mv $file.bak $file; } \ + || die "insert_if_absent $file $str: failed" + fi +} + +# Adjust $PATTERN for $VC_IGNORE_FILE and insert it with +# insert_if_absent. +insert_vc_ignore() { + vc_ignore_file="$1" + pattern="$2" + case $vc_ignore_file in + *.gitignore) + # A .gitignore entry that does not start with '/' applies + # recursively to subdirectories, so prepend '/' to every + # .gitignore entry. + pattern=$(echo "$pattern" | sed s,^,/,);; + esac + insert_if_absent "$vc_ignore_file" "$pattern" +} + +symlink_to_dir() +{ + src=$1/$2 + dst=${3-$2} + + test -f "$src" && { + + # If the destination directory doesn't exist, create it. + # This is required at least for "lib/uniwidth/cjk.h". + dst_dir=$(dirname "$dst") + if ! test -d "$dst_dir"; then + mkdir -p "$dst_dir" + + # If we've just created a directory like lib/uniwidth, + # tell version control system(s) it's ignorable. + # FIXME: for now, this does only one level + parent=$(dirname "$dst_dir") + for dot_ig in x $vc_ignore; do + test $dot_ig = x && continue + ig=$parent/$dot_ig + insert_vc_ignore $ig "${dst_dir##*/}" + done + fi + + if $copy; then + { + test ! -h "$dst" || { + echo "$me: rm -f $dst" && + rm -f "$dst" + } + } && + test -f "$dst" && + cmp -s "$src" "$dst" || { + echo "$me: cp -fp $src $dst" && + cp -fp "$src" "$dst" + } + else + # Leave any existing symlink alone, if it already points to the source, + # so that broken build tools that care about symlink times + # aren't confused into doing unnecessary builds. Conversely, if the + # existing symlink's timestamp is older than the source, make it afresh, + # so that broken tools aren't confused into skipping needed builds. See + # . + test -h "$dst" && + src_ls=$(ls -diL "$src" 2>/dev/null) && set $src_ls && src_i=$1 && + dst_ls=$(ls -diL "$dst" 2>/dev/null) && set $dst_ls && dst_i=$1 && + test "$src_i" = "$dst_i" && + both_ls=$(ls -dt "$src" "$dst") && + test "X$both_ls" = "X$dst$nl$src" || { + dot_dots= + case $src in + /*) ;; + *) + case /$dst/ in + *//* | */../* | */./* | /*/*/*/*/*/) + die "invalid symlink calculation: $src -> $dst";; + /*/*/*/*/) dot_dots=../../../;; + /*/*/*/) dot_dots=../../;; + /*/*/) dot_dots=../;; + esac;; + esac + + echo "$me: ln -fs $dot_dots$src $dst" && + ln -fs "$dot_dots$src" "$dst" + } + fi + } +} + # Override the default configuration, if necessary. # Make sure that bootstrap.conf is sourced from the current directory # if we were invoked as "sh bootstrap". @@ -320,6 +453,12 @@ do --help) usage exit;; + --version) + set -e + echo "bootstrap $scriptversion" + echo "$copyright" + exit 0 + ;; --gnulib-srcdir=*) GNULIB_SRCDIR=${option#--gnulib-srcdir=};; --skip-po) @@ -335,7 +474,7 @@ do --no-git) use_git=false;; *) - die "$option: unknown option";; + bootstrap_option_hook $option || die "$option: unknown option";; esac done @@ -346,47 +485,6 @@ if test -n "$checkout_only_file" && test ! -r "$checkout_only_file"; then die "Bootstrapping from a non-checked-out distribution is risky." fi -# Strip blank and comment lines to leave significant entries. -gitignore_entries() { - sed '/^#/d; /^$/d' "$@" -} - -# If $STR is not already on a line by itself in $FILE, insert it at the start. -# Entries are inserted at the start of the ignore list to ensure existing -# entries starting with ! are not overridden. Such entries support -# whitelisting exceptions after a more generic blacklist pattern. -insert_if_absent() { - file=$1 - str=$2 - test -f $file || touch $file - test -r $file || die "Error: failed to read ignore file: $file" - duplicate_entries=$(gitignore_entries $file | sort | uniq -d) - if [ "$duplicate_entries" ] ; then - die "Error: Duplicate entries in $file: " $duplicate_entries - fi - linesold=$(gitignore_entries $file | wc -l) - linesnew=$( { echo "$str"; cat $file; } | gitignore_entries | sort -u | wc -l) - if [ $linesold != $linesnew ] ; then - { echo "$str" | cat - $file > $file.bak && mv $file.bak $file; } \ - || die "insert_if_absent $file $str: failed" - fi -} - -# Adjust $PATTERN for $VC_IGNORE_FILE and insert it with -# insert_if_absent. -insert_vc_ignore() { - vc_ignore_file="$1" - pattern="$2" - case $vc_ignore_file in - *.gitignore) - # A .gitignore entry that does not start with '/' applies - # recursively to subdirectories, so prepend '/' to every - # .gitignore entry. - pattern=$(echo "$pattern" | sed s,^,/,);; - esac - insert_if_absent "$vc_ignore_file" "$pattern" -} - # Die if there is no AC_CONFIG_AUX_DIR($build_aux) line in configure.ac. found_aux_dir=no grep '^[ ]*AC_CONFIG_AUX_DIR(\['"$build_aux"'\])' configure.ac \ @@ -665,9 +763,25 @@ if $use_gnulib; then shallow= if test -z "$GNULIB_REVISION"; then git clone -h 2>&1 | grep -- --depth > /dev/null && shallow='--depth 2' + git clone $shallow ${GNULIB_URL:-$default_gnulib_url} "$gnulib_path" \ + || cleanup_gnulib + else + git fetch -h 2>&1 | grep -- --depth > /dev/null && shallow='--depth 2' + mkdir -p "$gnulib_path" + # Only want a shallow checkout of $GNULIB_REVISION, but git does not + # support cloning by commit hash. So attempt a shallow fetch by commit + # hash to minimize the amount of data downloaded and changes needed to + # be processed, which can drastically reduce download and processing + # time for checkout. If the fetch by commit fails, a shallow fetch can + # not be performed because we do not know what the depth of the commit + # is without fetching all commits. So fallback to fetching all commits. + git -C "$gnulib_path" init + git -C "$gnulib_path" remote add origin ${GNULIB_URL:-$default_gnulib_url} + git -C "$gnulib_path" fetch $shallow origin "$GNULIB_REVISION" \ + || git -C "$gnulib_path" fetch origin \ + || cleanup_gnulib + git -C "$gnulib_path" reset --hard FETCH_HEAD fi - git clone $shallow ${GNULIB_URL:-$default_gnulib_url} "$gnulib_path" \ - || cleanup_gnulib trap - 1 2 13 15 fi @@ -784,75 +898,6 @@ case $SKIP_PO in fi;; esac -symlink_to_dir() -{ - src=$1/$2 - dst=${3-$2} - - test -f "$src" && { - - # If the destination directory doesn't exist, create it. - # This is required at least for "lib/uniwidth/cjk.h". - dst_dir=$(dirname "$dst") - if ! test -d "$dst_dir"; then - mkdir -p "$dst_dir" - - # If we've just created a directory like lib/uniwidth, - # tell version control system(s) it's ignorable. - # FIXME: for now, this does only one level - parent=$(dirname "$dst_dir") - for dot_ig in x $vc_ignore; do - test $dot_ig = x && continue - ig=$parent/$dot_ig - insert_vc_ignore $ig "${dst_dir##*/}" - done - fi - - if $copy; then - { - test ! -h "$dst" || { - echo "$me: rm -f $dst" && - rm -f "$dst" - } - } && - test -f "$dst" && - cmp -s "$src" "$dst" || { - echo "$me: cp -fp $src $dst" && - cp -fp "$src" "$dst" - } - else - # Leave any existing symlink alone, if it already points to the source, - # so that broken build tools that care about symlink times - # aren't confused into doing unnecessary builds. Conversely, if the - # existing symlink's timestamp is older than the source, make it afresh, - # so that broken tools aren't confused into skipping needed builds. See - # . - test -h "$dst" && - src_ls=$(ls -diL "$src" 2>/dev/null) && set $src_ls && src_i=$1 && - dst_ls=$(ls -diL "$dst" 2>/dev/null) && set $dst_ls && dst_i=$1 && - test "$src_i" = "$dst_i" && - both_ls=$(ls -dt "$src" "$dst") && - test "X$both_ls" = "X$dst$nl$src" || { - dot_dots= - case $src in - /*) ;; - *) - case /$dst/ in - *//* | */../* | */./* | /*/*/*/*/*/) - die "invalid symlink calculation: $src -> $dst";; - /*/*/*/*/) dot_dots=../../../;; - /*/*/*/) dot_dots=../../;; - /*/*/) dot_dots=../;; - esac;; - esac - - echo "$me: ln -fs $dot_dots$src $dst" && - ln -fs "$dot_dots$src" "$dst" - } - fi - } -} - version_controlled_file() { parent=$1 file=$2 @@ -970,7 +1015,7 @@ bootstrap_post_import_hook \ # Uninitialized submodules are listed with an initial dash. if $use_git && git submodule | grep '^-' >/dev/null; then die "some git submodules are not initialized. " \ - "Run 'git submodule init' and bootstrap again." + "Run 'git submodule update --init' and bootstrap again." fi # Remove any dangling symlink matching "*.m4" or "*.[ch]" in some @@ -1064,7 +1109,7 @@ bootstrap_epilogue echo "$0: done. Now you can run './configure'." -# Local variables: +# Local Variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" diff --git a/bootstrap.conf b/bootstrap.conf index 71ce943c7d..e4e5f3750a 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -1,6 +1,6 @@ # Bootstrap configuration. -# Copyright (C) 2006-2019 Free Software Foundation, Inc. +# Copyright (C) 2006-2022 Free Software Foundation, Inc. # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -16,11 +16,10 @@ # along with this program. If not, see . -GNULIB_REVISION=d271f868a8df9bbec29049d01e056481b7a1a263 +GNULIB_REVISION=9f48fb992a3d7e96610c4ce8be969cff2d61a01b # gnulib modules used by this package. -# mbswidth is used by gnulib-fix-width.diff's changes to argp rather than -# directly. +# mbswidth is used by fix-width.diff's changes to argp rather than directly. gnulib_modules=" argp base64 @@ -67,8 +66,8 @@ SKIP_PO=t # Build prerequisites buildreq="\ -autoconf 2.63 -automake 1.11 +autoconf 2.64 +automake 1.14 gettext 0.18.3 git 1.5.5 tar - @@ -80,11 +79,12 @@ cp -a INSTALL INSTALL.grub bootstrap_post_import_hook () { set -e - for patchname in fix-null-deref fix-null-state-deref fix-regcomp-uninit-token \ - fix-regexec-null-deref fix-uninit-structure fix-unused-value fix-width; do - patch -d grub-core/lib/gnulib -p2 \ - < "grub-core/lib/gnulib-patches/$patchname.patch" - done + + # Instead of patching our gnulib and therefore maintaining a fork, submit + # changes to gnulib and update the hash above when they've merged. Do not + # add new patches here. + patch -d grub-core/lib/gnulib -p2 < grub-core/lib/gnulib-patches/fix-width.patch + for patchname in \ 0001-Support-POTFILES-shell \ 0002-Handle-gettext_printf-shell-function \ diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index 5eef708338..26ac8765e3 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -31,12 +31,6 @@ EXTRA_DIST += grub-core/gensymlist.sh EXTRA_DIST += grub-core/genemuinit.sh EXTRA_DIST += grub-core/genemuinitheader.sh -EXTRA_DIST += grub-core/lib/gnulib-patches/fix-null-deref.patch -EXTRA_DIST += grub-core/lib/gnulib-patches/fix-null-state-deref.patch -EXTRA_DIST += grub-core/lib/gnulib-patches/fix-regcomp-uninit-token.patch -EXTRA_DIST += grub-core/lib/gnulib-patches/fix-regexec-null-deref.patch -EXTRA_DIST += grub-core/lib/gnulib-patches/fix-uninit-structure.patch -EXTRA_DIST += grub-core/lib/gnulib-patches/fix-unused-value.patch EXTRA_DIST += grub-core/lib/gnulib-patches/fix-width.patch EXTRA_DIST += grub-core/lib/libgcrypt diff --git a/config.h.in b/config.h.in index c3134309c6..512d1bbe13 100644 --- a/config.h.in +++ b/config.h.in @@ -67,10 +67,78 @@ # define _GNU_SOURCE 1 # ifndef _GL_INLINE_HEADER_BEGIN +/* gnulib gets configured against the host, not the target, and the rest of + * our buildsystem works around that. This is difficult to avoid as gnulib's + * detection requires a more capable system than our target. Instead, we + * reach in and set values appropriately - intentionally setting more than the + * bare minimum. If, when updating gnulib, something breaks, there's probably + * a change needed here or in grub-core/Makefile.core.def. */ +# define SIZE_MAX ((size_t) -1) +# define _GL_ATTRIBUTE_ALLOC_SIZE(args) \ + __attribute__ ((__alloc_size__ args)) +# define _GL_ATTRIBUTE_ALWAYS_INLINE __attribute__ ((__always_inline__)) +# define _GL_ATTRIBUTE_ARTIFICIAL __attribute__ ((__artificial__)) +# define _GL_ATTRIBUTE_COLD __attribute__ ((cold)) # define _GL_ATTRIBUTE_CONST __attribute__ ((const)) +# define _GL_ATTRIBUTE_DEALLOC(f, i) __attribute ((__malloc__ (f, i))) +# define _GL_ATTRIBUTE_DEALLOC_FREE _GL_ATTRIBUTE_DEALLOC (free, 1) +# define _GL_ATTRIBUTE_DEPRECATED __attribute__ ((__deprecated__)) +# define _GL_ATTRIBUTE_ERROR(msg) __attribute__ ((__error__ (msg))) +# define _GL_ATTRIBUTE_EXTERNALLY_VISIBLE \ + __attribute__ ((externally_visible)) +# define _GL_ATTRIBUTE_FORMAT(spec) __attribute__ ((__format__ spec)) +# define _GL_ATTRIBUTE_LEAF __attribute__ ((__leaf__)) +# define _GL_ATTRIBUTE_MALLOC __attribute__ ((malloc)) +# define _GL_ATTRIBUTE_MAYBE_UNUSED _GL_ATTRIBUTE_UNUSED +# define _GL_ATTRIBUTE_MAY_ALIAS __attribute__ ((__may_alias__)) +# define _GL_ATTRIBUTE_NODISCARD __attribute__ ((__warn_unused_result__)) +# define _GL_ATTRIBUTE_NOINLINE __attribute__ ((__noinline__)) +# define _GL_ATTRIBUTE_NONNULL(args) __attribute__ ((__nonnull__ args)) +# define _GL_ATTRIBUTE_NONSTRING __attribute__ ((__nonstring__)) +# define _GL_ATTRIBUTE_PACKED __attribute__ ((__packed__)) +# define _GL_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# define _GL_ATTRIBUTE_RETURNS_NONNULL \ + __attribute__ ((__returns_nonnull__)) +# define _GL_ATTRIBUTE_SENTINEL(pos) __attribute__ ((__sentinel__ pos)) +# define _GL_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# define _GL_ATTRIBUTE_WARNING(msg) __attribute__ ((__warning__ (msg))) +# define _GL_CMP(n1, n2) (((n1) > (n2)) - ((n1) < (n2))) +# define _GL_GNUC_PREREQ GNUC_PREREQ +# define _GL_INLINE inline +# define _GL_UNUSED_LABEL _GL_ATTRIBUTE_UNUSED + +/* We can't use __has_attribute for these because gcc-5.1 is too old for + * that. Everything above is present in that version, though. */ +# if __GNUC__ >= 7 +# define _GL_ATTRIBUTE_FALLTHROUGH __attribute__ ((fallthrough)) +# else +# define _GL_ATTRIBUTE_FALLTHROUGH /* empty */ +# endif + +# ifndef ASM_FILE +typedef __INT_FAST32_TYPE__ int_fast32_t; +typedef __UINT_FAST32_TYPE__ uint_fast32_t; +# endif + +/* Ensure ialloc nests static/non-static inline properly. */ +# define IALLOC_INLINE static inline + +/* gnulib uses these for blocking out warnings they can't/won't fix. gnulib + * also makes the decision about whether to provide a declaration for + * reallocarray() at compile-time, so this is a convenient place to override - + * it's used by the ialloc module, which is used by base64. */ +# define _GL_INLINE_HEADER_BEGIN _Pragma ("GCC diagnostic push") \ + void * \ + reallocarray (void *ptr, unsigned int nmemb, unsigned int size); +# define _GL_INLINE_HEADER_END _Pragma ("GCC diagnostic pop") /* We don't have an abort() for gnulib to call in regexp. */ # define abort __builtin_unreachable # endif /* !_GL_INLINE_HEADER_BEGIN */ +/* gnulib doesn't build cleanly with older compilers. */ +# if __GNUC__ < 11 +_Pragma ("GCC diagnostic ignored \"-Wtype-limits\"") +# endif + #endif diff --git a/configure.ac b/configure.ac index 40c4338bce..79f45ef1e1 100644 --- a/configure.ac +++ b/configure.ac @@ -49,7 +49,7 @@ AC_CANONICAL_TARGET program_prefix="${save_program_prefix}" AM_INIT_AUTOMAKE([1.11]) -AC_PREREQ(2.63) +AC_PREREQ(2.64) AC_CONFIG_SRCDIR([include/grub/dl.h]) AC_CONFIG_HEADER([config-util.h]) diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 08ac0fb15f..ec1ec5083b 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -762,6 +762,9 @@ module = { name = regexp; common = commands/regexp.c; common = commands/wildcard.c; + common = lib/gnulib/malloc/dynarray_finalize.c; + common = lib/gnulib/malloc/dynarray_emplace_enlarge.c; + common = lib/gnulib/malloc/dynarray_resize.c; common = lib/gnulib/regex.c; cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)'; cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)'; diff --git a/grub-core/disk/luks2.c b/grub-core/disk/luks2.c index 371a53b837..c917a5f91e 100644 --- a/grub-core/disk/luks2.c +++ b/grub-core/disk/luks2.c @@ -389,7 +389,7 @@ luks2_verify_key (grub_luks2_digest_t *d, grub_uint8_t *candidate_key, { grub_uint8_t candidate_digest[GRUB_CRYPTODISK_MAX_KEYLEN]; grub_uint8_t digest[GRUB_CRYPTODISK_MAX_KEYLEN], salt[GRUB_CRYPTODISK_MAX_KEYLEN]; - grub_size_t saltlen = sizeof (salt), digestlen = sizeof (digest); + idx_t saltlen = sizeof (salt), digestlen = sizeof (digest); const gcry_md_spec_t *hash; gcry_err_code_t gcry_ret; @@ -428,7 +428,7 @@ luks2_decrypt_key (grub_uint8_t *out_key, grub_uint8_t area_key[GRUB_CRYPTODISK_MAX_KEYLEN]; grub_uint8_t salt[GRUB_CRYPTODISK_MAX_KEYLEN]; grub_uint8_t *split_key = NULL; - grub_size_t saltlen = sizeof (salt); + idx_t saltlen = sizeof (salt); char cipher[32], *p; const gcry_md_spec_t *hash; gcry_err_code_t gcry_ret; diff --git a/grub-core/lib/gnulib-patches/fix-null-deref.patch b/grub-core/lib/gnulib-patches/fix-null-deref.patch deleted file mode 100644 index 8fafa153a4..0000000000 --- a/grub-core/lib/gnulib-patches/fix-null-deref.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/lib/argp-parse.c b/lib/argp-parse.c -index 6dec57310..900adad54 100644 ---- a/lib/argp-parse.c -+++ b/lib/argp-parse.c -@@ -940,7 +940,7 @@ weak_alias (__argp_parse, argp_parse) - void * - __argp_input (const struct argp *argp, const struct argp_state *state) - { -- if (state) -+ if (state && state->pstate) - { - struct group *group; - struct parser *parser = state->pstate; diff --git a/grub-core/lib/gnulib-patches/fix-null-state-deref.patch b/grub-core/lib/gnulib-patches/fix-null-state-deref.patch deleted file mode 100644 index 813ec09c8a..0000000000 --- a/grub-core/lib/gnulib-patches/fix-null-state-deref.patch +++ /dev/null @@ -1,12 +0,0 @@ ---- a/lib/argp-help.c 2020-10-28 14:32:19.189215988 +0000 -+++ b/lib/argp-help.c 2020-10-28 14:38:21.204673940 +0000 -@@ -145,7 +145,8 @@ - if (*(int *)((char *)upptr + up->uparams_offs) >= upptr->rmargin) - { - __argp_failure (state, 0, 0, -- dgettext (state->root_argp->argp_domain, -+ dgettext (state == NULL ? NULL -+ : state->root_argp->argp_domain, - "\ - ARGP_HELP_FMT: %s value is less than or equal to %s"), - "rmargin", up->name); diff --git a/grub-core/lib/gnulib-patches/fix-regcomp-uninit-token.patch b/grub-core/lib/gnulib-patches/fix-regcomp-uninit-token.patch deleted file mode 100644 index 02e06315df..0000000000 --- a/grub-core/lib/gnulib-patches/fix-regcomp-uninit-token.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- a/lib/regcomp.c 2020-11-24 17:06:08.159223858 +0000 -+++ b/lib/regcomp.c 2020-11-24 17:06:15.630253923 +0000 -@@ -3808,11 +3808,7 @@ - create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right, - re_token_type_t type) - { -- re_token_t t; --#if defined GCC_LINT || defined lint -- memset (&t, 0, sizeof t); --#endif -- t.type = type; -+ re_token_t t = { .type = type }; - return create_token_tree (dfa, left, right, &t); - } - diff --git a/grub-core/lib/gnulib-patches/fix-regexec-null-deref.patch b/grub-core/lib/gnulib-patches/fix-regexec-null-deref.patch deleted file mode 100644 index db6dac9c9e..0000000000 --- a/grub-core/lib/gnulib-patches/fix-regexec-null-deref.patch +++ /dev/null @@ -1,12 +0,0 @@ ---- a/lib/regexec.c 2020-10-21 14:25:35.310195912 +0000 -+++ b/lib/regexec.c 2020-11-05 10:55:09.621542984 +0000 -@@ -1692,6 +1692,9 @@ - { - Idx top = mctx->state_log_top; - -+ if (mctx->state_log == NULL) -+ return REG_NOERROR; -+ - if ((next_state_log_idx >= mctx->input.bufs_len - && mctx->input.bufs_len < mctx->input.len) - || (next_state_log_idx >= mctx->input.valid_len diff --git a/grub-core/lib/gnulib-patches/fix-uninit-structure.patch b/grub-core/lib/gnulib-patches/fix-uninit-structure.patch deleted file mode 100644 index 7b4d9f67af..0000000000 --- a/grub-core/lib/gnulib-patches/fix-uninit-structure.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/lib/regcomp.c 2020-10-22 13:49:06.770168928 +0000 -+++ b/lib/regcomp.c 2020-10-22 13:50:37.026528298 +0000 -@@ -3662,7 +3662,7 @@ - Idx alloc = 0; - #endif /* not RE_ENABLE_I18N */ - reg_errcode_t ret; -- re_token_t br_token; -+ re_token_t br_token = {0}; - bin_tree_t *tree; - - sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); diff --git a/grub-core/lib/gnulib-patches/fix-unused-value.patch b/grub-core/lib/gnulib-patches/fix-unused-value.patch deleted file mode 100644 index ba51f1bf22..0000000000 --- a/grub-core/lib/gnulib-patches/fix-unused-value.patch +++ /dev/null @@ -1,14 +0,0 @@ ---- a/lib/regexec.c 2020-10-21 14:25:35.310195912 +0000 -+++ b/lib/regexec.c 2020-10-21 14:32:07.961765604 +0000 -@@ -828,7 +828,11 @@ - break; - if (__glibc_unlikely (err != REG_NOMATCH)) - goto free_return; -+#ifdef DEBUG -+ /* Only used for assertion below when DEBUG is set, otherwise -+ it will be over-written when we loop around. */ - match_last = -1; -+#endif - } - else - break; /* We found a match. */ diff --git a/grub-core/lib/posix_wrap/limits.h b/grub-core/lib/posix_wrap/limits.h index 591dbf3289..4be7b40806 100644 --- a/grub-core/lib/posix_wrap/limits.h +++ b/grub-core/lib/posix_wrap/limits.h @@ -25,7 +25,11 @@ #define USHRT_MAX GRUB_USHRT_MAX #define UINT_MAX GRUB_UINT_MAX #define ULONG_MAX GRUB_ULONG_MAX -#define SIZE_MAX GRUB_SIZE_MAX + +/* gnulib also defines this type */ +#ifndef SIZE_MAX +# define SIZE_MAX GRUB_SIZE_MAX +#endif #define SCHAR_MIN GRUB_SCHAR_MIN #define SCHAR_MAX GRUB_SCHAR_MAX diff --git a/include/grub/compiler.h b/include/grub/compiler.h index ebafec6895..441a9eca07 100644 --- a/include/grub/compiler.h +++ b/include/grub/compiler.h @@ -30,10 +30,10 @@ /* Does this compiler support compile-time error attributes? */ #if GNUC_PREREQ(4,3) -# define ATTRIBUTE_ERROR(msg) \ +# define GRUB_ATTRIBUTE_ERROR(msg) \ __attribute__ ((__error__ (msg))) #else -# define ATTRIBUTE_ERROR(msg) __attribute__ ((noreturn)) +# define GRUB_ATTRIBUTE_ERROR(msg) __attribute__ ((noreturn)) #endif #if GNUC_PREREQ(4,4) diff --git a/include/grub/list.h b/include/grub/list.h index b13acb9624..21f4b4b44a 100644 --- a/include/grub/list.h +++ b/include/grub/list.h @@ -40,7 +40,7 @@ void EXPORT_FUNC(grub_list_remove) (grub_list_t item); static inline void * grub_bad_type_cast_real (int line, const char *file) - ATTRIBUTE_ERROR ("bad type cast between incompatible grub types"); + GRUB_ATTRIBUTE_ERROR ("bad type cast between incompatible grub types"); static inline void * grub_bad_type_cast_real (int line, const char *file) From 8569c33d05478c3fbe2a83afa5ab860512f29e2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Tue, 8 Feb 2022 08:39:10 +0100 Subject: [PATCH 203/367] commands/search: Fix bug stopping iteration when --no-floppy is used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using --no-floppy and a floppy was encountered, iterate_device() was returning 1, causing the iteration to stop instead of continuing. Signed-off-by: Renaud Métrich Reviewed-by: Daniel Kiper (cherry picked from commit 68ba54c2298604146be83cae144dafd1cfd1fe2d) Signed-off-by: Robbie Harwood --- grub-core/commands/search.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c index ed090b3af8..51656e361c 100644 --- a/grub-core/commands/search.c +++ b/grub-core/commands/search.c @@ -64,7 +64,7 @@ iterate_device (const char *name, void *data) /* Skip floppy drives when requested. */ if (ctx->no_floppy && name[0] == 'f' && name[1] == 'd' && name[2] >= '0' && name[2] <= '9') - return 1; + return 0; #ifdef DO_SEARCH_FS_UUID #define compare_fn grub_strcasecmp From 232c03310ea2e2adba40978ec998a7fdbac49113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Tue, 8 Feb 2022 08:39:11 +0100 Subject: [PATCH 204/367] search: new --efidisk-only option on EFI systems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using 'search' on EFI systems, we sometimes want to exclude devices that are not EFI disks (e.g. md, lvm). This is typically used when wanting to chainload when having a software raid (md) for EFI partition: with no option, 'search --file /EFI/redhat/shimx64.efi' sets root envvar to 'md/boot_efi' which cannot be used for chainloading since there is no effective EFI device behind. This commit also refactors handling of --no-floppy option. Signed-off-by: Renaud Métrich [rharwood: apply rmetrich's flags initialization fix] Signed-off-by: Robbie Harwood --- grub-core/commands/search.c | 27 +++++++++++++++++++++++---- grub-core/commands/search_wrap.c | 18 ++++++++++++------ include/grub/search.h | 15 ++++++++++++--- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c index 51656e361c..57d26ced8a 100644 --- a/grub-core/commands/search.c +++ b/grub-core/commands/search.c @@ -47,7 +47,7 @@ struct search_ctx { const char *key; const char *var; - int no_floppy; + enum search_flags flags; char **hints; unsigned nhints; int count; @@ -62,10 +62,29 @@ iterate_device (const char *name, void *data) int found = 0; /* Skip floppy drives when requested. */ - if (ctx->no_floppy && + if (ctx->flags & SEARCH_FLAGS_NO_FLOPPY && name[0] == 'f' && name[1] == 'd' && name[2] >= '0' && name[2] <= '9') return 0; + /* Limit to EFI disks when requested. */ + if (ctx->flags & SEARCH_FLAGS_EFIDISK_ONLY) + { + grub_device_t dev; + dev = grub_device_open (name); + if (! dev) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (! dev->disk || dev->disk->dev->id != GRUB_DISK_DEVICE_EFIDISK_ID) + { + grub_device_close (dev); + grub_errno = GRUB_ERR_NONE; + return 0; + } + grub_device_close (dev); + } + #ifdef DO_SEARCH_FS_UUID #define compare_fn grub_strcasecmp #else @@ -261,13 +280,13 @@ try (struct search_ctx *ctx) } void -FUNC_NAME (const char *key, const char *var, int no_floppy, +FUNC_NAME (const char *key, const char *var, enum search_flags flags, char **hints, unsigned nhints) { struct search_ctx ctx = { .key = key, .var = var, - .no_floppy = no_floppy, + .flags = flags, .hints = hints, .nhints = nhints, .count = 0, diff --git a/grub-core/commands/search_wrap.c b/grub-core/commands/search_wrap.c index 47fc8eb996..0b62acf853 100644 --- a/grub-core/commands/search_wrap.c +++ b/grub-core/commands/search_wrap.c @@ -40,6 +40,7 @@ static const struct grub_arg_option options[] = N_("Set a variable to the first device found."), N_("VARNAME"), ARG_TYPE_STRING}, {"no-floppy", 'n', 0, N_("Do not probe any floppy drive."), 0, 0}, + {"efidisk-only", 0, 0, N_("Only probe EFI disks."), 0, 0}, {"hint", 'h', GRUB_ARG_OPTION_REPEATABLE, N_("First try the device HINT. If HINT ends in comma, " "also try subpartitions"), N_("HINT"), ARG_TYPE_STRING}, @@ -73,6 +74,7 @@ enum options SEARCH_FS_UUID, SEARCH_SET, SEARCH_NO_FLOPPY, + SEARCH_EFIDISK_ONLY, SEARCH_HINT, SEARCH_HINT_IEEE1275, SEARCH_HINT_BIOS, @@ -89,6 +91,7 @@ grub_cmd_search (grub_extcmd_context_t ctxt, int argc, char **args) const char *id = 0; int i = 0, j = 0, nhints = 0; char **hints = NULL; + enum search_flags flags = 0; if (state[SEARCH_HINT].set) for (i = 0; state[SEARCH_HINT].args[i]; i++) @@ -180,15 +183,18 @@ grub_cmd_search (grub_extcmd_context_t ctxt, int argc, char **args) goto out; } + if (state[SEARCH_NO_FLOPPY].set) + flags |= SEARCH_FLAGS_NO_FLOPPY; + + if (state[SEARCH_EFIDISK_ONLY].set) + flags |= SEARCH_FLAGS_EFIDISK_ONLY; + if (state[SEARCH_LABEL].set) - grub_search_label (id, var, state[SEARCH_NO_FLOPPY].set, - hints, nhints); + grub_search_label (id, var, flags, hints, nhints); else if (state[SEARCH_FS_UUID].set) - grub_search_fs_uuid (id, var, state[SEARCH_NO_FLOPPY].set, - hints, nhints); + grub_search_fs_uuid (id, var, flags, hints, nhints); else if (state[SEARCH_FILE].set) - grub_search_fs_file (id, var, state[SEARCH_NO_FLOPPY].set, - hints, nhints); + grub_search_fs_file (id, var, flags, hints, nhints); else grub_error (GRUB_ERR_INVALID_COMMAND, "unspecified search type"); diff --git a/include/grub/search.h b/include/grub/search.h index d80347df34..4190aeb2cb 100644 --- a/include/grub/search.h +++ b/include/grub/search.h @@ -19,11 +19,20 @@ #ifndef GRUB_SEARCH_HEADER #define GRUB_SEARCH_HEADER 1 -void grub_search_fs_file (const char *key, const char *var, int no_floppy, +enum search_flags + { + SEARCH_FLAGS_NO_FLOPPY = 1, + SEARCH_FLAGS_EFIDISK_ONLY = 2 + }; + +void grub_search_fs_file (const char *key, const char *var, + enum search_flags flags, char **hints, unsigned nhints); -void grub_search_fs_uuid (const char *key, const char *var, int no_floppy, +void grub_search_fs_uuid (const char *key, const char *var, + enum search_flags flags, char **hints, unsigned nhints); -void grub_search_label (const char *key, const char *var, int no_floppy, +void grub_search_label (const char *key, const char *var, + enum search_flags flags, char **hints, unsigned nhints); #endif From 25045ec75ce4c84ccaf0c1cf3cdbe0c0af2574d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Tue, 15 Feb 2022 14:05:22 +0100 Subject: [PATCH 205/367] efi: new 'connectefi' command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When efi.quickboot is enabled on VMWare (which is the default for hardware release 16 and later), it may happen that not all EFI devices are connected. Due to this, browsing the devices in make_devices() just fails to find devices, in particular disks or partitions for a given disk. This typically happens when network booting, then trying to chainload to local disk (this is used in deployment tools such as Red Hat Satellite), which is done through using the following grub.cfg snippet: -------- 8< ---------------- 8< ---------------- 8< -------- unset prefix search --file --set=prefix /EFI/redhat/grubx64.efi if [ -n "$prefix" ]; then chainloader ($prefix)/EFI/redhat/grubx64/efi ... -------- 8< ---------------- 8< ---------------- 8< -------- With efi.quickboot, none of the devices are connected, causing "search" to fail. Sometimes devices are connected but not the partition of the disk matching $prefix, causing partition to not be found by "chainloader". This patch introduces a new "connectefi pciroot|scsi" command which recursively connects all EFI devices starting from a given controller type: - if 'pciroot' is specified, recursion is performed for all PCI root handles - if 'scsi' is specified, recursion is performed for all SCSI I/O handles (recommended usage to avoid connecting unwanted handles which may impact Grub performances) Typical grub.cfg snippet would then be: -------- 8< ---------------- 8< ---------------- 8< -------- connectefi scsi unset prefix search --file --set=prefix /EFI/redhat/grubx64.efi if [ -n "$prefix" ]; then chainloader ($prefix)/EFI/redhat/grubx64/efi ... -------- 8< ---------------- 8< ---------------- 8< -------- The code is easily extensible to handle other arguments in the future if needed. Signed-off-by: Renaud Métrich Signed-off-by: Robbie Harwood --- NEWS | 2 +- grub-core/Makefile.core.def | 6 + grub-core/commands/efi/connectefi.c | 205 ++++++++++++++++++++++++++++ grub-core/commands/efi/lsefi.c | 1 + grub-core/disk/efi/efidisk.c | 13 ++ grub-core/kern/efi/efi.c | 13 ++ include/grub/efi/disk.h | 2 + include/grub/efi/efi.h | 5 + 8 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 grub-core/commands/efi/connectefi.c diff --git a/NEWS b/NEWS index 73b8492bc4..d7c1d23aed 100644 --- a/NEWS +++ b/NEWS @@ -98,7 +98,7 @@ New in 2.02: * Prefer pmtimer for TSC calibration. * New/improved platform support: - * New `efifwsetup' and `lsefi' commands on EFI platforms. + * New `efifwsetup', `lsefi' and `connectefi` commands on EFI platforms. * New `cmosdump' and `cmosset' commands on platforms with CMOS support. * New command `pcidump' for PCI platforms. * Improve opcode parsing in ACPI halt implementation. diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index ec1ec5083b..741a033978 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -836,6 +836,12 @@ module = { enable = efi; }; +module = { + name = connectefi; + common = commands/efi/connectefi.c; + enable = efi; +}; + module = { name = blocklist; common = commands/blocklist.c; diff --git a/grub-core/commands/efi/connectefi.c b/grub-core/commands/efi/connectefi.c new file mode 100644 index 0000000000..8ab75bd51b --- /dev/null +++ b/grub-core/commands/efi/connectefi.c @@ -0,0 +1,205 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +typedef struct handle_list +{ + grub_efi_handle_t handle; + struct handle_list *next; +} handle_list_t; + +static handle_list_t *already_handled = NULL; + +static grub_err_t +add_handle (grub_efi_handle_t handle) +{ + handle_list_t *e; + e = grub_malloc (sizeof (*e)); + if (! e) + return grub_errno; + e->handle = handle; + e->next = already_handled; + already_handled = e; + return GRUB_ERR_NONE; +} + +static int +is_in_list (grub_efi_handle_t handle) +{ + handle_list_t *e; + for (e = already_handled; e != NULL; e = e->next) + if (e->handle == handle) + return 1; + return 0; +} + +static void +free_handle_list (void) +{ + handle_list_t *e; + while ((e = already_handled) != NULL) + { + already_handled = already_handled->next; + grub_free (e); + } +} + +typedef enum searched_item_flag +{ + SEARCHED_ITEM_FLAG_LOOP = 1, + SEARCHED_ITEM_FLAG_RECURSIVE = 2 +} searched_item_flags; + +typedef struct searched_item +{ + grub_efi_guid_t guid; + const char *name; + searched_item_flags flags; +} searched_items; + +static grub_err_t +grub_cmd_connectefi (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + unsigned s; + searched_items pciroot_items[] = + { + { GRUB_EFI_PCI_ROOT_IO_GUID, "PCI root", SEARCHED_ITEM_FLAG_RECURSIVE } + }; + searched_items scsi_items[] = + { + { GRUB_EFI_PCI_ROOT_IO_GUID, "PCI root", 0 }, + { GRUB_EFI_PCI_IO_GUID, "PCI", SEARCHED_ITEM_FLAG_LOOP }, + { GRUB_EFI_SCSI_IO_PROTOCOL_GUID, "SCSI I/O", SEARCHED_ITEM_FLAG_RECURSIVE } + }; + searched_items *items = NULL; + unsigned nitems = 0; + grub_err_t grub_err = GRUB_ERR_NONE; + unsigned total_connected = 0; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + if (grub_strcmp(args[0], N_("pciroot")) == 0) + { + items = pciroot_items; + nitems = ARRAY_SIZE (pciroot_items); + } + else if (grub_strcmp(args[0], N_("scsi")) == 0) + { + items = scsi_items; + nitems = ARRAY_SIZE (scsi_items); + } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unexpected argument `%s'"), args[0]); + + for (s = 0; s < nitems; s++) + { + grub_efi_handle_t *handles; + grub_efi_uintn_t num_handles; + unsigned i, connected = 0, loop = 0; + +loop: + loop++; + grub_dprintf ("efi", "step '%s' loop %d:\n", items[s].name, loop); + + handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, + &items[s].guid, 0, &num_handles); + + if (!handles) + continue; + + for (i = 0; i < num_handles; i++) + { + grub_efi_handle_t handle = handles[i]; + grub_efi_status_t status; + unsigned j; + + /* Skip already handled handles */ + if (is_in_list (handle)) + { + grub_dprintf ("efi", " handle %p: already processed\n", + handle); + continue; + } + + status = grub_efi_connect_controller(handle, NULL, NULL, + items[s].flags & SEARCHED_ITEM_FLAG_RECURSIVE ? 1 : 0); + if (status == GRUB_EFI_SUCCESS) + { + connected++; + total_connected++; + grub_dprintf ("efi", " handle %p: connected\n", handle); + } + else + grub_dprintf ("efi", " handle %p: failed to connect (%d)\n", + handle, (grub_efi_int8_t) status); + + if ((grub_err = add_handle (handle)) != GRUB_ERR_NONE) + break; /* fatal */ + } + + grub_free (handles); + if (grub_err != GRUB_ERR_NONE) + break; /* fatal */ + + if (items[s].flags & SEARCHED_ITEM_FLAG_LOOP && connected) + { + connected = 0; + goto loop; + } + + free_handle_list (); + } + + free_handle_list (); + + if (total_connected) + grub_efidisk_reenumerate_disks (); + + return grub_err; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(connectefi) +{ + cmd = grub_register_command ("connectefi", grub_cmd_connectefi, + N_("pciroot|scsi"), + N_("Connect EFI handles." + " If 'pciroot' is specified, connect PCI" + " root EFI handles recursively." + " If 'scsi' is specified, connect SCSI" + " I/O EFI handles recursively.")); +} + +GRUB_MOD_FINI(connectefi) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/commands/efi/lsefi.c b/grub-core/commands/efi/lsefi.c index d1ce99af43..f2d2430e66 100644 --- a/grub-core/commands/efi/lsefi.c +++ b/grub-core/commands/efi/lsefi.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/grub-core/disk/efi/efidisk.c b/grub-core/disk/efi/efidisk.c index fe8ba6e6c9..062143dfff 100644 --- a/grub-core/disk/efi/efidisk.c +++ b/grub-core/disk/efi/efidisk.c @@ -396,6 +396,19 @@ enumerate_disks (void) free_devices (devices); } +void +grub_efidisk_reenumerate_disks (void) +{ + free_devices (fd_devices); + free_devices (hd_devices); + free_devices (cd_devices); + fd_devices = 0; + hd_devices = 0; + cd_devices = 0; + + enumerate_disks (); +} + static int grub_efidisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, grub_disk_pull_t pull) diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index 14bc10eb56..7fcca69c17 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -95,6 +95,19 @@ grub_efi_locate_handle (grub_efi_locate_search_type_t search_type, return buffer; } +grub_efi_status_t +grub_efi_connect_controller (grub_efi_handle_t controller_handle, + grub_efi_handle_t *driver_image_handle, + grub_efi_device_path_protocol_t *remaining_device_path, + grub_efi_boolean_t recursive) +{ + grub_efi_boot_services_t *b; + + b = grub_efi_system_table->boot_services; + return efi_call_4 (b->connect_controller, controller_handle, + driver_image_handle, remaining_device_path, recursive); +} + void * grub_efi_open_protocol (grub_efi_handle_t handle, grub_efi_guid_t *protocol, diff --git a/include/grub/efi/disk.h b/include/grub/efi/disk.h index 254475c842..6845c2f1fd 100644 --- a/include/grub/efi/disk.h +++ b/include/grub/efi/disk.h @@ -27,6 +27,8 @@ grub_efi_handle_t EXPORT_FUNC(grub_efidisk_get_device_handle) (grub_disk_t disk); char *EXPORT_FUNC(grub_efidisk_get_device_name) (grub_efi_handle_t *handle); +void EXPORT_FUNC(grub_efidisk_reenumerate_disks) (void); + void grub_efidisk_init (void); void grub_efidisk_fini (void); diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index 8dfc89a33b..ec52083c49 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -41,6 +41,11 @@ EXPORT_FUNC(grub_efi_locate_handle) (grub_efi_locate_search_type_t search_type, grub_efi_guid_t *protocol, void *search_key, grub_efi_uintn_t *num_handles); +grub_efi_status_t +EXPORT_FUNC(grub_efi_connect_controller) (grub_efi_handle_t controller_handle, + grub_efi_handle_t *driver_image_handle, + grub_efi_device_path_protocol_t *remaining_device_path, + grub_efi_boolean_t recursive); void *EXPORT_FUNC(grub_efi_open_protocol) (grub_efi_handle_t handle, grub_efi_guid_t *protocol, grub_efi_uint32_t attributes); From 6e49b9a7fe91325a9a7b48f96a956bf5f8061e40 Mon Sep 17 00:00:00 2001 From: Dimitri John Ledkov Date: Thu, 3 Mar 2022 13:10:56 +0100 Subject: [PATCH 206/367] grub-core/loader/i386/efi/linux.c: do not validate kernels twice On codebases that have shim-lock-verifier built into the grub core (like 2.06 upstream), shim-lock-verifier is in enforcing mode when booted with secureboot. It means that grub_cmd_linux() command attempts to perform shim validate upon opening linux kernel image, including kernel measurement. And the verifier correctly returns file open error when shim validate protocol is not present or shim fails to validate the kernel. This makes the call to grub_linuxefi_secure_validate() redundant, but also harmful. As validating the kernel image twice, extends the PCRs with the same measurement twice. Which breaks existing sealing policies when upgrading from grub2.04+rhboot+sb+linuxefi to grub2.06+rhboot+sb+linuxefi builds. It is also incorrect to measure the kernel twice. This patch must not be ported to older editions of grub code bases that do not have verifiers framework, or it is not builtin, or shim-lock-verifier is an optional module. This patch is tested to ensure that unsigned kernels are not possible to boot in secureboot mode when shim rejects kernel, or shim protocol is missing, and that the measurements become stable once again. The above also ensures that CVE-2020-15705 is not reintroduced. Signed-off-by: Dimitri John Ledkov --- grub-core/loader/i386/efi/linux.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 3cf0f9b330..941df6400b 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -30,7 +30,6 @@ #include #include #include -#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -278,7 +277,6 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_ssize_t start, filelen; void *kernel = NULL; int setup_header_end_offset; - int rc; grub_dl_ref (my_mod); @@ -308,17 +306,6 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } - if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) - { - rc = grub_linuxefi_secure_validate (kernel, filelen); - if (rc <= 0) - { - grub_error (GRUB_ERR_INVALID_COMMAND, - N_("%s has invalid signature"), argv[0]); - goto fail; - } - } - lh = (struct linux_i386_kernel_header *)kernel; grub_dprintf ("linux", "original lh is at %p\n", kernel); From bd78f100fe86e1801fd24b648b08a525cd6d5ad3 Mon Sep 17 00:00:00 2001 From: Dimitri John Ledkov Date: Fri, 4 Mar 2022 11:29:31 +0100 Subject: [PATCH 207/367] grub-core/loader/arm64/linux.c: do not validate kernel twice Call to grub_file_open(, GRUB_FILE_TYPE_LINUX_KERNEL) already passes the kernel file through shim-lock verifier when secureboot is on. Thus there is no need to validate the kernel image again. And when doing so again, duplicate PCR measurement is performed, breaking measurements compatibility with 2.04+linuxefi. This patch must not be ported to older editions of grub code bases that do not have verifiers framework, or it is not builtin, or shim-lock-verifier is an optional module. Signed-off-by: Dimitri John Ledkov --- grub-core/loader/arm64/linux.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c index f18d90bd74..d2af47c2c0 100644 --- a/grub-core/loader/arm64/linux.c +++ b/grub-core/loader/arm64/linux.c @@ -34,7 +34,6 @@ #include #include #include -#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -341,7 +340,6 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_off_t filelen; grub_uint32_t align; void *kernel = NULL; - int rc; grub_dl_ref (my_mod); @@ -370,17 +368,6 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } - if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) - { - rc = grub_linuxefi_secure_validate (kernel, filelen); - if (rc <= 0) - { - grub_error (GRUB_ERR_INVALID_COMMAND, - N_("%s has invalid signature"), argv[0]); - goto fail; - } - } - if (grub_arch_efi_linux_check_image (kernel) != GRUB_ERR_NONE) goto fail; if (parse_pe_header (kernel, &kernel_size, &handover_offset, &align) != GRUB_ERR_NONE) From 93a4ef5f9919d184c7cb6e24daf96cee58aab91e Mon Sep 17 00:00:00 2001 From: Dimitri John Ledkov Date: Fri, 4 Mar 2022 09:31:43 +0100 Subject: [PATCH 208/367] grub-core/loader/efi/chainloader.c: do not validate chainloader twice On secureboot systems, with shimlock verifier, call to grub_file_open(, GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE) will already pass the chainloader target through shim-lock protocol verify call. And create a TPM measurement. If verification fails, grub_cmd_chainloader will fail at file open time. This makes previous code paths for negative, and zero return codes from grub_linuxefi_secure_validate unreachable under secureboot. But also breaking measurements compatibility with 2.04+linuxefi codebases, as the chainloader file is passed through shim_lock->verify() twice (via verifier & direct call to grub_linuxefi_secure_validate) extending the PCRs twice. This reduces grub_loader options to perform grub_secureboot_chainloader when secureboot is on, and otherwise attempt grub_chainloader_boot. It means that booting with secureboot off, yet still with shim (which always verifies things successfully), will stop choosing grub_secureboot_chainloader, and opting for a more regular loadimage/startimage codepath. If we want to use the grub_secureboot_chainloader codepath in such scenarios we should adapt the code to simply check for shim_lock protocol presence / shim_lock->context() success?! But I am not sure if that is necessary. This patch must not be ported to older editions of grub code bases that do not have verifiers framework, or it is not builtin, or shim-lock-verifier is an optional module. Signed-off-by: Dimitri John Ledkov --- grub-core/loader/efi/chainloader.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index 3af6b12292..644cd2e56f 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -906,7 +906,6 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_efi_device_path_t *dp = 0; char *filename; void *boot_image = 0; - int rc; if (argc == 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); @@ -1082,9 +1081,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), orig_dev = 0; } - rc = grub_linuxefi_secure_validate((void *)(unsigned long)address, fsize); - grub_dprintf ("chain", "linuxefi_secure_validate: %d\n", rc); - if (rc > 0) + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) { grub_file_close (file); grub_device_close (dev); @@ -1092,7 +1089,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_secureboot_chainloader_unload, 0); return 0; } - else if (rc == 0) + else { grub_load_and_start_image(boot_image); grub_file_close (file); @@ -1101,7 +1098,6 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), return 0; } - // -1 fall-through to fail fail: if (orig_dev) From 21b4a5e1d2eed94d11c6a9f22290ab9fdae80c10 Mon Sep 17 00:00:00 2001 From: Dimitri John Ledkov Date: Fri, 4 Mar 2022 11:36:09 +0100 Subject: [PATCH 209/367] grub-core/loader/efi/linux.c: drop now unused grub_linuxefi_secure_validate Drop the now unused grub_linuxefi_secure_validate() as all prior users of this API now rely on the shim-lock-verifier codepath instead. This patch must not be ported to older editions of grub code bases that do not have verifiers framework, or it is not builtin, or shim-lock-verifier is an optional module. Signed-off-by: Dimitri John Ledkov --- grub-core/loader/efi/linux.c | 40 ------------------------------------ include/grub/efi/linux.h | 2 -- 2 files changed, 42 deletions(-) diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c index 9260731c10..9265cf4200 100644 --- a/grub-core/loader/efi/linux.c +++ b/grub-core/loader/efi/linux.c @@ -24,46 +24,6 @@ #include #include -#define SHIM_LOCK_GUID \ - { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} } - -struct grub_efi_shim_lock -{ - grub_efi_status_t (*verify) (void *buffer, grub_uint32_t size); -}; -typedef struct grub_efi_shim_lock grub_efi_shim_lock_t; - -// Returns 1 on success, -1 on error, 0 when not available -int -grub_linuxefi_secure_validate (void *data, grub_uint32_t size) -{ - grub_efi_guid_t guid = SHIM_LOCK_GUID; - grub_efi_shim_lock_t *shim_lock; - grub_efi_status_t status; - - shim_lock = grub_efi_locate_protocol(&guid, NULL); - grub_dprintf ("secureboot", "shim_lock: %p\n", shim_lock); - if (!shim_lock) - { - grub_dprintf ("secureboot", "shim not available\n"); - return 0; - } - - grub_dprintf ("secureboot", "Asking shim to verify kernel signature\n"); - status = shim_lock->verify (data, size); - grub_dprintf ("secureboot", "shim_lock->verify(): %ld\n", (long int)status); - if (status == GRUB_EFI_SUCCESS) - { - grub_dprintf ("secureboot", "Kernel signature verification passed\n"); - return 1; - } - - grub_dprintf ("secureboot", "Kernel signature verification failed (0x%lx)\n", - (unsigned long) status); - - return -1; -} - #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" diff --git a/include/grub/efi/linux.h b/include/grub/efi/linux.h index 0033d9305a..887b02fd9f 100644 --- a/include/grub/efi/linux.h +++ b/include/grub/efi/linux.h @@ -22,8 +22,6 @@ #include #include -int -EXPORT_FUNC(grub_linuxefi_secure_validate) (void *data, grub_uint32_t size); grub_err_t EXPORT_FUNC(grub_efi_linux_boot) (void *kernel_address, grub_off_t offset, void *kernel_param); From 31dd02bd6bc729326f82e84878129a866ec4c96a Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Thu, 24 Mar 2022 14:34:32 +1100 Subject: [PATCH 210/367] powerpc: prefix detection: support device names with commas Frustratingly, the device name itself can contain an embedded comma: e.g /pci@800000020000015/pci1014,034A@0/sas/disk@5000c50098a0ee8b So my previous approach was wrong: we cannot rely upon the presence of a comma to say that a partition has been specified! It turns out for prefixes like (,gpt2)/grub2 we really want to make up a full (device,partition)/patch prefix, because root discovery code in 10_linux will reset the root variable and use search to fill it again. If you have run grub-install, you probably don't have search built in, and if you don't have prefix containing (device,partition), grub will construct ($root)$prefix/powerpc-ieee1275/search.mod - but because $root has just been changed, this will no longer work, and the boot will fail! Retain the gist of the logic, but instead of looking for a comma, look for a leading '('. This matches the earlier code better anyway. There's certainly a better fix to be had. But any time you chose to build with a bare prefix like '/grub2', you're almost certainly going to build in search anyway, so this will do. Signed-off-by: Daniel Axtens --- grub-core/kern/main.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c index 993b8a8598..e94a2f78fb 100644 --- a/grub-core/kern/main.c +++ b/grub-core/kern/main.c @@ -242,14 +242,29 @@ grub_set_prefix_and_root (void) what sorts of paths represent disks with partition tables and those without partition tables. - So we act unless there is a comma in the device, which would indicate - a partition has already been specified. - - (If we only have a path, the code in normal to discover config files - will try both without partitions and then with any partitions so we - will cover both CDs and HDs.) + - Frustratingly, the device name itself can contain an embedded comma: + /pci@800000020000015/pci1014,034A@0/sas/disk@5000c50098a0ee8b + So we cannot even rely upon the presence of a comma to say that a + partition has been specified! + + If we only have a path in $prefix, the code in normal to discover + config files will try all disks, both without partitions and then with + any partitions so we will cover both CDs and HDs. + + However, it doesn't then set the prefix to be something like + (discovered partition)/path, and so it is fragile against runtime + changes to $root. For example some of the stuff done in 10_linux to + reload $root sets root differently and then uses search to find it + again. If the search module is not built in, when we change root, grub + will look in (new root)/path/powerpc-ieee1275, that won't work, and we + will not be able to load the search module and the boot will fail. + + This is particularly likely to hit us in the grub-install + (,msdos2)/grub2 case, so we act unless the supplied prefix starts with + '(', which would likely indicate a partition has already been + specified. */ - if (grub_strchr (device, ',') == NULL) + if (prefix && prefix[0] != '(') grub_env_set ("prefix", path); else #endif From 58b705f175e6de38fd47a3addc5063802537384c Mon Sep 17 00:00:00 2001 From: Diego Domingos Date: Thu, 24 Mar 2022 13:14:42 -0400 Subject: [PATCH 211/367] make ofdisk_retries optional The feature Retry on Fail added to GRUB can cause a LPM to take longer if the SAN is slow. When a LPM to external site occur, the path of the disk can change and thus the disk search function on grub can take some time since it is used as a hint. This can cause the Retry on Fail feature to try to access the disk 20x times (since this is hardcoded number) and, if the SAN is slow, the boot time can increase a lot. In some situations not acceptable. The following patch enables a configuration at user space of the maximum number of retries we want for this feature. The variable ofdisk_retries should be set using grub2-editenv and will be checked by retry function. If the variable is not set, so the default number of retries will be used instead. --- include/grub/ieee1275/ofdisk.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/grub/ieee1275/ofdisk.h b/include/grub/ieee1275/ofdisk.h index 7d2d540930..0074d55eee 100644 --- a/include/grub/ieee1275/ofdisk.h +++ b/include/grub/ieee1275/ofdisk.h @@ -25,7 +25,12 @@ extern void grub_ofdisk_fini (void); #define MAX_RETRIES 20 -#define RETRY_IEEE1275_OFDISK_OPEN(device, last_ihandle) unsigned retry_i=0;for(retry_i=0; retry_i < MAX_RETRIES; retry_i++){ \ +#define RETRY_IEEE1275_OFDISK_OPEN(device, last_ihandle) \ + unsigned max_retries = MAX_RETRIES; \ + if(grub_env_get("ofdisk_retries") != NULL) \ + max_retries = grub_strtoul(grub_env_get("ofdisk_retries"), 0, 10)+1; \ + grub_dprintf("ofdisk","MAX_RETRIES set to %u\n",max_retries); \ + unsigned retry_i=0;for(retry_i=0; retry_i < max_retries; retry_i++){ \ if(!grub_ieee1275_open(device, last_ihandle)) \ break; \ grub_dprintf("ofdisk","Opening disk %s failed. Retrying...\n",device); } From 0bcccb4565486f338e126462f48c846ba15598e4 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Thu, 28 Apr 2022 21:53:36 +0100 Subject: [PATCH 212/367] loader/efi/chainloader: grub_load_and_start_image doesn't load and start grub_load_and_start_image only loads an image - it still requires the caller to start it. This renames it to grub_load_image. It's called from 2 places: - grub_cmd_chainloader when not using the shim protocol. - grub_secureboot_chainloader_boot if handle_image returns an error. In this case, the image is loaded and then nothing else happens which seems strange. I assume the intention is that it falls back to LoadImage and StartImage if handle_image fails, so I've made it do that. Signed-off-by: Chris Coulson (cherry picked from commit b4d70820a65c00561045856b7b8355461a9545f6) --- grub-core/loader/efi/chainloader.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index 644cd2e56f..d3bf02ed8a 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -841,7 +841,7 @@ grub_secureboot_chainloader_unload (void) } static grub_err_t -grub_load_and_start_image(void *boot_image) +grub_load_image(void *boot_image) { grub_efi_boot_services_t *b; grub_efi_status_t status; @@ -883,13 +883,23 @@ grub_load_and_start_image(void *boot_image) static grub_err_t grub_secureboot_chainloader_boot (void) { + grub_efi_boot_services_t *b; int rc; + rc = handle_image ((void *)(unsigned long)address, fsize); if (rc == 0) { - grub_load_and_start_image((void *)(unsigned long)address); + /* We weren't able to attempt to execute the image, so fall back + * to LoadImage / StartImage. + */ + rc = grub_load_image((void *)(unsigned long)address); + if (rc == 0) + grub_chainloader_boot (); } + b = grub_efi_system_table->boot_services; + efi_call_1 (b->unload_image, image_handle); + grub_loader_unset (); return grub_errno; } @@ -1091,7 +1101,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), } else { - grub_load_and_start_image(boot_image); + grub_load_image(boot_image); grub_file_close (file); grub_device_close (dev); grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0); From 18fb38468c63b3eedd80064bef2d5f7e61061bc4 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Fri, 29 Apr 2022 21:13:08 +0100 Subject: [PATCH 213/367] loader/efi/chainloader: simplify the loader state When not using the shim lock protocol, the chainloader command retains the source buffer and device path passed to LoadImage, requiring the unload hook passed to grub_loader_set to free them. It isn't required to retain this state though - they aren't required by StartImage or anything else in the boot hook, so clean them up before grub_cmd_chainloader finishes. This also wraps the loader state when using the shim lock protocol inside a struct. Signed-off-by: Chris Coulson (cherry picked from commit fa39862933b3be1553a580a3a5c28073257d8046) [rharwood: fix unitialized handle and double-frees of file/dev] Signed-off-by: Robbie Harwood --- grub-core/loader/efi/chainloader.c | 160 ++++++++++++++++++----------- 1 file changed, 102 insertions(+), 58 deletions(-) diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index d3bf02ed8a..3342492ff1 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -48,38 +48,21 @@ GRUB_MOD_LICENSE ("GPLv3+"); static grub_dl_t my_mod; -static grub_efi_physical_address_t address; -static grub_efi_uintn_t pages; -static grub_ssize_t fsize; -static grub_efi_device_path_t *file_path; static grub_efi_handle_t image_handle; -static grub_efi_char16_t *cmdline; -static grub_ssize_t cmdline_len; -static grub_efi_handle_t dev_handle; -static grub_efi_status_t (*entry_point) (grub_efi_handle_t image_handle, grub_efi_system_table_t *system_table); - -static grub_err_t -grub_chainloader_unload (void) -{ - grub_efi_boot_services_t *b; - - b = grub_efi_system_table->boot_services; - efi_call_1 (b->unload_image, image_handle); - grub_efi_free_pages (address, pages); - - grub_free (file_path); - grub_free (cmdline); - cmdline = 0; - file_path = 0; - dev_handle = 0; - - grub_dl_unref (my_mod); - return GRUB_ERR_NONE; -} +struct grub_secureboot_chainloader_context { + grub_efi_physical_address_t address; + grub_efi_uintn_t pages; + grub_ssize_t fsize; + grub_efi_device_path_t *file_path; + grub_efi_char16_t *cmdline; + grub_ssize_t cmdline_len; + grub_efi_handle_t dev_handle; +}; +static struct grub_secureboot_chainloader_context *sb_context; static grub_err_t -grub_chainloader_boot (void) +grub_start_image (grub_efi_handle_t handle) { grub_efi_boot_services_t *b; grub_efi_status_t status; @@ -87,7 +70,7 @@ grub_chainloader_boot (void) grub_efi_char16_t *exit_data = NULL; b = grub_efi_system_table->boot_services; - status = efi_call_3 (b->start_image, image_handle, &exit_data_size, &exit_data); + status = efi_call_3 (b->start_image, handle, &exit_data_size, &exit_data); if (status != GRUB_EFI_SUCCESS) { if (exit_data) @@ -111,11 +94,37 @@ grub_chainloader_boot (void) if (exit_data) grub_efi_free_pool (exit_data); - grub_loader_unset (); - return grub_errno; } +static grub_err_t +grub_chainloader_unload (void) +{ + grub_efi_loaded_image_t *loaded_image; + grub_efi_boot_services_t *b; + + loaded_image = grub_efi_get_loaded_image (image_handle); + if (loaded_image != NULL) + grub_free (loaded_image->load_options); + + b = grub_efi_system_table->boot_services; + efi_call_1 (b->unload_image, image_handle); + + grub_dl_unref (my_mod); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_chainloader_boot (void) +{ + grub_err_t err; + + err = grub_start_image (image_handle); + + grub_loader_unset (); + return err; +} + static grub_err_t copy_file_path (grub_efi_file_path_device_path_t *fp, const char *str, grub_efi_uint16_t len) @@ -150,7 +159,7 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename) char *dir_start; char *dir_end; grub_size_t size; - grub_efi_device_path_t *d; + grub_efi_device_path_t *d, *file_path; dir_start = grub_strchr (filename, ')'); if (! dir_start) @@ -526,10 +535,12 @@ grub_efi_get_media_file_path (grub_efi_device_path_t *dp) } static grub_efi_boolean_t -handle_image (void *data, grub_efi_uint32_t datasize) +handle_image (struct grub_secureboot_chainloader_context *load_context) { grub_efi_loaded_image_t *li, li_bak; grub_efi_status_t efi_status; + void *data = (void *)(unsigned long)load_context->address; + grub_efi_uint32_t datasize = load_context->fsize; void *buffer = NULL; char *buffer_aligned = NULL; grub_efi_uint32_t i; @@ -540,6 +551,7 @@ handle_image (void *data, grub_efi_uint32_t datasize) grub_uint32_t buffer_size; int found_entry_point = 0; int rc; + grub_efi_status_t (*entry_point) (grub_efi_handle_t image_handle, grub_efi_system_table_t *system_table); rc = read_header (data, datasize, &context); if (rc < 0) @@ -797,10 +809,10 @@ handle_image (void *data, grub_efi_uint32_t datasize) grub_memcpy (&li_bak, li, sizeof (grub_efi_loaded_image_t)); li->image_base = buffer_aligned; li->image_size = context.image_size; - li->load_options = cmdline; - li->load_options_size = cmdline_len; - li->file_path = grub_efi_get_media_file_path (file_path); - li->device_handle = dev_handle; + li->load_options = load_context->cmdline; + li->load_options_size = load_context->cmdline_len; + li->file_path = grub_efi_get_media_file_path (load_context->file_path); + li->device_handle = load_context->dev_handle; if (!li->file_path) { grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching file path found"); @@ -829,19 +841,22 @@ handle_image (void *data, grub_efi_uint32_t datasize) static grub_err_t grub_secureboot_chainloader_unload (void) { - grub_efi_free_pages (address, pages); - grub_free (file_path); - grub_free (cmdline); - cmdline = 0; - file_path = 0; - dev_handle = 0; + grub_efi_free_pages (sb_context->address, sb_context->pages); + grub_free (sb_context->file_path); + grub_free (sb_context->cmdline); + grub_free (sb_context); + + sb_context = 0; grub_dl_unref (my_mod); return GRUB_ERR_NONE; } static grub_err_t -grub_load_image(void *boot_image) +grub_load_image(grub_efi_device_path_t *file_path, void *boot_image, + grub_efi_uintn_t image_size, grub_efi_handle_t dev_handle, + grub_efi_char16_t *cmdline, grub_ssize_t cmdline_len, + grub_efi_handle_t *image_handle_out) { grub_efi_boot_services_t *b; grub_efi_status_t status; @@ -850,7 +865,7 @@ grub_load_image(void *boot_image) b = grub_efi_system_table->boot_services; status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path, - boot_image, fsize, &image_handle); + boot_image, image_size, image_handle_out); if (status != GRUB_EFI_SUCCESS) { if (status == GRUB_EFI_OUT_OF_RESOURCES) @@ -863,7 +878,7 @@ grub_load_image(void *boot_image) /* LoadImage does not set a device handler when the image is loaded from memory, so it is necessary to set it explicitly here. This is a mess. */ - loaded_image = grub_efi_get_loaded_image (image_handle); + loaded_image = grub_efi_get_loaded_image (*image_handle_out); if (! loaded_image) { grub_error (GRUB_ERR_BAD_OS, "no loaded image available"); @@ -885,20 +900,25 @@ grub_secureboot_chainloader_boot (void) { grub_efi_boot_services_t *b; int rc; + grub_efi_handle_t handle = 0; - rc = handle_image ((void *)(unsigned long)address, fsize); + rc = handle_image (sb_context); if (rc == 0) { /* We weren't able to attempt to execute the image, so fall back * to LoadImage / StartImage. */ - rc = grub_load_image((void *)(unsigned long)address); + rc = grub_load_image(sb_context->file_path, + (void *)(unsigned long)sb_context->address, + sb_context->fsize, sb_context->dev_handle, + sb_context->cmdline, sb_context->cmdline_len, + &handle); if (rc == 0) - grub_chainloader_boot (); + grub_start_image (handle); } b = grub_efi_system_table->boot_services; - efi_call_1 (b->unload_image, image_handle); + efi_call_1 (b->unload_image, handle); grub_loader_unset (); return grub_errno; @@ -913,9 +933,15 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_efi_boot_services_t *b; grub_device_t dev = 0; grub_device_t orig_dev = 0; - grub_efi_device_path_t *dp = 0; + grub_efi_device_path_t *dp = 0, *file_path = 0; char *filename; void *boot_image = 0; + grub_efi_physical_address_t address = 0; + grub_ssize_t fsize; + grub_efi_uintn_t pages = 0; + grub_efi_char16_t *cmdline = 0; + grub_ssize_t cmdline_len = 0; + grub_efi_handle_t dev_handle = 0; if (argc == 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); @@ -923,12 +949,6 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_dl_ref (my_mod); - /* Initialize some global variables. */ - address = 0; - image_handle = 0; - file_path = 0; - dev_handle = 0; - b = grub_efi_system_table->boot_services; if (argc > 1) @@ -1093,17 +1113,35 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) { + sb_context = grub_malloc (sizeof (*sb_context)); + if (sb_context == NULL) + goto fail; + sb_context->address = address; + sb_context->fsize = fsize; + sb_context->pages = pages; + sb_context->file_path = file_path; + sb_context->cmdline = cmdline; + sb_context->cmdline_len = cmdline_len; + sb_context->dev_handle = dev_handle; + grub_file_close (file); grub_device_close (dev); + grub_loader_set (grub_secureboot_chainloader_boot, grub_secureboot_chainloader_unload, 0); return 0; } else { - grub_load_image(boot_image); + grub_load_image(file_path, boot_image, fsize, dev_handle, cmdline, + cmdline_len, &image_handle); grub_file_close (file); grub_device_close (dev); + + /* We're finished with the source image buffer and file path now */ + efi_call_2 (b->free_pages, address, pages); + grub_free (file_path); + grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0); return 0; @@ -1130,6 +1168,12 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), if (cmdline) grub_free (cmdline); + if (image_handle != 0) + { + efi_call_1 (b->unload_image, image_handle); + image_handle = 0; + } + grub_dl_unref (my_mod); return grub_errno; From 9d43f7e3b81fee5bc036238b0cbcef2a77038591 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Fri, 29 Apr 2022 21:16:02 +0100 Subject: [PATCH 214/367] commands/boot: Add API to pass context to loader Loaders rely on global variables for saving context which is consumed in the boot hook and freed in the unload hook. In the case where a loader command is executed twice, calling grub_loader_set a second time executes the unload hook, but in some cases this runs when the loader's global context has already been updated, resulting in the updated context being freed and potential use-after-free bugs when the boot hook is subsequently called. This adds a new API (grub_loader_set_ex) which allows a loader to specify context that is passed to its boot and unload hooks. This is an alternative to requiring that loaders call grub_loader_unset before mutating their global context. Signed-off-by: Chris Coulson (cherry picked from commit 4322a64dde7e8fedb58e50b79408667129d45dd3) --- grub-core/commands/boot.c | 66 ++++++++++++++++++++++++++++++++++----- include/grub/loader.h | 5 +++ 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/grub-core/commands/boot.c b/grub-core/commands/boot.c index bbca81e947..53691a62d9 100644 --- a/grub-core/commands/boot.c +++ b/grub-core/commands/boot.c @@ -27,10 +27,20 @@ GRUB_MOD_LICENSE ("GPLv3+"); -static grub_err_t (*grub_loader_boot_func) (void); -static grub_err_t (*grub_loader_unload_func) (void); +static grub_err_t (*grub_loader_boot_func) (void *); +static grub_err_t (*grub_loader_unload_func) (void *); +static void *grub_loader_context; static int grub_loader_flags; +struct grub_simple_loader_hooks +{ + grub_err_t (*boot) (void); + grub_err_t (*unload) (void); +}; + +/* Don't heap allocate this to avoid making grub_loader_set fallible. */ +static struct grub_simple_loader_hooks simple_loader_hooks; + struct grub_preboot { grub_err_t (*preboot_func) (int); @@ -44,6 +54,29 @@ static int grub_loader_loaded; static struct grub_preboot *preboots_head = 0, *preboots_tail = 0; +static grub_err_t +grub_simple_boot_hook (void *context) +{ + struct grub_simple_loader_hooks *hooks; + + hooks = (struct grub_simple_loader_hooks *) context; + return hooks->boot (); +} + +static grub_err_t +grub_simple_unload_hook (void *context) +{ + struct grub_simple_loader_hooks *hooks; + grub_err_t ret; + + hooks = (struct grub_simple_loader_hooks *) context; + + ret = hooks->unload (); + grub_memset (hooks, 0, sizeof (*hooks)); + + return ret; +} + int grub_loader_is_loaded (void) { @@ -110,28 +143,45 @@ grub_loader_unregister_preboot_hook (struct grub_preboot *hnd) } void -grub_loader_set (grub_err_t (*boot) (void), - grub_err_t (*unload) (void), - int flags) +grub_loader_set_ex (grub_err_t (*boot) (void *), + grub_err_t (*unload) (void *), + void *context, + int flags) { if (grub_loader_loaded && grub_loader_unload_func) - grub_loader_unload_func (); + grub_loader_unload_func (grub_loader_context); grub_loader_boot_func = boot; grub_loader_unload_func = unload; + grub_loader_context = context; grub_loader_flags = flags; grub_loader_loaded = 1; } +void +grub_loader_set (grub_err_t (*boot) (void), + grub_err_t (*unload) (void), + int flags) +{ + grub_loader_set_ex (grub_simple_boot_hook, + grub_simple_unload_hook, + &simple_loader_hooks, + flags); + + simple_loader_hooks.boot = boot; + simple_loader_hooks.unload = unload; +} + void grub_loader_unset(void) { if (grub_loader_loaded && grub_loader_unload_func) - grub_loader_unload_func (); + grub_loader_unload_func (grub_loader_context); grub_loader_boot_func = 0; grub_loader_unload_func = 0; + grub_loader_context = 0; grub_loader_loaded = 0; } @@ -158,7 +208,7 @@ grub_loader_boot (void) return err; } } - err = (grub_loader_boot_func) (); + err = (grub_loader_boot_func) (grub_loader_context); for (cur = preboots_tail; cur; cur = cur->prev) if (! err) diff --git a/include/grub/loader.h b/include/grub/loader.h index b208642821..1846fa6c5f 100644 --- a/include/grub/loader.h +++ b/include/grub/loader.h @@ -40,6 +40,11 @@ void EXPORT_FUNC (grub_loader_set) (grub_err_t (*boot) (void), grub_err_t (*unload) (void), int flags); +void EXPORT_FUNC (grub_loader_set_ex) (grub_err_t (*boot) (void *), + grub_err_t (*unload) (void *), + void *context, + int flags); + /* Unset current loader, if any. */ void EXPORT_FUNC (grub_loader_unset) (void); From 80687f2ab9a4c4cfe39b89f13d37f87dc197ee78 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Fri, 29 Apr 2022 21:30:56 +0100 Subject: [PATCH 215/367] loader/efi/chainloader: Use grub_loader_set_ex This ports the EFI chainloader to use grub_loader_set_ex in order to fix a use-after-free bug that occurs when grub_cmd_chainloader is executed more than once before a boot attempt is performed. Signed-off-by: Chris Coulson (cherry picked from commit 4b7f0402b7cb0f67a93be736f2b75b818d7f44c9) [rharwood: context sludge from other change] Signed-off-by: Robbie Harwood --- grub-core/loader/efi/chainloader.c | 38 +++++++++++++++++------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index 3342492ff1..fb874f1855 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -48,8 +48,6 @@ GRUB_MOD_LICENSE ("GPLv3+"); static grub_dl_t my_mod; -static grub_efi_handle_t image_handle; - struct grub_secureboot_chainloader_context { grub_efi_physical_address_t address; grub_efi_uintn_t pages; @@ -59,7 +57,6 @@ struct grub_secureboot_chainloader_context { grub_ssize_t cmdline_len; grub_efi_handle_t dev_handle; }; -static struct grub_secureboot_chainloader_context *sb_context; static grub_err_t grub_start_image (grub_efi_handle_t handle) @@ -98,11 +95,14 @@ grub_start_image (grub_efi_handle_t handle) } static grub_err_t -grub_chainloader_unload (void) +grub_chainloader_unload (void *context) { + grub_efi_handle_t image_handle; grub_efi_loaded_image_t *loaded_image; grub_efi_boot_services_t *b; + image_handle = (grub_efi_handle_t) context; + loaded_image = grub_efi_get_loaded_image (image_handle); if (loaded_image != NULL) grub_free (loaded_image->load_options); @@ -115,10 +115,12 @@ grub_chainloader_unload (void) } static grub_err_t -grub_chainloader_boot (void) +grub_chainloader_boot (void *context) { + grub_efi_handle_t image_handle; grub_err_t err; + image_handle = (grub_efi_handle_t) context; err = grub_start_image (image_handle); grub_loader_unset (); @@ -839,15 +841,17 @@ handle_image (struct grub_secureboot_chainloader_context *load_context) } static grub_err_t -grub_secureboot_chainloader_unload (void) +grub_secureboot_chainloader_unload (void *context) { + struct grub_secureboot_chainloader_context *sb_context; + + sb_context = (struct grub_secureboot_chainloader_context *) context; + grub_efi_free_pages (sb_context->address, sb_context->pages); grub_free (sb_context->file_path); grub_free (sb_context->cmdline); grub_free (sb_context); - sb_context = 0; - grub_dl_unref (my_mod); return GRUB_ERR_NONE; } @@ -896,12 +900,15 @@ grub_load_image(grub_efi_device_path_t *file_path, void *boot_image, } static grub_err_t -grub_secureboot_chainloader_boot (void) +grub_secureboot_chainloader_boot (void *context) { + struct grub_secureboot_chainloader_context *sb_context; grub_efi_boot_services_t *b; int rc; grub_efi_handle_t handle = 0; + sb_context = (struct grub_secureboot_chainloader_context *) context; + rc = handle_image (sb_context); if (rc == 0) { @@ -942,6 +949,8 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_efi_char16_t *cmdline = 0; grub_ssize_t cmdline_len = 0; grub_efi_handle_t dev_handle = 0; + grub_efi_handle_t image_handle = 0; + struct grub_secureboot_chainloader_context *sb_context = 0; if (argc == 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); @@ -1127,8 +1136,8 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_file_close (file); grub_device_close (dev); - grub_loader_set (grub_secureboot_chainloader_boot, - grub_secureboot_chainloader_unload, 0); + grub_loader_set_ex (grub_secureboot_chainloader_boot, + grub_secureboot_chainloader_unload, sb_context, 0); return 0; } else @@ -1142,7 +1151,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), efi_call_2 (b->free_pages, address, pages); grub_free (file_path); - grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0); + grub_loader_set_ex (grub_chainloader_boot, grub_chainloader_unload, image_handle, 0); return 0; } @@ -1169,10 +1178,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_free (cmdline); if (image_handle != 0) - { - efi_call_1 (b->unload_image, image_handle); - image_handle = 0; - } + efi_call_1 (b->unload_image, image_handle); grub_dl_unref (my_mod); From 82fe61aa91294e602526aa3fe8115e2be402e207 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Mon, 2 May 2022 14:39:31 +0200 Subject: [PATCH 216/367] loader/i386/efi/linux: Avoid a use-after-free in the linuxefi loader In some error paths in grub_cmd_linux, the pointer to lh may be dereferenced after the buffer it points to has been freed. There aren't any security implications from this because nothing else uses the allocator after the buffer is freed and before the pointer is dereferenced, but fix it anyway. Signed-off-by: Chris Coulson (cherry picked from commit 8224f5a71af94bec8697de17e7e579792db9f9e2) --- grub-core/loader/i386/efi/linux.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 941df6400b..27bc2aa161 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -465,9 +465,6 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), if (file) grub_file_close (file); - if (kernel) - grub_free (kernel); - if (grub_errno != GRUB_ERR_NONE) { grub_dl_unref (my_mod); @@ -483,6 +480,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), kernel_free (params, sizeof(*params)); } + grub_free (kernel); + return grub_errno; } From 9b12533217d9795769ac2cb7e59af62f8f8f1ca4 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Mon, 2 May 2022 17:04:23 +0200 Subject: [PATCH 217/367] loader/i386/efi/linux: Use grub_loader_set_ex This ports the linuxefi loader to use grub_loader_set_ex in order to fix a use-after-fre bug that occurs when grub_cmd_linux is executed more than once before a boot attempt is performed. This is more complicated than for the chainloader command, as the initrd command needs access to the loader state. To solve this, the linuxefi module registers a dummy initrd command at startup that returns an error. The linuxefi command then registers a proper initrd command with a higher priority that is passed the loader state. Signed-off-by: Chris Coulson (cherry picked from commit 7cf736436b4c934df5ddfa6f44b46a7e07d99fdc) [rharwood/pjones: set kernel_size in context] Signed-off-by: Robbie Harwood --- grub-core/loader/i386/efi/linux.c | 146 ++++++++++++++++++------------ 1 file changed, 87 insertions(+), 59 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 27bc2aa161..e3c2d6fe0b 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -34,13 +34,19 @@ GRUB_MOD_LICENSE ("GPLv3+"); static grub_dl_t my_mod; -static int loaded; -static void *kernel_mem; -static grub_uint64_t kernel_size; -static void *initrd_mem; -static grub_uint32_t handover_offset; -struct linux_kernel_params *params; -static char *linux_cmdline; + +static grub_command_t cmd_linux, cmd_initrd; +static grub_command_t cmd_linuxefi, cmd_initrdefi; + +struct grub_linuxefi_context { + void *kernel_mem; + grub_uint64_t kernel_size; + grub_uint32_t handover_offset; + struct linux_kernel_params *params; + char *cmdline; + + void *initrd_mem; +}; #define MIN(a, b) \ ({ typeof (a) _a = (a); \ @@ -123,25 +129,32 @@ kernel_alloc(grub_efi_uintn_t size, const char * const errmsg) } static grub_err_t -grub_linuxefi_boot (void) +grub_linuxefi_boot (void *data) { + struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) data; + asm volatile ("cli"); - return grub_efi_linux_boot ((char *)kernel_mem, - handover_offset, - params); + return grub_efi_linux_boot ((char *)context->kernel_mem, + context->handover_offset, + context->params); } static grub_err_t -grub_linuxefi_unload (void) +grub_linuxefi_unload (void *data) { + struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) data; + struct linux_kernel_params *params = context->params; + grub_dl_unref (my_mod); - loaded = 0; - kernel_free(initrd_mem, params->ramdisk_size); - kernel_free(linux_cmdline, params->cmdline_size + 1); - kernel_free(kernel_mem, kernel_size); - kernel_free(params, sizeof(*params)); + kernel_free (context->initrd_mem, params->ramdisk_size); + kernel_free (context->cmdline, params->cmdline_size + 1); + kernel_free (context->kernel_mem, context->kernel_size); + kernel_free (params, sizeof(*params)); + cmd_initrd->data = 0; + cmd_initrdefi->data = 0; + grub_free (context); return GRUB_ERR_NONE; } @@ -188,13 +201,14 @@ read(grub_file_t file, grub_uint8_t *bufp, grub_size_t len) #define HIGH_U32(val) ((grub_uint32_t)(((grub_addr_t)(val) >> 32) & 0xffffffffull)) static grub_err_t -grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), - int argc, char *argv[]) +grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[]) { grub_file_t *files = 0; int i, nfiles = 0; grub_size_t size = 0; grub_uint8_t *ptr; + struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) cmd->data; + struct linux_kernel_params *params; if (argc == 0) { @@ -202,12 +216,14 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), goto fail; } - if (!loaded) + if (!context) { grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first")); goto fail; } + params = context->params; + files = grub_calloc (argc, sizeof (files[0])); if (!files) goto fail; @@ -225,19 +241,19 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), } } - initrd_mem = kernel_alloc(size, N_("can't allocate initrd")); - if (initrd_mem == NULL) + context->initrd_mem = kernel_alloc(size, N_("can't allocate initrd")); + if (context->initrd_mem == NULL) goto fail; - grub_dprintf ("linux", "initrd_mem = %p\n", initrd_mem); + grub_dprintf ("linux", "initrd_mem = %p\n", context->initrd_mem); params->ramdisk_size = LOW_U32(size); - params->ramdisk_image = LOW_U32(initrd_mem); + params->ramdisk_image = LOW_U32(context->initrd_mem); #if defined(__x86_64__) params->ext_ramdisk_size = HIGH_U32(size); - params->ext_ramdisk_image = HIGH_U32(initrd_mem); + params->ext_ramdisk_image = HIGH_U32(context->initrd_mem); #endif - ptr = initrd_mem; + ptr = context->initrd_mem; for (i = 0; i < nfiles; i++) { @@ -261,8 +277,8 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), grub_file_close (files[i]); grub_free (files); - if (initrd_mem && grub_errno) - grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)initrd_mem, + if (context->initrd_mem && grub_errno) + grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)context->initrd_mem, BYTES_TO_PAGES(size)); return grub_errno; @@ -277,6 +293,12 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_ssize_t start, filelen; void *kernel = NULL; int setup_header_end_offset; + void *kernel_mem = 0; + grub_uint64_t kernel_size = 0; + grub_uint32_t handover_offset; + struct linux_kernel_params *params = 0; + char *cmdline = 0; + struct grub_linuxefi_context *context = 0; grub_dl_ref (my_mod); @@ -390,27 +412,27 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_dprintf ("linux", "new lh is at %p\n", lh); grub_dprintf ("linux", "setting up cmdline\n"); - linux_cmdline = kernel_alloc (lh->cmdline_size + 1, N_("can't allocate cmdline")); - if (!linux_cmdline) + cmdline = kernel_alloc (lh->cmdline_size + 1, N_("can't allocate cmdline")); + if (!cmdline) goto fail; - grub_dprintf ("linux", "linux_cmdline = %p\n", linux_cmdline); + grub_dprintf ("linux", "cmdline = %p\n", cmdline); - grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE)); + grub_memcpy (cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE)); grub_create_loader_cmdline (argc, argv, - linux_cmdline + sizeof (LINUX_IMAGE) - 1, + cmdline + sizeof (LINUX_IMAGE) - 1, lh->cmdline_size - (sizeof (LINUX_IMAGE) - 1), GRUB_VERIFY_KERNEL_CMDLINE); - grub_dprintf ("linux", "cmdline:%s\n", linux_cmdline); + grub_dprintf ("linux", "cmdline:%s\n", cmdline); grub_dprintf ("linux", "setting lh->cmd_line_ptr to 0x%08x\n", - LOW_U32(linux_cmdline)); - lh->cmd_line_ptr = LOW_U32(linux_cmdline); + LOW_U32(cmdline)); + lh->cmd_line_ptr = LOW_U32(cmdline); #if defined(__x86_64__) - if ((grub_efi_uintn_t)linux_cmdline > 0xffffffffull) + if ((grub_efi_uintn_t)cmdline > 0xffffffffull) { grub_dprintf ("linux", "setting params->ext_cmd_line_ptr to 0x%08x\n", - HIGH_U32(linux_cmdline)); - params->ext_cmd_line_ptr = HIGH_U32(linux_cmdline); + HIGH_U32(cmdline)); + params->ext_cmd_line_ptr = HIGH_U32(cmdline); } #endif @@ -435,16 +457,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } max_addresses[1].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; max_addresses[2].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; - kernel_mem = kernel_alloc (lh->init_size, N_("can't allocate kernel")); + kernel_size = lh->init_size; + kernel_mem = kernel_alloc (kernel_size, N_("can't allocate kernel")); restore_addresses(); if (!kernel_mem) goto fail; grub_dprintf("linux", "kernel_mem = %p\n", kernel_mem); - grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0); - - loaded = 1; - grub_dprintf ("linux", "setting lh->code32_start to 0x%08x\n", LOW_U32(kernel_mem)); lh->code32_start = LOW_U32(kernel_mem); @@ -461,33 +480,42 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), "setting lh->ext_loader_{type,ver} = {0x%02x,0x%02x}\n", params->ext_loader_type, params->ext_loader_ver); + context = grub_zalloc (sizeof (*context)); + if (!context) + goto fail; + context->kernel_mem = kernel_mem; + context->kernel_size = kernel_size; + context->handover_offset = handover_offset; + context->params = params; + context->cmdline = cmdline; + + grub_loader_set_ex (grub_linuxefi_boot, grub_linuxefi_unload, context, 0); + + cmd_initrd->data = context; + cmd_initrdefi->data = context; + + grub_file_close (file); + grub_free (kernel); + return 0; + fail: if (file) grub_file_close (file); - if (grub_errno != GRUB_ERR_NONE) - { - grub_dl_unref (my_mod); - loaded = 0; - } + grub_dl_unref (my_mod); - if (!loaded) - { - if (lh) - kernel_free (linux_cmdline, lh->cmdline_size + 1); + if (lh) + kernel_free (cmdline, lh->cmdline_size + 1); - kernel_free (kernel_mem, kernel_size); - kernel_free (params, sizeof(*params)); - } + kernel_free (kernel_mem, kernel_size); + kernel_free (params, sizeof(*params)); + grub_free (context); grub_free (kernel); return grub_errno; } -static grub_command_t cmd_linux, cmd_initrd; -static grub_command_t cmd_linuxefi, cmd_initrdefi; - GRUB_MOD_INIT(linux) { cmd_linux = From 3143df6934ed2fb4188d264d9c6f51e14b393181 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Tue, 3 May 2022 09:47:35 +0200 Subject: [PATCH 218/367] loader/i386/efi/linux: Fix a memory leak in the initrd command Subsequent invocations of the initrd command result in the previous initrd being leaked, so fix that. Signed-off-by: Chris Coulson (cherry picked from commit d98af31ce1e31bb22163960d53f5eb28c66582a0) --- grub-core/loader/i386/efi/linux.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index e3c2d6fe0b..9e5c11ac69 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -209,6 +209,7 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[]) grub_uint8_t *ptr; struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) cmd->data; struct linux_kernel_params *params; + void *initrd_mem = 0; if (argc == 0) { @@ -241,19 +242,19 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[]) } } - context->initrd_mem = kernel_alloc(size, N_("can't allocate initrd")); - if (context->initrd_mem == NULL) + initrd_mem = kernel_alloc(size, N_("can't allocate initrd")); + if (initrd_mem == NULL) goto fail; - grub_dprintf ("linux", "initrd_mem = %p\n", context->initrd_mem); + grub_dprintf ("linux", "initrd_mem = %p\n", initrd_mem); params->ramdisk_size = LOW_U32(size); - params->ramdisk_image = LOW_U32(context->initrd_mem); + params->ramdisk_image = LOW_U32(initrd_mem); #if defined(__x86_64__) params->ext_ramdisk_size = HIGH_U32(size); - params->ext_ramdisk_image = HIGH_U32(context->initrd_mem); + params->ext_ramdisk_image = HIGH_U32(initrd_mem); #endif - ptr = context->initrd_mem; + ptr = initrd_mem; for (i = 0; i < nfiles; i++) { @@ -270,6 +271,9 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[]) ptr += ALIGN_UP_OVERHEAD (cursize, 4); } + kernel_free(context->initrd_mem, params->ramdisk_size); + + context->initrd_mem = initrd_mem; params->ramdisk_size = size; fail: @@ -277,9 +281,8 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[]) grub_file_close (files[i]); grub_free (files); - if (context->initrd_mem && grub_errno) - grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)context->initrd_mem, - BYTES_TO_PAGES(size)); + if (initrd_mem && grub_errno) + kernel_free (initrd_mem, size); return grub_errno; } From 9ae95e3c6785ce9848496df1180ddf8a061c868e Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Thu, 2 Dec 2021 15:03:53 +0100 Subject: [PATCH 219/367] kern/efi/sb: Reject non-kernel files in the shim_lock verifier We must not allow other verifiers to pass things like the GRUB modules. Instead of maintaining a blocklist, maintain an allowlist of things that we do not care about. This allowlist really should be made reusable, and shared by the lockdown verifier, but this is the minimal patch addressing security concerns where the TPM verifier was able to mark modules as verified (or the OpenPGP verifier for that matter), when it should not do so on shim-powered secure boot systems. Fixes: CVE-2022-28735 Signed-off-by: Julian Andres Klode Reviewed-by: Daniel Kiper (cherry picked from commit fa61ad69861c1cb3f68bf853d78fae7fd93986a0) --- grub-core/kern/efi/sb.c | 39 ++++++++++++++++++++++++++++++++++++--- include/grub/verify.h | 1 + 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/grub-core/kern/efi/sb.c b/grub-core/kern/efi/sb.c index c52ec6226a..89c4bb3fd1 100644 --- a/grub-core/kern/efi/sb.c +++ b/grub-core/kern/efi/sb.c @@ -119,10 +119,11 @@ shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)), void **context __attribute__ ((unused)), enum grub_verify_flags *flags) { - *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + *flags = GRUB_VERIFY_FLAGS_NONE; switch (type & GRUB_FILE_TYPE_MASK) { + /* Files we check. */ case GRUB_FILE_TYPE_LINUX_KERNEL: case GRUB_FILE_TYPE_MULTIBOOT_KERNEL: case GRUB_FILE_TYPE_BSD_KERNEL: @@ -130,11 +131,43 @@ shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)), case GRUB_FILE_TYPE_PLAN9_KERNEL: case GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE: *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK; + return GRUB_ERR_NONE; - /* Fall through. */ + /* Files that do not affect secureboot state. */ + case GRUB_FILE_TYPE_NONE: + case GRUB_FILE_TYPE_LOOPBACK: + case GRUB_FILE_TYPE_LINUX_INITRD: + case GRUB_FILE_TYPE_OPENBSD_RAMDISK: + case GRUB_FILE_TYPE_XNU_RAMDISK: + case GRUB_FILE_TYPE_SIGNATURE: + case GRUB_FILE_TYPE_PUBLIC_KEY: + case GRUB_FILE_TYPE_PUBLIC_KEY_TRUST: + case GRUB_FILE_TYPE_PRINT_BLOCKLIST: + case GRUB_FILE_TYPE_TESTLOAD: + case GRUB_FILE_TYPE_GET_SIZE: + case GRUB_FILE_TYPE_FONT: + case GRUB_FILE_TYPE_ZFS_ENCRYPTION_KEY: + case GRUB_FILE_TYPE_CAT: + case GRUB_FILE_TYPE_HEXCAT: + case GRUB_FILE_TYPE_CMP: + case GRUB_FILE_TYPE_HASHLIST: + case GRUB_FILE_TYPE_TO_HASH: + case GRUB_FILE_TYPE_KEYBOARD_LAYOUT: + case GRUB_FILE_TYPE_PIXMAP: + case GRUB_FILE_TYPE_GRUB_MODULE_LIST: + case GRUB_FILE_TYPE_CONFIG: + case GRUB_FILE_TYPE_THEME: + case GRUB_FILE_TYPE_GETTEXT_CATALOG: + case GRUB_FILE_TYPE_FS_SEARCH: + case GRUB_FILE_TYPE_LOADENV: + case GRUB_FILE_TYPE_SAVEENV: + case GRUB_FILE_TYPE_VERIFY_SIGNATURE: + *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + return GRUB_ERR_NONE; + /* Other files. */ default: - return GRUB_ERR_NONE; + return grub_error (GRUB_ERR_ACCESS_DENIED, N_("prohibited by secure boot policy")); } } diff --git a/include/grub/verify.h b/include/grub/verify.h index cd129c398f..672ae16924 100644 --- a/include/grub/verify.h +++ b/include/grub/verify.h @@ -24,6 +24,7 @@ enum grub_verify_flags { + GRUB_VERIFY_FLAGS_NONE = 0, GRUB_VERIFY_FLAGS_SKIP_VERIFICATION = 1, GRUB_VERIFY_FLAGS_SINGLE_CHUNK = 2, /* Defer verification to another authority. */ From aa540bc80d06f8757cf0d56787cb5fd626cecbd1 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 25 Jun 2021 02:19:05 +1000 Subject: [PATCH 220/367] kern/file: Do not leak device_name on error in grub_file_open() If we have an error in grub_file_open() before we free device_name, we will leak it. Free device_name in the error path and null out the pointer in the good path once we free it there. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit 1499a5068839fa37cb77ecef4b5bdacbd1ed12ea) --- grub-core/kern/file.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c index ec10e54fc0..db938e099d 100644 --- a/grub-core/kern/file.c +++ b/grub-core/kern/file.c @@ -84,6 +84,7 @@ grub_file_open (const char *name, enum grub_file_type type) device = grub_device_open (device_name); grub_free (device_name); + device_name = NULL; if (! device) goto fail; @@ -138,6 +139,7 @@ grub_file_open (const char *name, enum grub_file_type type) return file; fail: + grub_free (device_name); if (device) grub_device_close (device); From 726a97e62a25228e6f4ef29d3c5a6256399eeaa5 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 6 Jul 2021 14:02:55 +1000 Subject: [PATCH 221/367] video/readers/png: Abort sooner if a read operation fails Fuzzing revealed some inputs that were taking a long time, potentially forever, because they did not bail quickly upon encountering an I/O error. Try to catch I/O errors sooner and bail out. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit 882be97d1df6449b9fd4d593f0cb70005fde3494) --- grub-core/video/readers/png.c | 55 ++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c index 0157ff7420..e2a6b1cf3c 100644 --- a/grub-core/video/readers/png.c +++ b/grub-core/video/readers/png.c @@ -142,6 +142,7 @@ static grub_uint8_t grub_png_get_byte (struct grub_png_data *data) { grub_uint8_t r; + grub_ssize_t bytes_read = 0; if ((data->inside_idat) && (data->idat_remain == 0)) { @@ -175,7 +176,14 @@ grub_png_get_byte (struct grub_png_data *data) } r = 0; - grub_file_read (data->file, &r, 1); + bytes_read = grub_file_read (data->file, &r, 1); + + if (bytes_read != 1) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: unexpected end of data"); + return 0; + } if (data->inside_idat) data->idat_remain--; @@ -231,15 +239,16 @@ grub_png_decode_image_palette (struct grub_png_data *data, if (len == 0) return GRUB_ERR_NONE; - for (i = 0; 3 * i < len && i < 256; i++) + grub_errno = GRUB_ERR_NONE; + for (i = 0; 3 * i < len && i < 256 && grub_errno == GRUB_ERR_NONE; i++) for (j = 0; j < 3; j++) data->palette[i][j] = grub_png_get_byte (data); - for (i *= 3; i < len; i++) + for (i *= 3; i < len && grub_errno == GRUB_ERR_NONE; i++) grub_png_get_byte (data); grub_png_get_dword (data); - return GRUB_ERR_NONE; + return grub_errno; } static grub_err_t @@ -256,9 +265,13 @@ grub_png_decode_image_header (struct grub_png_data *data) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: invalid image size"); color_bits = grub_png_get_byte (data); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; data->is_16bit = (color_bits == 16); color_type = grub_png_get_byte (data); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; /* According to PNG spec, no other types are valid. */ if ((color_type & ~(PNG_COLOR_MASK_ALPHA | PNG_COLOR_MASK_COLOR)) @@ -340,14 +353,20 @@ grub_png_decode_image_header (struct grub_png_data *data) if (grub_png_get_byte (data) != PNG_COMPRESSION_BASE) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: compression method not supported"); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if (grub_png_get_byte (data) != PNG_FILTER_TYPE_BASE) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: filter method not supported"); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if (grub_png_get_byte (data) != PNG_INTERLACE_NONE) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: interlace method not supported"); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; /* Skip crc checksum. */ grub_png_get_dword (data); @@ -449,7 +468,7 @@ grub_png_get_huff_code (struct grub_png_data *data, struct huff_table *ht) int code, i; code = 0; - for (i = 0; i < ht->max_length; i++) + for (i = 0; i < ht->max_length && grub_errno == GRUB_ERR_NONE; i++) { code = (code << 1) + grub_png_get_bits (data, 1); if (code < ht->maxval[i]) @@ -504,8 +523,14 @@ grub_png_init_dynamic_block (struct grub_png_data *data) grub_uint8_t lens[DEFLATE_HCLEN_MAX]; nl = DEFLATE_HLIT_BASE + grub_png_get_bits (data, 5); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; nd = DEFLATE_HDIST_BASE + grub_png_get_bits (data, 5); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; nb = DEFLATE_HCLEN_BASE + grub_png_get_bits (data, 4); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if ((nl > DEFLATE_HLIT_MAX) || (nd > DEFLATE_HDIST_MAX) || (nb > DEFLATE_HCLEN_MAX)) @@ -533,7 +558,7 @@ grub_png_init_dynamic_block (struct grub_png_data *data) data->dist_offset); prev = 0; - for (i = 0; i < nl + nd; i++) + for (i = 0; i < nl + nd && grub_errno == GRUB_ERR_NONE; i++) { int n, code; struct huff_table *ht; @@ -721,17 +746,21 @@ grub_png_read_dynamic_block (struct grub_png_data *data) len = cplens[n]; if (cplext[n]) len += grub_png_get_bits (data, cplext[n]); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; n = grub_png_get_huff_code (data, &data->dist_table); dist = cpdist[n]; if (cpdext[n]) dist += grub_png_get_bits (data, cpdext[n]); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; pos = data->wp - dist; if (pos < 0) pos += WSIZE; - while (len > 0) + while (len > 0 && grub_errno == GRUB_ERR_NONE) { data->slide[data->wp] = data->slide[pos]; grub_png_output_byte (data, data->slide[data->wp]); @@ -759,7 +788,11 @@ grub_png_decode_image_data (struct grub_png_data *data) int final; cmf = grub_png_get_byte (data); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; flg = grub_png_get_byte (data); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if ((cmf & 0xF) != Z_DEFLATED) return grub_error (GRUB_ERR_BAD_FILE_TYPE, @@ -774,7 +807,11 @@ grub_png_decode_image_data (struct grub_png_data *data) int block_type; final = grub_png_get_bits (data, 1); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; block_type = grub_png_get_bits (data, 2); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; switch (block_type) { @@ -790,7 +827,7 @@ grub_png_decode_image_data (struct grub_png_data *data) grub_png_get_byte (data); grub_png_get_byte (data); - for (i = 0; i < len; i++) + for (i = 0; i < len && grub_errno == GRUB_ERR_NONE; i++) grub_png_output_byte (data, grub_png_get_byte (data)); break; @@ -1045,6 +1082,8 @@ grub_png_decode_png (struct grub_png_data *data) len = grub_png_get_dword (data); type = grub_png_get_dword (data); + if (grub_errno != GRUB_ERR_NONE) + break; data->next_offset = data->file->offset + len + 4; switch (type) From 0cdcc7bc9f2f3f997a2c142755e15d5bd9d0b612 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 6 Jul 2021 14:13:40 +1000 Subject: [PATCH 222/367] video/readers/png: Refuse to handle multiple image headers This causes the bitmap to be leaked. Do not permit multiple image headers. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit 8ce433557adeadbc46429aabb9f850b02ad2bdfb) --- grub-core/video/readers/png.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c index e2a6b1cf3c..8955b8ecfd 100644 --- a/grub-core/video/readers/png.c +++ b/grub-core/video/readers/png.c @@ -258,6 +258,9 @@ grub_png_decode_image_header (struct grub_png_data *data) int color_bits; enum grub_video_blit_format blt; + if (data->image_width || data->image_height) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: two image headers found"); + data->image_width = grub_png_get_dword (data); data->image_height = grub_png_get_dword (data); From ce5290a919e85fa5e4f9c67bb8ef0b4121c1d3e0 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 6 Jul 2021 18:51:35 +1000 Subject: [PATCH 223/367] video/readers/png: Drop greyscale support to fix heap out-of-bounds write A 16-bit greyscale PNG without alpha is processed in the following loop: for (i = 0; i < (data->image_width * data->image_height); i++, d1 += 4, d2 += 2) { d1[R3] = d2[1]; d1[G3] = d2[1]; d1[B3] = d2[1]; } The increment of d1 is wrong. d1 is incremented by 4 bytes per iteration, but there are only 3 bytes allocated for storage. This means that image data will overwrite somewhat-attacker-controlled parts of memory - 3 bytes out of every 4 following the end of the image. This has existed since greyscale support was added in 2013 in commit 3ccf16dff98f (grub-core/video/readers/png.c: Support grayscale). Saving starfield.png as a 16-bit greyscale image without alpha in the gimp and attempting to load it causes grub-emu to crash - I don't think this code has ever worked. Delete all PNG greyscale support. Fixes: CVE-2021-3695 Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit 0e1d163382669bd734439d8864ee969616d971d9) [rharwood: context conflict] Signed-off-by: Robbie Harwood --- grub-core/video/readers/png.c | 87 +++-------------------------------- 1 file changed, 7 insertions(+), 80 deletions(-) diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c index 8955b8ecfd..a3161e25b6 100644 --- a/grub-core/video/readers/png.c +++ b/grub-core/video/readers/png.c @@ -100,7 +100,7 @@ struct grub_png_data unsigned image_width, image_height; int bpp, is_16bit; - int raw_bytes, is_gray, is_alpha, is_palette; + int raw_bytes, is_alpha, is_palette; int row_bytes, color_bits; grub_uint8_t *image_data; @@ -296,13 +296,13 @@ grub_png_decode_image_header (struct grub_png_data *data) data->bpp = 3; else { - data->is_gray = 1; - data->bpp = 1; + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: color type not supported"); } if ((color_bits != 8) && (color_bits != 16) && (color_bits != 4 - || !(data->is_gray || data->is_palette))) + || !data->is_palette)) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: bit depth must be 8 or 16"); @@ -331,7 +331,7 @@ grub_png_decode_image_header (struct grub_png_data *data) } #ifndef GRUB_CPU_WORDS_BIGENDIAN - if (data->is_16bit || data->is_gray || data->is_palette) + if (data->is_16bit || data->is_palette) #endif { data->image_data = grub_calloc (data->image_height, data->row_bytes); @@ -899,27 +899,8 @@ grub_png_convert_image (struct grub_png_data *data) int shift; int mask = (1 << data->color_bits) - 1; unsigned j; - if (data->is_gray) - { - /* Generic formula is - (0xff * i) / ((1U << data->color_bits) - 1) - but for allowed bit depth of 1, 2 and for it's - equivalent to - (0xff / ((1U << data->color_bits) - 1)) * i - Precompute the multipliers to avoid division. - */ - - const grub_uint8_t multipliers[5] = { 0xff, 0xff, 0x55, 0x24, 0x11 }; - for (i = 0; i < (1U << data->color_bits); i++) - { - grub_uint8_t col = multipliers[data->color_bits] * i; - palette[i][0] = col; - palette[i][1] = col; - palette[i][2] = col; - } - } - else - grub_memcpy (palette, data->palette, 3 << data->color_bits); + + grub_memcpy (palette, data->palette, 3 << data->color_bits); d1c = d1; d2c = d2; for (j = 0; j < data->image_height; j++, d1c += data->image_width * 3, @@ -956,60 +937,6 @@ grub_png_convert_image (struct grub_png_data *data) } return; } - - if (data->is_gray) - { - switch (data->bpp) - { - case 4: - /* 16-bit gray with alpha. */ - for (i = 0; i < (data->image_width * data->image_height); - i++, d1 += 4, d2 += 4) - { - d1[R4] = d2[3]; - d1[G4] = d2[3]; - d1[B4] = d2[3]; - d1[A4] = d2[1]; - } - break; - case 2: - if (data->is_16bit) - /* 16-bit gray without alpha. */ - { - for (i = 0; i < (data->image_width * data->image_height); - i++, d1 += 4, d2 += 2) - { - d1[R3] = d2[1]; - d1[G3] = d2[1]; - d1[B3] = d2[1]; - } - } - else - /* 8-bit gray with alpha. */ - { - for (i = 0; i < (data->image_width * data->image_height); - i++, d1 += 4, d2 += 2) - { - d1[R4] = d2[1]; - d1[G4] = d2[1]; - d1[B4] = d2[1]; - d1[A4] = d2[0]; - } - } - break; - /* 8-bit gray without alpha. */ - case 1: - for (i = 0; i < (data->image_width * data->image_height); - i++, d1 += 3, d2++) - { - d1[R3] = d2[0]; - d1[G3] = d2[0]; - d1[B3] = d2[0]; - } - break; - } - return; - } { /* Only copy the upper 8 bit. */ From 0f76596be3bec8147e6d5c117dce44845e3153c9 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 6 Jul 2021 23:25:07 +1000 Subject: [PATCH 224/367] video/readers/png: Avoid heap OOB R/W inserting huff table items In fuzzing we observed crashes where a code would attempt to be inserted into a huffman table before the start, leading to a set of heap OOB reads and writes as table entries with negative indices were shifted around and the new code written in. Catch the case where we would underflow the array and bail. Fixes: CVE-2021-3696 Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit 1ae9a91d42cb40da8a6f11fac65541858e340afa) --- grub-core/video/readers/png.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c index a3161e25b6..d7ed5aa6cf 100644 --- a/grub-core/video/readers/png.c +++ b/grub-core/video/readers/png.c @@ -438,6 +438,13 @@ grub_png_insert_huff_item (struct huff_table *ht, int code, int len) for (i = len; i < ht->max_length; i++) n += ht->maxval[i]; + if (n > ht->num_values) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: out of range inserting huffman table item"); + return; + } + for (i = 0; i < n; i++) ht->values[ht->num_values - i] = ht->values[ht->num_values - i - 1]; From bee218bbca4d479f95d9bb4b19d75a96c09c4fab Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 6 Jul 2021 19:19:11 +1000 Subject: [PATCH 225/367] video/readers/png: Sanity check some huffman codes ASAN picked up two OOB global reads: we weren't checking if some code values fit within the cplens or cpdext arrays. Check and throw an error if not. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit c3a8ab0cbd24153ec7b1f84a96ddfdd72ef8d117) --- grub-core/video/readers/png.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c index d7ed5aa6cf..7f2ba7849b 100644 --- a/grub-core/video/readers/png.c +++ b/grub-core/video/readers/png.c @@ -753,6 +753,9 @@ grub_png_read_dynamic_block (struct grub_png_data *data) int len, dist, pos; n -= 257; + if (((unsigned int) n) >= ARRAY_SIZE (cplens)) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: invalid huff code"); len = cplens[n]; if (cplext[n]) len += grub_png_get_bits (data, cplext[n]); @@ -760,6 +763,9 @@ grub_png_read_dynamic_block (struct grub_png_data *data) return grub_errno; n = grub_png_get_huff_code (data, &data->dist_table); + if (((unsigned int) n) >= ARRAY_SIZE (cpdist)) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: invalid huff code"); dist = cpdist[n]; if (cpdext[n]) dist += grub_png_get_bits (data, cpdext[n]); From db1fcb16c8243e31580a9fdcf2196c64f1bbc37f Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Mon, 28 Jun 2021 14:16:14 +1000 Subject: [PATCH 226/367] video/readers/jpeg: Abort sooner if a read operation fails Fuzzing revealed some inputs that were taking a long time, potentially forever, because they did not bail quickly upon encountering an I/O error. Try to catch I/O errors sooner and bail out. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit ab2e5d2e4bff488bbb557ed435a61ae102ef9f0c) --- grub-core/video/readers/jpeg.c | 86 +++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c index e31602f766..10225abd53 100644 --- a/grub-core/video/readers/jpeg.c +++ b/grub-core/video/readers/jpeg.c @@ -109,9 +109,17 @@ static grub_uint8_t grub_jpeg_get_byte (struct grub_jpeg_data *data) { grub_uint8_t r; + grub_ssize_t bytes_read; r = 0; - grub_file_read (data->file, &r, 1); + bytes_read = grub_file_read (data->file, &r, 1); + + if (bytes_read != 1) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: unexpected end of data"); + return 0; + } return r; } @@ -120,9 +128,17 @@ static grub_uint16_t grub_jpeg_get_word (struct grub_jpeg_data *data) { grub_uint16_t r; + grub_ssize_t bytes_read; r = 0; - grub_file_read (data->file, &r, sizeof (grub_uint16_t)); + bytes_read = grub_file_read (data->file, &r, sizeof (grub_uint16_t)); + + if (bytes_read != sizeof (grub_uint16_t)) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: unexpected end of data"); + return 0; + } return grub_be_to_cpu16 (r); } @@ -135,6 +151,11 @@ grub_jpeg_get_bit (struct grub_jpeg_data *data) if (data->bit_mask == 0) { data->bit_save = grub_jpeg_get_byte (data); + if (grub_errno != GRUB_ERR_NONE) { + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: file read error"); + return 0; + } if (data->bit_save == JPEG_ESC_CHAR) { if (grub_jpeg_get_byte (data) != 0) @@ -143,6 +164,11 @@ grub_jpeg_get_bit (struct grub_jpeg_data *data) "jpeg: invalid 0xFF in data stream"); return 0; } + if (grub_errno != GRUB_ERR_NONE) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: file read error"); + return 0; + } } data->bit_mask = 0x80; } @@ -161,7 +187,7 @@ grub_jpeg_get_number (struct grub_jpeg_data *data, int num) return 0; msb = value = grub_jpeg_get_bit (data); - for (i = 1; i < num; i++) + for (i = 1; i < num && grub_errno == GRUB_ERR_NONE; i++) value = (value << 1) + (grub_jpeg_get_bit (data) != 0); if (!msb) value += 1 - (1 << num); @@ -202,6 +228,8 @@ grub_jpeg_decode_huff_table (struct grub_jpeg_data *data) while (data->file->offset + sizeof (count) + 1 <= next_marker) { id = grub_jpeg_get_byte (data); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; ac = (id >> 4) & 1; id &= 0xF; if (id > 1) @@ -252,6 +280,8 @@ grub_jpeg_decode_quan_table (struct grub_jpeg_data *data) next_marker = data->file->offset; next_marker += grub_jpeg_get_word (data); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if (next_marker > data->file->size) { @@ -263,6 +293,8 @@ grub_jpeg_decode_quan_table (struct grub_jpeg_data *data) <= next_marker) { id = grub_jpeg_get_byte (data); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if (id >= 0x10) /* Upper 4-bit is precision. */ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: only 8-bit precision is supported"); @@ -294,6 +326,9 @@ grub_jpeg_decode_sof (struct grub_jpeg_data *data) next_marker = data->file->offset; next_marker += grub_jpeg_get_word (data); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + if (grub_jpeg_get_byte (data) != 8) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: only 8-bit precision is supported"); @@ -319,6 +354,8 @@ grub_jpeg_decode_sof (struct grub_jpeg_data *data) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid index"); ss = grub_jpeg_get_byte (data); /* Sampling factor. */ + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if (!id) { grub_uint8_t vs, hs; @@ -498,7 +535,7 @@ grub_jpeg_idct_transform (jpeg_data_unit_t du) } } -static void +static grub_err_t grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du) { int h1, h2, qt; @@ -513,6 +550,9 @@ grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du) data->dc_value[id] += grub_jpeg_get_number (data, grub_jpeg_get_huff_code (data, h1)); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + du[0] = data->dc_value[id] * (int) data->quan_table[qt][0]; pos = 1; while (pos < ARRAY_SIZE (data->quan_table[qt])) @@ -527,11 +567,13 @@ grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du) num >>= 4; pos += num; + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + if (pos >= ARRAY_SIZE (jpeg_zigzag_order)) { - grub_error (GRUB_ERR_BAD_FILE_TYPE, - "jpeg: invalid position in zigzag order!?"); - return; + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: invalid position in zigzag order!?"); } du[jpeg_zigzag_order[pos]] = val * (int) data->quan_table[qt][pos]; @@ -539,6 +581,7 @@ grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du) } grub_jpeg_idct_transform (du); + return GRUB_ERR_NONE; } static void @@ -597,7 +640,8 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data) data_offset += grub_jpeg_get_word (data); cc = grub_jpeg_get_byte (data); - + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if (cc != 3 && cc != 1) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: component count must be 1 or 3"); @@ -610,7 +654,8 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data) id = grub_jpeg_get_byte (data) - 1; if ((id < 0) || (id >= 3)) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid index"); - + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; ht = grub_jpeg_get_byte (data); data->comp_index[id][1] = (ht >> 4); data->comp_index[id][2] = (ht & 0xF) + 2; @@ -618,11 +663,14 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data) if ((data->comp_index[id][1] < 0) || (data->comp_index[id][1] > 3) || (data->comp_index[id][2] < 0) || (data->comp_index[id][2] > 3)) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid hufftable index"); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; } grub_jpeg_get_byte (data); /* Skip 3 unused bytes. */ grub_jpeg_get_word (data); - + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if (data->file->offset != data_offset) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: extra byte in sos"); @@ -640,6 +688,7 @@ grub_jpeg_decode_data (struct grub_jpeg_data *data) { unsigned c1, vb, hb, nr1, nc1; int rst = data->dri; + grub_err_t err = GRUB_ERR_NONE; vb = 8 << data->log_vs; hb = 8 << data->log_hs; @@ -660,17 +709,22 @@ grub_jpeg_decode_data (struct grub_jpeg_data *data) for (r2 = 0; r2 < (1U << data->log_vs); r2++) for (c2 = 0; c2 < (1U << data->log_hs); c2++) - grub_jpeg_decode_du (data, 0, data->ydu[r2 * 2 + c2]); + { + err = grub_jpeg_decode_du (data, 0, data->ydu[r2 * 2 + c2]); + if (err != GRUB_ERR_NONE) + return err; + } if (data->color_components >= 3) { - grub_jpeg_decode_du (data, 1, data->cbdu); - grub_jpeg_decode_du (data, 2, data->crdu); + err = grub_jpeg_decode_du (data, 1, data->cbdu); + if (err != GRUB_ERR_NONE) + return err; + err = grub_jpeg_decode_du (data, 2, data->crdu); + if (err != GRUB_ERR_NONE) + return err; } - if (grub_errno) - return grub_errno; - nr2 = (data->r1 == nr1 - 1) ? (data->image_height - data->r1 * vb) : vb; nc2 = (c1 == nc1 - 1) ? (data->image_width - c1 * hb) : hb; From 481d030bcec8877adf594522c4cd6a2957199563 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Mon, 28 Jun 2021 14:16:58 +1000 Subject: [PATCH 227/367] video/readers/jpeg: Do not reallocate a given huff table Fix a memory leak where an invalid file could cause us to reallocate memory for a huffman table we had already allocated memory for. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit bc06e12b4de55cc6f926af9f064170c82b1403e9) --- grub-core/video/readers/jpeg.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c index 10225abd53..caa211f06d 100644 --- a/grub-core/video/readers/jpeg.c +++ b/grub-core/video/readers/jpeg.c @@ -245,6 +245,9 @@ grub_jpeg_decode_huff_table (struct grub_jpeg_data *data) n += count[i]; id += ac * 2; + if (data->huff_value[id] != NULL) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: attempt to reallocate huffman table"); data->huff_value[id] = grub_malloc (n); if (grub_errno) return grub_errno; From 46f2644fa4c9e2223c67adaf031aea2ef372e763 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Mon, 28 Jun 2021 14:25:17 +1000 Subject: [PATCH 228/367] video/readers/jpeg: Refuse to handle multiple start of streams An invalid file could contain multiple start of stream blocks, which would cause us to reallocate and leak our bitmap. Refuse to handle multiple start of streams. Additionally, fix a grub_error() call formatting. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit f3a854def3e281b7ad4bbea730cd3046de1da52f) --- grub-core/video/readers/jpeg.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c index caa211f06d..1df1171d78 100644 --- a/grub-core/video/readers/jpeg.c +++ b/grub-core/video/readers/jpeg.c @@ -677,6 +677,9 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data) if (data->file->offset != data_offset) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: extra byte in sos"); + if (*data->bitmap) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: too many start of scan blocks"); + if (grub_video_bitmap_create (data->bitmap, data->image_width, data->image_height, GRUB_VIDEO_BLIT_FORMAT_RGB_888)) @@ -699,8 +702,8 @@ grub_jpeg_decode_data (struct grub_jpeg_data *data) nc1 = (data->image_width + hb - 1) >> (3 + data->log_hs); if (data->bitmap_ptr == NULL) - return grub_error(GRUB_ERR_BAD_FILE_TYPE, - "jpeg: attempted to decode data before start of stream"); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: attempted to decode data before start of stream"); for (; data->r1 < nr1 && (!data->dri || rst); data->r1++, data->bitmap_ptr += (vb * data->image_width - hb * nc1) * 3) From bc3b1a85ac56f84a281af2c23888a022768cd7dc Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Wed, 7 Jul 2021 15:38:19 +1000 Subject: [PATCH 229/367] video/readers/jpeg: Block int underflow -> wild pointer write Certain 1 px wide images caused a wild pointer write in grub_jpeg_ycrcb_to_rgb(). This was caused because in grub_jpeg_decode_data(), we have the following loop: for (; data->r1 < nr1 && (!data->dri || rst); data->r1++, data->bitmap_ptr += (vb * data->image_width - hb * nc1) * 3) We did not check if vb * width >= hb * nc1. On a 64-bit platform, if that turns out to be negative, it will underflow, be interpreted as unsigned 64-bit, then be added to the 64-bit pointer, so we see data->bitmap_ptr jump, e.g.: 0x6180_0000_0480 to 0x6181_0000_0498 ^ ~--- carry has occurred and this pointer is now far away from any object. On a 32-bit platform, it will decrement the pointer, creating a pointer that won't crash but will overwrite random data. Catch the underflow and error out. Fixes: CVE-2021-3697 Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit 41aeb2004db9924fecd9f2dd64bc2a5a5594a4b5) --- grub-core/video/readers/jpeg.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c index 1df1171d78..2da04094b3 100644 --- a/grub-core/video/readers/jpeg.c +++ b/grub-core/video/readers/jpeg.c @@ -705,6 +705,10 @@ grub_jpeg_decode_data (struct grub_jpeg_data *data) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: attempted to decode data before start of stream"); + if (vb * data->image_width <= hb * nc1) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: cannot decode image with these dimensions"); + for (; data->r1 < nr1 && (!data->dri || rst); data->r1++, data->bitmap_ptr += (vb * data->image_width - hb * nc1) * 3) for (c1 = 0; c1 < nc1 && (!data->dri || rst); From 0965ab128fb2e3ba59704c81f195ed2cd437d406 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 13 Jul 2021 13:24:38 +1000 Subject: [PATCH 230/367] normal/charset: Fix array out-of-bounds formatting unicode for display In some cases attempting to display arbitrary binary strings leads to ASAN splats reading the widthspec array out of bounds. Check the index. If it would be out of bounds, return a width of 1. I don't know if that's strictly correct, but we're not really expecting great display of arbitrary binary data, and it's certainly not worse than an OOB read. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit fdf32abc7a3928852422c0f291d8cd1dd6b34a8d) --- grub-core/normal/charset.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/normal/charset.c b/grub-core/normal/charset.c index 4dfcc31078..7a5a7c153c 100644 --- a/grub-core/normal/charset.c +++ b/grub-core/normal/charset.c @@ -395,6 +395,8 @@ grub_unicode_estimate_width (const struct grub_unicode_glyph *c) { if (grub_unicode_get_comb_type (c->base)) return 0; + if (((unsigned long) (c->base >> 3)) >= ARRAY_SIZE (widthspec)) + return 1; if (widthspec[c->base >> 3] & (1 << (c->base & 7))) return 2; else From 9672eb8a4e86b87d1a88c4de620d242a763eb476 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 8 Mar 2022 23:47:46 +1100 Subject: [PATCH 231/367] net/netbuff: Block overly large netbuff allocs A netbuff shouldn't be too huge. It's bounded by MTU and TCP segment reassembly. This helps avoid some bugs (and provides a spot to instrument to catch them at their source). Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit ee9591103004cd13b4efadda671536090ca7fd57) --- grub-core/net/netbuff.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/grub-core/net/netbuff.c b/grub-core/net/netbuff.c index dbeeefe478..d5e9e9a0d7 100644 --- a/grub-core/net/netbuff.c +++ b/grub-core/net/netbuff.c @@ -79,10 +79,23 @@ grub_netbuff_alloc (grub_size_t len) COMPILE_TIME_ASSERT (NETBUFF_ALIGN % sizeof (grub_properly_aligned_t) == 0); + /* + * The largest size of a TCP packet is 64 KiB, and everything else + * should be a lot smaller - most MTUs are 1500 or less. Cap data + * size at 64 KiB + a buffer. + */ + if (len > 0xffffUL + 0x1000UL) + { + grub_error (GRUB_ERR_BUG, + "attempted to allocate a packet that is too big"); + return NULL; + } + if (len < NETBUFFMINLEN) len = NETBUFFMINLEN; len = ALIGN_UP (len, NETBUFF_ALIGN); + #ifdef GRUB_MACHINE_EMU data = grub_malloc (len + sizeof (*nb)); #else From 471a29849ff885f4c64e607c5c2b013687acbcbc Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Mon, 20 Dec 2021 19:41:21 +1100 Subject: [PATCH 232/367] net/ip: Do IP fragment maths safely This avoids an underflow and subsequent unpleasantness. Fixes: CVE-2022-28733 Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit eb74e5743ca7e18a5e75c392fe0b21d1549a1936) --- grub-core/net/ip.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index ce6bdc75c6..cf74f1f794 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -25,6 +25,7 @@ #include #include #include +#include #include struct iphdr { @@ -551,7 +552,14 @@ grub_net_recv_ip4_packets (struct grub_net_buff *nb, { rsm->total_len = (8 * (grub_be_to_cpu16 (iph->frags) & OFFSET_MASK) + (nb->tail - nb->data)); - rsm->total_len -= ((iph->verhdrlen & 0xf) * sizeof (grub_uint32_t)); + + if (grub_sub (rsm->total_len, (iph->verhdrlen & 0xf) * sizeof (grub_uint32_t), + &rsm->total_len)) + { + grub_dprintf ("net", "IP reassembly size underflow\n"); + return GRUB_ERR_NONE; + } + rsm->asm_netbuff = grub_netbuff_alloc (rsm->total_len); if (!rsm->asm_netbuff) { From 013eae6de30db7a05c242ef74568a2881ee2d2c2 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Thu, 16 Sep 2021 01:29:54 +1000 Subject: [PATCH 233/367] net/dns: Fix double-free addresses on corrupt DNS response grub_net_dns_lookup() takes as inputs a pointer to an array of addresses ("addresses") for the given name, and pointer to a number of addresses ("naddresses"). grub_net_dns_lookup() is responsible for allocating "addresses", and the caller is responsible for freeing it if "naddresses" > 0. The DNS recv_hook will sometimes set and free the addresses array, for example if the packet is too short: if (ptr + 10 >= nb->tail) { if (!*data->naddresses) grub_free (*data->addresses); grub_netbuff_free (nb); return GRUB_ERR_NONE; } Later on the nslookup command code unconditionally frees the "addresses" array. Normally this is fine: the array is either populated with valid data or is NULL. But in these sorts of error cases it is neither NULL nor valid and we get a double-free. Only free "addresses" if "naddresses" > 0. It looks like the other use of grub_net_dns_lookup() is not affected. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit eb2e69fcf51307757e43f55ee8c9354d1ee42dd1) --- grub-core/net/dns.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c index 906ec7d678..135faac035 100644 --- a/grub-core/net/dns.c +++ b/grub-core/net/dns.c @@ -667,9 +667,11 @@ grub_cmd_nslookup (struct grub_command *cmd __attribute__ ((unused)), grub_net_addr_to_str (&addresses[i], buf); grub_printf ("%s\n", buf); } - grub_free (addresses); if (naddresses) - return GRUB_ERR_NONE; + { + grub_free (addresses); + return GRUB_ERR_NONE; + } return grub_error (GRUB_ERR_NET_NO_DOMAIN, N_("no DNS record found")); } From 259dfb2b1beb5eb7e8f9bfa2fd24230e0c1d2af1 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Mon, 20 Dec 2021 21:55:43 +1100 Subject: [PATCH 234/367] net/dns: Don't read past the end of the string we're checking against I don't really understand what's going on here but fuzzing found a bug where we read past the end of check_with. That's a C string, so use grub_strlen() to make sure we don't overread it. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit 6a97b3f4b1d5173aa516edc6dedbc63de7306d21) --- grub-core/net/dns.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c index 135faac035..17961a9f18 100644 --- a/grub-core/net/dns.c +++ b/grub-core/net/dns.c @@ -146,11 +146,18 @@ check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head, int *length, char *set) { const char *readable_ptr = check_with; + int readable_len; const grub_uint8_t *ptr; char *optr = set; int bytes_processed = 0; if (length) *length = 0; + + if (readable_ptr != NULL) + readable_len = grub_strlen (readable_ptr); + else + readable_len = 0; + for (ptr = name_at; ptr < tail && bytes_processed < tail - head + 2; ) { /* End marker. */ @@ -172,13 +179,16 @@ check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head, ptr = head + (((ptr[0] & 0x3f) << 8) | ptr[1]); continue; } - if (readable_ptr && grub_memcmp (ptr + 1, readable_ptr, *ptr) != 0) + if (readable_ptr != NULL && (*ptr > readable_len || grub_memcmp (ptr + 1, readable_ptr, *ptr) != 0)) return 0; if (grub_memchr (ptr + 1, 0, *ptr) || grub_memchr (ptr + 1, '.', *ptr)) return 0; if (readable_ptr) - readable_ptr += *ptr; + { + readable_ptr += *ptr; + readable_len -= *ptr; + } if (readable_ptr && *readable_ptr != '.' && *readable_ptr != 0) return 0; bytes_processed += *ptr + 1; @@ -192,7 +202,10 @@ check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head, if (optr) *optr++ = '.'; if (readable_ptr && *readable_ptr) - readable_ptr++; + { + readable_ptr++; + readable_len--; + } ptr += *ptr + 1; } return 0; From 1aed0af1b3923823aacc775106b7437cae92db48 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Mon, 20 Sep 2021 01:12:24 +1000 Subject: [PATCH 235/367] net/tftp: Prevent a UAF and double-free from a failed seek A malicious tftp server can cause UAFs and a double free. An attempt to read from a network file is handled by grub_net_fs_read(). If the read is at an offset other than the current offset, grub_net_seek_real() is invoked. In grub_net_seek_real(), if a backwards seek cannot be satisfied from the currently received packets, and the underlying transport does not provide a seek method, then grub_net_seek_real() will close and reopen the network protocol layer. For tftp, the ->close() call goes to tftp_close() and frees the tftp_data_t file->data. The file->data pointer is not nulled out after the free. If the ->open() call fails, the file->data will not be reallocated and will continue point to a freed memory block. This could happen from a server refusing to send the requisite ack to the new tftp request, for example. The seek and the read will then fail, but the grub_file continues to exist: the failed seek does not necessarily cause the entire file to be thrown away (e.g. where the file is checked to see if it is gzipped/lzio/xz/etc., a read failure is interpreted as a decompressor passing on the file, not as an invalidation of the entire grub_file_t structure). This means subsequent attempts to read or seek the file will use the old file->data after free. Eventually, the file will be close()d again and file->data will be freed again. Mark a net_fs file that doesn't reopen as broken. Do not permit read() or close() on a broken file (seek is not exposed directly to the file API - it is only called as part of read, so this blocks seeks as well). As an additional defence, null out the ->data pointer if tftp_open() fails. That would have lead to a simple null pointer dereference rather than a mess of UAFs. This may affect other protocols, I haven't checked. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit dada1dda695439bb55b2848dddc2d89843552f81) --- grub-core/net/net.c | 11 +++++++++-- grub-core/net/tftp.c | 1 + include/grub/net.h | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 55aed92722..1001c611d1 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -1625,7 +1625,8 @@ grub_net_fs_close (grub_file_t file) grub_netbuff_free (file->device->net->packs.first->nb); grub_net_remove_packet (file->device->net->packs.first); } - file->device->net->protocol->close (file); + if (!file->device->net->broken) + file->device->net->protocol->close (file); grub_free (file->device->net->name); return GRUB_ERR_NONE; } @@ -1847,7 +1848,10 @@ grub_net_seek_real (struct grub_file *file, grub_off_t offset) file->device->net->stall = 0; err = file->device->net->protocol->open (file, file->device->net->name); if (err) - return err; + { + file->device->net->broken = 1; + return err; + } grub_net_fs_read_real (file, NULL, offset); return grub_errno; } @@ -1856,6 +1860,9 @@ grub_net_seek_real (struct grub_file *file, grub_off_t offset) static grub_ssize_t grub_net_fs_read (grub_file_t file, char *buf, grub_size_t len) { + if (file->device->net->broken) + return -1; + if (file->offset != file->device->net->offset) { grub_err_t err; diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index d54b13f09f..788ad1dc44 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -408,6 +408,7 @@ tftp_open (struct grub_file *file, const char *filename) { grub_net_udp_close (data->sock); grub_free (data); + file->data = NULL; return grub_errno; } diff --git a/include/grub/net.h b/include/grub/net.h index 42af7de250..9e4898cc6b 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -280,6 +280,7 @@ typedef struct grub_net grub_fs_t fs; int eof; int stall; + int broken; } *grub_net_t; extern grub_net_t (*EXPORT_VAR (grub_net_open)) (const char *name); From da32934c3df6cc4c51eb803b95aa5640d3bc8083 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 18 Jan 2022 14:29:20 +1100 Subject: [PATCH 236/367] net/tftp: Avoid a trivial UAF Under tftp errors, we print a tftp error message from the tftp header. However, the tftph pointer is a pointer inside nb, the netbuff. Previously, we were freeing the nb and then dereferencing it. Don't do that, use it and then free it later. This isn't really _bad_ per se, especially as we're single-threaded, but it trips up fuzzers. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit 956f4329cec23e4375182030ca9b2be631a61ba5) --- grub-core/net/tftp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index 788ad1dc44..a95766dcbd 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -251,9 +251,9 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), return GRUB_ERR_NONE; case TFTP_ERROR: data->have_oack = 1; - grub_netbuff_free (nb); grub_error (GRUB_ERR_IO, "%s", tftph->u.err.errmsg); grub_error_save (&data->save_err); + grub_netbuff_free (nb); return GRUB_ERR_NONE; default: grub_netbuff_free (nb); From 1db4acf9f008086064462dd096c41da21e936c6b Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 1 Mar 2022 23:14:15 +1100 Subject: [PATCH 237/367] net/http: Do not tear down socket if it's already been torn down It's possible for data->sock to get torn down in tcp error handling. If we unconditionally tear it down again we will end up doing writes to an offset of the NULL pointer when we go to tear it down again. Detect if it has been torn down and don't do it again. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit ec233d3ecf995293304de443579aab5c46c49e85) --- grub-core/net/http.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/grub-core/net/http.c b/grub-core/net/http.c index 7f878b5615..19cb8768e3 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -427,7 +427,7 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) return err; } - for (i = 0; !data->headers_recv && i < 100; i++) + for (i = 0; data->sock && !data->headers_recv && i < 100; i++) { grub_net_tcp_retransmit (); grub_net_poll_cards (300, &data->headers_recv); @@ -435,7 +435,8 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) if (!data->headers_recv) { - grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); + if (data->sock) + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); if (data->err) { char *str = data->errmsg; From b47d5626d2a6bcc22be549aba6d401c57484ece9 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 8 Mar 2022 18:17:03 +1100 Subject: [PATCH 238/367] net/http: Fix OOB write for split http headers GRUB has special code for handling an http header that is split across two packets. The code tracks the end of line by looking for a "\n" byte. The code for split headers has always advanced the pointer just past the end of the line, whereas the code that handles unsplit headers does not advance the pointer. This extra advance causes the length to be one greater, which breaks an assumption in parse_line(), leading to it writing a NUL byte one byte past the end of the buffer where we reconstruct the line from the two packets. It's conceivable that an attacker controlled set of packets could cause this to zero out the first byte of the "next" pointer of the grub_mm_region structure following the current_line buffer. Do not advance the pointer in the split header case. Fixes: CVE-2022-28734 Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit e9fb459638811c12b0989dbf64e3e124974ef617) --- grub-core/net/http.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/grub-core/net/http.c b/grub-core/net/http.c index 19cb8768e3..58546739a2 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -193,9 +193,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), int have_line = 1; char *t; ptr = grub_memchr (nb->data, '\n', nb->tail - nb->data); - if (ptr) - ptr++; - else + if (ptr == NULL) { have_line = 0; ptr = (char *) nb->tail; From 72df538472cd24b499a56f840a52d78a525fc7e4 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 8 Mar 2022 19:04:40 +1100 Subject: [PATCH 239/367] net/http: Error out on headers with LF without CR In a similar vein to the previous patch, parse_line() would write a NUL byte past the end of the buffer if there was an HTTP header with a LF rather than a CRLF. RFC-2616 says: Many HTTP/1.1 header field values consist of words separated by LWS or special characters. These special characters MUST be in a quoted string to be used within a parameter value (as defined in section 3.6). We don't support quoted sections or continuation lines, etc. If we see an LF that's not part of a CRLF, bail out. Fixes: CVE-2022-28734 Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit d232ad41ac4979a9de4d746e5fdff9caf0e303de) --- grub-core/net/http.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/grub-core/net/http.c b/grub-core/net/http.c index 58546739a2..57d2721719 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -69,7 +69,15 @@ parse_line (grub_file_t file, http_data_t data, char *ptr, grub_size_t len) char *end = ptr + len; while (end > ptr && *(end - 1) == '\r') end--; + + /* LF without CR. */ + if (end == ptr + len) + { + data->errmsg = grub_strdup (_("invalid HTTP header - LF without CR")); + return GRUB_ERR_NONE; + } *end = 0; + /* Trailing CRLF. */ if (data->in_chunk_len == 1) { From 8cc635041db65d86645ab421e34e73970b959949 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Wed, 6 Apr 2022 18:03:37 +0530 Subject: [PATCH 240/367] fs/f2fs: Do not read past the end of nat journal entries A corrupt f2fs file system could specify a nat journal entry count that is beyond the maximum NAT_JOURNAL_ENTRIES. Check if the specified nat journal entry count before accessing the array, and throw an error if it is too large. Signed-off-by: Sudhakar Kuppusamy Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit a3988cb3f0a108dd67ac127a79a4c8479d23334e) --- grub-core/fs/f2fs.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/grub-core/fs/f2fs.c b/grub-core/fs/f2fs.c index 8a9992ca9e..63702214b0 100644 --- a/grub-core/fs/f2fs.c +++ b/grub-core/fs/f2fs.c @@ -632,23 +632,27 @@ get_nat_journal (struct grub_f2fs_data *data) return err; } -static grub_uint32_t -get_blkaddr_from_nat_journal (struct grub_f2fs_data *data, grub_uint32_t nid) +static grub_err_t +get_blkaddr_from_nat_journal (struct grub_f2fs_data *data, grub_uint32_t nid, + grub_uint32_t *blkaddr) { grub_uint16_t n = grub_le_to_cpu16 (data->nat_j.n_nats); - grub_uint32_t blkaddr = 0; grub_uint16_t i; + if (n >= NAT_JOURNAL_ENTRIES) + return grub_error (GRUB_ERR_BAD_FS, + "invalid number of nat journal entries"); + for (i = 0; i < n; i++) { if (grub_le_to_cpu32 (data->nat_j.entries[i].nid) == nid) { - blkaddr = grub_le_to_cpu32 (data->nat_j.entries[i].ne.block_addr); + *blkaddr = grub_le_to_cpu32 (data->nat_j.entries[i].ne.block_addr); break; } } - return blkaddr; + return GRUB_ERR_NONE; } static grub_uint32_t @@ -656,10 +660,13 @@ get_node_blkaddr (struct grub_f2fs_data *data, grub_uint32_t nid) { struct grub_f2fs_nat_block *nat_block; grub_uint32_t seg_off, block_off, entry_off, block_addr; - grub_uint32_t blkaddr; + grub_uint32_t blkaddr = 0; grub_err_t err; - blkaddr = get_blkaddr_from_nat_journal (data, nid); + err = get_blkaddr_from_nat_journal (data, nid, &blkaddr); + if (err != GRUB_ERR_NONE) + return 0; + if (blkaddr) return blkaddr; From 400ca4e1305d3e522e6826032d66baa61a20c228 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Wed, 6 Apr 2022 18:49:09 +0530 Subject: [PATCH 241/367] fs/f2fs: Do not read past the end of nat bitmap A corrupt f2fs filesystem could have a block offset or a bitmap offset that would cause us to read beyond the bounds of the nat bitmap. Introduce the nat_bitmap_size member in grub_f2fs_data which holds the size of nat bitmap. Set the size when loading the nat bitmap in nat_bitmap_ptr(), and catch when an invalid offset would create a pointer past the end of the allocated space. Check against the bitmap size in grub_f2fs_test_bit() test bit to avoid reading past the end of the nat bitmap. Signed-off-by: Sudhakar Kuppusamy Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit 62d63d5e38c67a6e349148bf7cb87c560e935a7e) --- grub-core/fs/f2fs.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/grub-core/fs/f2fs.c b/grub-core/fs/f2fs.c index 63702214b0..8898b235e0 100644 --- a/grub-core/fs/f2fs.c +++ b/grub-core/fs/f2fs.c @@ -122,6 +122,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define F2FS_INLINE_DOTS 0x10 /* File having implicit dot dentries. */ #define MAX_VOLUME_NAME 512 +#define MAX_NAT_BITMAP_SIZE 3900 enum FILE_TYPE { @@ -183,7 +184,7 @@ struct grub_f2fs_checkpoint grub_uint32_t checksum_offset; grub_uint64_t elapsed_time; grub_uint8_t alloc_type[MAX_ACTIVE_LOGS]; - grub_uint8_t sit_nat_version_bitmap[3900]; + grub_uint8_t sit_nat_version_bitmap[MAX_NAT_BITMAP_SIZE]; grub_uint32_t checksum; } GRUB_PACKED; @@ -302,6 +303,7 @@ struct grub_f2fs_data struct grub_f2fs_nat_journal nat_j; char *nat_bitmap; + grub_uint32_t nat_bitmap_size; grub_disk_t disk; struct grub_f2fs_node *inode; @@ -377,15 +379,20 @@ sum_blk_addr (struct grub_f2fs_data *data, int base, int type) } static void * -nat_bitmap_ptr (struct grub_f2fs_data *data) +nat_bitmap_ptr (struct grub_f2fs_data *data, grub_uint32_t *nat_bitmap_size) { struct grub_f2fs_checkpoint *ckpt = &data->ckpt; grub_uint32_t offset; + *nat_bitmap_size = MAX_NAT_BITMAP_SIZE; if (grub_le_to_cpu32 (data->sblock.cp_payload) > 0) return ckpt->sit_nat_version_bitmap; offset = grub_le_to_cpu32 (ckpt->sit_ver_bitmap_bytesize); + if (offset >= MAX_NAT_BITMAP_SIZE) + return NULL; + + *nat_bitmap_size = *nat_bitmap_size - offset; return ckpt->sit_nat_version_bitmap + offset; } @@ -438,11 +445,15 @@ grub_f2fs_crc_valid (grub_uint32_t blk_crc, void *buf, const grub_uint32_t len) } static int -grub_f2fs_test_bit (grub_uint32_t nr, const char *p) +grub_f2fs_test_bit (grub_uint32_t nr, const char *p, grub_uint32_t len) { int mask; + grub_uint32_t shifted_nr = (nr >> 3); + + if (shifted_nr >= len) + return -1; - p += (nr >> 3); + p += shifted_nr; mask = 1 << (7 - (nr & 0x07)); return mask & *p; @@ -662,6 +673,7 @@ get_node_blkaddr (struct grub_f2fs_data *data, grub_uint32_t nid) grub_uint32_t seg_off, block_off, entry_off, block_addr; grub_uint32_t blkaddr = 0; grub_err_t err; + int result_bit; err = get_blkaddr_from_nat_journal (data, nid, &blkaddr); if (err != GRUB_ERR_NONE) @@ -682,8 +694,15 @@ get_node_blkaddr (struct grub_f2fs_data *data, grub_uint32_t nid) ((seg_off * data->blocks_per_seg) << 1) + (block_off & (data->blocks_per_seg - 1)); - if (grub_f2fs_test_bit (block_off, data->nat_bitmap)) + result_bit = grub_f2fs_test_bit (block_off, data->nat_bitmap, + data->nat_bitmap_size); + if (result_bit > 0) block_addr += data->blocks_per_seg; + else if (result_bit == -1) + { + grub_free (nat_block); + return 0; + } err = grub_f2fs_block_read (data, block_addr, nat_block); if (err) @@ -833,7 +852,9 @@ grub_f2fs_mount (grub_disk_t disk) if (err) goto fail; - data->nat_bitmap = nat_bitmap_ptr (data); + data->nat_bitmap = nat_bitmap_ptr (data, &data->nat_bitmap_size); + if (data->nat_bitmap == NULL) + goto fail; err = get_nat_journal (data); if (err) From acaddf9c8c61b333c8d2151afb9dfe3557cad87c Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Wed, 6 Apr 2022 18:17:43 +0530 Subject: [PATCH 242/367] fs/f2fs: Do not copy file names that are too long A corrupt f2fs file system might specify a name length which is greater than the maximum name length supported by the GRUB f2fs driver. We will allocate enough memory to store the overly long name, but there are only F2FS_NAME_LEN bytes in the source, so we would read past the end of the source. While checking directory entries, do not copy a file name with an invalid length. Signed-off-by: Sudhakar Kuppusamy Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit 9a891f638509e031d322c94e3cbcf38d36f3993a) --- grub-core/fs/f2fs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/grub-core/fs/f2fs.c b/grub-core/fs/f2fs.c index 8898b235e0..df6beb544c 100644 --- a/grub-core/fs/f2fs.c +++ b/grub-core/fs/f2fs.c @@ -1003,6 +1003,10 @@ grub_f2fs_check_dentries (struct grub_f2fs_dir_iter_ctx *ctx) ftype = ctx->dentry[i].file_type; name_len = grub_le_to_cpu16 (ctx->dentry[i].name_len); + + if (name_len >= F2FS_NAME_LEN) + return 0; + filename = grub_malloc (name_len + 1); if (!filename) return 0; From b254bf5477cfa8c05443bafcf4cca8f44bd59f31 Mon Sep 17 00:00:00 2001 From: Darren Kenny Date: Tue, 29 Mar 2022 10:49:56 +0000 Subject: [PATCH 243/367] fs/btrfs: Fix several fuzz issues with invalid dir item sizing According to the btrfs code in Linux, the structure of a directory item leaf should be of the form: |struct btrfs_dir_item|name|data| in GRUB the name len and data len are in the grub_btrfs_dir_item structure's n and m fields respectively. The combined size of the structure, name and data should be less than the allocated memory, a difference to the Linux kernel's struct btrfs_dir_item is that the grub_btrfs_dir_item has an extra field for where the name is stored, so we adjust for that too. Signed-off-by: Darren Kenny Reviewed-by: Daniel Kiper (cherry picked from commit 6d3f06c0b6a8992b9b1bb0e62af93ac5ff2781f0) [rharwood: we've an extra variable here] Signed-off-by: Robbie Harwood --- grub-core/fs/btrfs.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 07c0ff874b..2fcfb738fe 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -2254,6 +2254,7 @@ grub_btrfs_dir (grub_device_t device, const char *path, grub_uint64_t tree; grub_uint8_t type; char *new_path = NULL; + grub_size_t est_size = 0; if (!data) return grub_errno; @@ -2320,6 +2321,18 @@ grub_btrfs_dir (grub_device_t device, const char *path, break; } + if (direl == NULL || + grub_add (grub_le_to_cpu16 (direl->n), + grub_le_to_cpu16 (direl->m), &est_size) || + grub_add (est_size, sizeof (*direl), &est_size) || + grub_sub (est_size, sizeof (direl->name), &est_size) || + est_size > allocated) + { + grub_errno = GRUB_ERR_OUT_OF_RANGE; + r = -grub_errno; + goto out; + } + for (cdirel = direl; (grub_uint8_t *) cdirel - (grub_uint8_t *) direl < (grub_ssize_t) elemsize; @@ -2330,6 +2343,19 @@ grub_btrfs_dir (grub_device_t device, const char *path, char c; struct grub_btrfs_inode inode; struct grub_dirhook_info info; + + if (cdirel == NULL || + grub_add (grub_le_to_cpu16 (cdirel->n), + grub_le_to_cpu16 (cdirel->m), &est_size) || + grub_add (est_size, sizeof (*cdirel), &est_size) || + grub_sub (est_size, sizeof (cdirel->name), &est_size) || + est_size > allocated) + { + grub_errno = GRUB_ERR_OUT_OF_RANGE; + r = -grub_errno; + goto out; + } + err = grub_btrfs_read_inode (data, &inode, cdirel->key.object_id, tree); grub_memset (&info, 0, sizeof (info)); From aa74f4b7c92d9af6cf5637db8d6cd57481dff962 Mon Sep 17 00:00:00 2001 From: Darren Kenny Date: Tue, 29 Mar 2022 15:52:46 +0000 Subject: [PATCH 244/367] fs/btrfs: Fix more ASAN and SEGV issues found with fuzzing The fuzzer is generating btrfs file systems that have chunks with invalid combinations of stripes and substripes for the given RAID configurations. After examining the Linux kernel fs/btrfs/tree-checker.c code, it appears that sub-stripes should only be applied to RAID10, and in that case there should only ever be 2 of them. Similarly, RAID single should only have 1 stripe, and RAID1/1C3/1C4 should have 2. 3 or 4 stripes respectively, which is what redundancy corresponds. Some of the chunks ended up with a size of 0, which grub_malloc() still returned memory for and in turn generated ASAN errors later when accessed. While it would be possible to specifically limit the number of stripes, a more correct test was on the combination of the chunk item, and the number of stripes by the size of the chunk stripe structure in comparison to the size of the chunk itself. Signed-off-by: Darren Kenny Reviewed-by: Daniel Kiper (cherry picked from commit 3849647b4b98a4419366708fc4b7f339c6f55ec7) --- grub-core/fs/btrfs.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 2fcfb738fe..0e9b450413 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -941,6 +941,12 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, return grub_error (GRUB_ERR_BAD_FS, "couldn't find the chunk descriptor"); + if (!chsize) + { + grub_dprintf ("btrfs", "zero-size chunk\n"); + return grub_error (GRUB_ERR_BAD_FS, + "got an invalid zero-size chunk"); + } chunk = grub_malloc (chsize); if (!chunk) return grub_errno; @@ -999,6 +1005,16 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, stripe_length = grub_divmod64 (grub_le_to_cpu64 (chunk->size), nstripes, NULL); + + /* For single, there should be exactly 1 stripe. */ + if (grub_le_to_cpu16 (chunk->nstripes) != 1) + { + grub_dprintf ("btrfs", "invalid RAID_SINGLE: nstripes != 1 (%u)\n", + grub_le_to_cpu16 (chunk->nstripes)); + return grub_error (GRUB_ERR_BAD_FS, + "invalid RAID_SINGLE: nstripes != 1 (%u)", + grub_le_to_cpu16 (chunk->nstripes)); + } if (stripe_length == 0) stripe_length = 512; stripen = grub_divmod64 (off, stripe_length, &stripe_offset); @@ -1018,6 +1034,19 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, stripen = 0; stripe_offset = off; csize = grub_le_to_cpu64 (chunk->size) - off; + + /* + * Redundancy, and substripes only apply to RAID10, and there + * should be exactly 2 sub-stripes. + */ + if (grub_le_to_cpu16 (chunk->nstripes) != redundancy) + { + grub_dprintf ("btrfs", "invalid RAID1: nstripes != %u (%u)\n", + redundancy, grub_le_to_cpu16 (chunk->nstripes)); + return grub_error (GRUB_ERR_BAD_FS, + "invalid RAID1: nstripes != %u (%u)", + redundancy, grub_le_to_cpu16 (chunk->nstripes)); + } break; } case GRUB_BTRFS_CHUNK_TYPE_RAID0: @@ -1054,6 +1083,20 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, stripe_offset = low + chunk_stripe_length * high; csize = chunk_stripe_length - low; + + /* + * Substripes only apply to RAID10, and there + * should be exactly 2 sub-stripes. + */ + if (grub_le_to_cpu16 (chunk->nsubstripes) != 2) + { + grub_dprintf ("btrfs", "invalid RAID10: nsubstripes != 2 (%u)", + grub_le_to_cpu16 (chunk->nsubstripes)); + return grub_error (GRUB_ERR_BAD_FS, + "invalid RAID10: nsubstripes != 2 (%u)", + grub_le_to_cpu16 (chunk->nsubstripes)); + } + break; } case GRUB_BTRFS_CHUNK_TYPE_RAID5: @@ -1153,6 +1196,8 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, for (j = 0; j < 2; j++) { + grub_size_t est_chunk_alloc = 0; + grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T "+0x%" PRIxGRUB_UINT64_T " (%d stripes (%d substripes) of %" @@ -1165,6 +1210,16 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, grub_dprintf ("btrfs", "reading laddr 0x%" PRIxGRUB_UINT64_T "\n", addr); + if (grub_mul (sizeof (struct grub_btrfs_chunk_stripe), + grub_le_to_cpu16 (chunk->nstripes), &est_chunk_alloc) || + grub_add (est_chunk_alloc, + sizeof (struct grub_btrfs_chunk_item), &est_chunk_alloc) || + est_chunk_alloc > chunk->size) + { + err = GRUB_ERR_BAD_FS; + break; + } + if (is_raid56) { err = btrfs_read_from_chunk (data, chunk, stripen, From f887b09b01f36074337ca13f5190c650537d9073 Mon Sep 17 00:00:00 2001 From: Darren Kenny Date: Thu, 7 Apr 2022 15:18:12 +0000 Subject: [PATCH 245/367] fs/btrfs: Fix more fuzz issues related to chunks The corpus we generating issues in grub_btrfs_read_logical() when attempting to iterate over nstripes entries in the boot mapping. In most cases the reason for the failure was that the number of strips exceeded the possible space statically allocated in superblock bootmapping space. Each stripe entry in the bootmapping block consists of a grub_btrfs_key followed by a grub_btrfs_chunk_stripe. Another issue that came up was that while calculating the chunk size, in an earlier piece of code in that function, depending on the data provided in the btrfs file system, it would end up calculating a size that was too small to contain even 1 grub_btrfs_chunk_item, which is obviously invalid too. Signed-off-by: Darren Kenny Reviewed-by: Daniel Kiper (cherry picked from commit e00cd76cbadcc897a9cc4087cb2fcb5dbe15e596) --- grub-core/fs/btrfs.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 0e9b450413..47325f6ad7 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -947,6 +947,17 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, return grub_error (GRUB_ERR_BAD_FS, "got an invalid zero-size chunk"); } + + /* + * The space being allocated for a chunk should at least be able to + * contain one chunk item. + */ + if (chsize < sizeof (struct grub_btrfs_chunk_item)) + { + grub_dprintf ("btrfs", "chunk-size too small\n"); + return grub_error (GRUB_ERR_BAD_FS, + "got an invalid chunk size"); + } chunk = grub_malloc (chsize); if (!chunk) return grub_errno; @@ -1194,6 +1205,13 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, if (csize > (grub_uint64_t) size) csize = size; + /* + * The space for a chunk stripe is limited to the space provide in the super-block's + * bootstrap mapping with an initial btrfs key at the start of each chunk. + */ + grub_size_t avail_stripes = sizeof (data->sblock.bootstrap_mapping) / + (sizeof (struct grub_btrfs_key) + sizeof (struct grub_btrfs_chunk_stripe)); + for (j = 0; j < 2; j++) { grub_size_t est_chunk_alloc = 0; @@ -1220,6 +1238,12 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, break; } + if (grub_le_to_cpu16 (chunk->nstripes) > avail_stripes) + { + err = GRUB_ERR_BAD_FS; + break; + } + if (is_raid56) { err = btrfs_read_from_chunk (data, chunk, stripen, From f94152b93132c06245e4002b73688c5f7c3c4fba Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 21 Mar 2022 16:06:10 -0400 Subject: [PATCH 246/367] misc: Make grub_min() and grub_max() more resilient. grub_min(a,b) and grub_max(a,b) use a relatively naive implementation which leads to several problems: - they evaluate their parameters more than once - the naive way to address this, to declare temporary variables in a statement-expression, isn't resilient against nested uses, because MIN(a,MIN(b,c)) results in the temporary variables being declared in two nested scopes, which may result in a build warning depending on your build options. This patch changes our implementation to use a statement-expression inside a helper macro, and creates the symbols for the temporary variables with __COUNTER__ (A GNU C cpp extension) and token pasting to create uniquely named internal variables. Signed-off-by: Peter Jones --- grub-core/loader/multiboot_elfxx.c | 4 +--- include/grub/misc.h | 25 +++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/grub-core/loader/multiboot_elfxx.c b/grub-core/loader/multiboot_elfxx.c index f2318e0d16..87f6e31aa6 100644 --- a/grub-core/loader/multiboot_elfxx.c +++ b/grub-core/loader/multiboot_elfxx.c @@ -35,9 +35,7 @@ #endif #include - -#define CONCAT(a,b) CONCAT_(a, b) -#define CONCAT_(a,b) a ## b +#include #pragma GCC diagnostic ignored "-Wcast-align" diff --git a/include/grub/misc.h b/include/grub/misc.h index 6c4aa85ac5..cf84aec1db 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -35,6 +35,14 @@ #define ARRAY_SIZE(array) (sizeof (array) / sizeof (array[0])) #define COMPILE_TIME_ASSERT(cond) switch (0) { case 1: case !(cond): ; } +#ifndef CONCAT_ +#define CONCAT_(a, b) a ## b +#endif + +#ifndef CONCAT +#define CONCAT(a, b) CONCAT_(a, b) +#endif + #define grub_dprintf(condition, ...) grub_real_dprintf(GRUB_FILE, __LINE__, condition, __VA_ARGS__) void *EXPORT_FUNC(grub_memmove) (void *dest, const void *src, grub_size_t n); @@ -498,8 +506,21 @@ void EXPORT_FUNC(grub_real_boot_time) (const char *file, #define grub_boot_time(...) #endif -#define grub_max(a, b) (((a) > (b)) ? (a) : (b)) -#define grub_min(a, b) (((a) < (b)) ? (a) : (b)) +#define _grub_min(a, b, _a, _b) \ + ({ typeof (a) _a = (a); \ + typeof (b) _b = (b); \ + _a < _b ? _a : _b; }) +#define grub_min(a, b) _grub_min(a, b, \ + CONCAT(_a_,__COUNTER__), \ + CONCAT(_b_,__COUNTER__)) + +#define _grub_max(a, b, _a, _b) \ + ({ typeof (a) _a = (a); \ + typeof (b) _b = (b); \ + _a > _b ? _a : _b; }) +#define grub_max(a, b) _grub_max(a, b, \ + CONCAT(_a_,__COUNTER__), \ + CONCAT(_b_,__COUNTER__)) #define grub_log2ull(n) (GRUB_TYPE_BITS (grub_uint64_t) - __builtin_clzll (n) - 1) From 461b42c799e0efbae3a5e8315091db343d9eaec3 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 21 Apr 2022 16:31:17 -0400 Subject: [PATCH 247/367] ReiserFS: switch to using grub_min()/grub_max() This is a minor cleanup patch to remove the bespoke MIN() and MAX() definitions from the reiserfs driver, and uses grub_min() / grub_max() instead. Signed-off-by: Peter Jones --- grub-core/fs/reiserfs.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/grub-core/fs/reiserfs.c b/grub-core/fs/reiserfs.c index af6a226a7f..b8253da7fe 100644 --- a/grub-core/fs/reiserfs.c +++ b/grub-core/fs/reiserfs.c @@ -42,16 +42,6 @@ GRUB_MOD_LICENSE ("GPLv3+"); -#define MIN(a, b) \ - ({ typeof (a) _a = (a); \ - typeof (b) _b = (b); \ - _a < _b ? _a : _b; }) - -#define MAX(a, b) \ - ({ typeof (a) _a = (a); \ - typeof (b) _b = (b); \ - _a > _b ? _a : _b; }) - #define REISERFS_SUPER_BLOCK_OFFSET 0x10000 #define REISERFS_MAGIC_LEN 12 #define REISERFS_MAGIC_STRING "ReIsEr" @@ -1076,7 +1066,7 @@ grub_reiserfs_read_real (struct grub_fshelp_node *node, grub_reiserfs_set_key_type (&key, GRUB_REISERFS_ANY, 2); initial_position = off; current_position = 0; - final_position = MIN (len + initial_position, node->size); + final_position = grub_min (len + initial_position, node->size); grub_dprintf ("reiserfs", "Reading from %lld to %lld (%lld instead of requested %ld)\n", (unsigned long long) initial_position, @@ -1115,8 +1105,8 @@ grub_reiserfs_read_real (struct grub_fshelp_node *node, grub_dprintf ("reiserfs_blocktype", "D: %u\n", (unsigned) block); if (initial_position < current_position + item_size) { - offset = MAX ((signed) (initial_position - current_position), 0); - length = (MIN (item_size, final_position - current_position) + offset = grub_max ((signed) (initial_position - current_position), 0); + length = (grub_min (item_size, final_position - current_position) - offset); grub_dprintf ("reiserfs", "Reading direct block %u from %u to %u...\n", @@ -1161,9 +1151,9 @@ grub_reiserfs_read_real (struct grub_fshelp_node *node, grub_dprintf ("reiserfs_blocktype", "I: %u\n", (unsigned) block); if (current_position + block_size >= initial_position) { - offset = MAX ((signed) (initial_position - current_position), - 0); - length = (MIN (block_size, final_position - current_position) + offset = grub_max ((signed) (initial_position - current_position), + 0); + length = (grub_min (block_size, final_position - current_position) - offset); grub_dprintf ("reiserfs", "Reading indirect block %u from %u to %u...\n", @@ -1205,7 +1195,7 @@ grub_reiserfs_read_real (struct grub_fshelp_node *node, switch (found.type) { case GRUB_REISERFS_DIRECT: - read_length = MIN (len, item_size - file->offset); + read_length = grub_min (len, item_size - file->offset); grub_disk_read (found.data->disk, (found.block_number * block_size) / GRUB_DISK_SECTOR_SIZE, grub_le_to_cpu16 (found.header.item_location) + file->offset, @@ -1224,12 +1214,12 @@ grub_reiserfs_read_real (struct grub_fshelp_node *node, item_size, (char *) indirect_block_ptr); if (grub_errno) goto fail; - len = MIN (len, file->size - file->offset); + len = grub_min (len, file->size - file->offset); for (indirect_block = file->offset / block_size; indirect_block < indirect_block_count && read_length < len; indirect_block++) { - read = MIN (block_size, len - read_length); + read = grub_min (block_size, len - read_length); grub_disk_read (found.data->disk, (grub_le_to_cpu32 (indirect_block_ptr[indirect_block]) * block_size) / GRUB_DISK_SECTOR_SIZE, file->offset % block_size, read, From a21e5c1ffee828e30703ffaaa77f48fe07a188d1 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 24 Mar 2022 14:40:01 -0400 Subject: [PATCH 248/367] misc: make grub_boot_time() also call grub_dprintf("boot",...) Currently grub_boot_time() includes valuable debugging messages, but if you build without BOOT_TIME_STATS enabled, they are silently and confusingly compiled away. This patch changes grub_boot_time() to also log when "boot" is enabled in DEBUG, regardless of BOOT_TIME_STATS. Signed-off-by: Peter Jones --- grub-core/kern/misc.c | 3 ++- include/grub/misc.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index a186ad3dd4..cb45461402 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -1334,7 +1334,8 @@ grub_real_boot_time (const char *file, n->next = 0; va_start (args, fmt); - n->msg = grub_xvasprintf (fmt, args); + n->msg = grub_xvasprintf (fmt, args); + grub_dprintf ("boot", "%s\n", n->msg); va_end (args); *boot_time_last = n; diff --git a/include/grub/misc.h b/include/grub/misc.h index cf84aec1db..faae0ae860 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -503,7 +503,7 @@ void EXPORT_FUNC(grub_real_boot_time) (const char *file, const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 3, 4))); #define grub_boot_time(...) grub_real_boot_time(GRUB_FILE, __LINE__, __VA_ARGS__) #else -#define grub_boot_time(...) +#define grub_boot_time(fmt, ...) grub_dprintf("boot", fmt "\n", ##__VA_ARGS__) #endif #define _grub_min(a, b, _a, _b) \ From f6563e15bb490bb76a1a95cd3648fe03d1134d14 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 24 Feb 2022 16:32:51 -0500 Subject: [PATCH 249/367] modules: make .module_license read-only Currently .module_license is set writable (that is, the section has the SHF_WRITE flag set) in the module's ELF headers. This probably never actually matters, but it can't possibly be correct. This patch sets that data as "const", which causes that flag not to be set. Signed-off-by: Peter Jones --- include/grub/dl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/grub/dl.h b/include/grub/dl.h index 20d870f2a4..618ae6f474 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -121,7 +121,7 @@ grub_mod_fini (void) #define ATTRIBUTE_USED __unused__ #endif #define GRUB_MOD_LICENSE(license) \ - static char grub_module_license[] __attribute__ ((section (GRUB_MOD_SECTION (module_license)), ATTRIBUTE_USED)) = "LICENSE=" license; + static const char grub_module_license[] __attribute__ ((section (GRUB_MOD_SECTION (module_license)), ATTRIBUTE_USED)) = "LICENSE=" license; #define GRUB_MOD_DEP(name) \ static const char grub_module_depend_##name[] \ __attribute__((section(GRUB_MOD_SECTION(moddeps)), ATTRIBUTE_USED)) = #name From 0f66524e94d3c4f4d669d75c2122b0f1036776ea Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 24 Feb 2022 16:40:11 -0500 Subject: [PATCH 250/367] modules: strip .llvm_addrsig sections and similar. Currently grub modules built with clang or gcc have several sections which we don't actually need or support. We already have a list of section to skip in genmod.sh, and this patch adds the following sections to that list (as well as a few newlines): .note.gnu.property .llvm* Note that the glob there won't work without a new enough linker, but the failure is just reversion to the status quo, so that's not a big problem. Signed-off-by: Peter Jones --- grub-core/genmod.sh.in | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/grub-core/genmod.sh.in b/grub-core/genmod.sh.in index 1250589b3f..c2c5280d75 100644 --- a/grub-core/genmod.sh.in +++ b/grub-core/genmod.sh.in @@ -57,8 +57,11 @@ if test x@TARGET_APPLE_LINKER@ != x1; then @TARGET_STRIP@ --strip-unneeded \ -K grub_mod_init -K grub_mod_fini \ -K _grub_mod_init -K _grub_mod_fini \ - -R .note.gnu.gold-version -R .note.GNU-stack \ + -R .note.GNU-stack \ + -R .note.gnu.gold-version \ + -R .note.gnu.property \ -R .gnu.build.attributes \ + -R '.llvm*' \ -R .rel.gnu.build.attributes \ -R .rela.gnu.build.attributes \ -R .eh_frame -R .rela.eh_frame -R .rel.eh_frame \ From 0f76b53f2fe86542123c7aa1ae39c90852972a99 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 21 Mar 2022 16:56:10 -0400 Subject: [PATCH 251/367] modules: Don't allocate space for non-allocable sections. Currently when loading grub modules, we allocate space for all sections, including those without SHF_ALLOC set. We then copy the sections that /do/ have SHF_ALLOC set into the allocated memory, leaving some of our allocation untouched forever. Additionally, on platforms with GOT fixups and trampolines, we currently compute alignment round-ups for the sections and sections with sh_size = 0. This patch removes the extra space from the allocation computation, and makes the allocation computation loop skip empty sections as the loading loop does. Signed-off-by: Peter Jones --- grub-core/kern/dl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index f304494574..aef8af8aa7 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -289,6 +289,9 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) i < e->e_shnum; i++, s = (const Elf_Shdr *)((const char *) s + e->e_shentsize)) { + if (s->sh_size == 0 || !(s->sh_flags & SHF_ALLOC)) + continue; + tsize = ALIGN_UP (tsize, s->sh_addralign) + s->sh_size; if (talign < s->sh_addralign) talign = s->sh_addralign; From c850db5c0478c8328ebdd48ee8cce02995d4ead0 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 25 Mar 2022 15:40:12 -0400 Subject: [PATCH 252/367] pe: add the DOS header struct and fix some bad naming. In order to properly validate a loaded kernel's support for being loaded without a writable stack or executable, we need to be able to properly parse arbitrary PE headers. Currently, pe32.h is written in such a way that the MS-DOS header that tells us where to find the PE header in the binary can't be accessed. Further, for some reason it calls the DOS MZ magic "GRUB_PE32_MAGIC". This patch adds the structure for the DOS header, renames the DOS magic define, and adds defines for the actual PE magic. Signed-off-by: Peter Jones --- grub-core/loader/arm64/linux.c | 2 +- include/grub/efi/pe32.h | 28 ++++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c index d2af47c2c0..cc67f43906 100644 --- a/grub-core/loader/arm64/linux.c +++ b/grub-core/loader/arm64/linux.c @@ -58,7 +58,7 @@ grub_arch_efi_linux_check_image (struct linux_arch_kernel_header * lh) if (lh->magic != GRUB_LINUX_ARMXX_MAGIC_SIGNATURE) return grub_error(GRUB_ERR_BAD_OS, "invalid magic number"); - if ((lh->code0 & 0xffff) != GRUB_PE32_MAGIC) + if ((lh->code0 & 0xffff) != GRUB_DOS_MAGIC) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, N_("plain image kernel not supported - rebuild with CONFIG_(U)EFI_STUB enabled")); diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h index a43adf2746..2a5e1ee003 100644 --- a/include/grub/efi/pe32.h +++ b/include/grub/efi/pe32.h @@ -46,7 +46,30 @@ #define GRUB_PE32_MSDOS_STUB_SIZE 0x80 -#define GRUB_PE32_MAGIC 0x5a4d +#define GRUB_DOS_MAGIC 0x5a4d + +struct grub_dos_header +{ + grub_uint16_t magic; + grub_uint16_t cblp; + grub_uint16_t cp; + grub_uint16_t crlc; + grub_uint16_t cparhdr; + grub_uint16_t minalloc; + grub_uint16_t maxalloc; + grub_uint16_t ss; + grub_uint16_t sp; + grub_uint16_t csum; + grub_uint16_t ip; + grub_uint16_t cs; + grub_uint16_t lfarlc; + grub_uint16_t ovno; + grub_uint16_t res0[4]; + grub_uint16_t oemid; + grub_uint16_t oeminfo; + grub_uint16_t res1[10]; + grub_uint32_t lfanew; +}; /* According to the spec, the minimal alignment is 512 bytes... But some examples (such as EFI drivers in the Intel @@ -280,7 +303,8 @@ struct grub_pe32_section_table -#define GRUB_PE32_SIGNATURE_SIZE 4 +#define GRUB_PE32_SIGNATURE_SIZE 4 +#define GRUB_PE32_SIGNATURE "PE\0\0" struct grub_pe32_header { From 04c96d8ffe4d299227894a53d6ce5e9e52e36f47 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 9 Feb 2022 16:08:20 -0500 Subject: [PATCH 253/367] EFI: allocate kernel in EFI_RUNTIME_SERVICES_CODE instead of EFI_LOADER_DATA. On some of the firmwares with more security mitigations, EFI_LOADER_DATA doesn't get you executable memory, and we take a fault and reboot when we enter kernel. This patch correctly allocates the kernel code as EFI_RUNTIME_SERVICES_CODE rather than EFI_LOADER_DATA. Signed-off-by: Peter Jones [rharwood: use kernel_size] Signed-off-by: Robbie Harwood --- grub-core/loader/i386/efi/linux.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 9e5c11ac69..92b2fb5091 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -86,7 +86,9 @@ kernel_free(void *addr, grub_efi_uintn_t size) } static void * -kernel_alloc(grub_efi_uintn_t size, const char * const errmsg) +kernel_alloc(grub_efi_uintn_t size, + grub_efi_memory_type_t memtype, + const char * const errmsg) { void *addr = 0; unsigned int i; @@ -112,7 +114,7 @@ kernel_alloc(grub_efi_uintn_t size, const char * const errmsg) prev_max = max; addr = grub_efi_allocate_pages_real (max, pages, max_addresses[i].alloc_type, - GRUB_EFI_LOADER_DATA); + memtype); if (addr) grub_dprintf ("linux", "Allocated at %p\n", addr); } @@ -242,7 +244,8 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[]) } } - initrd_mem = kernel_alloc(size, N_("can't allocate initrd")); + initrd_mem = kernel_alloc(size, GRUB_EFI_RUNTIME_SERVICES_DATA, + N_("can't allocate initrd")); if (initrd_mem == NULL) goto fail; grub_dprintf ("linux", "initrd_mem = %p\n", initrd_mem); @@ -393,7 +396,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } #endif - params = kernel_alloc (sizeof(*params), "cannot allocate kernel parameters"); + params = kernel_alloc (sizeof(*params), GRUB_EFI_RUNTIME_SERVICES_DATA, + "cannot allocate kernel parameters"); if (!params) goto fail; grub_dprintf ("linux", "params = %p\n", params); @@ -415,7 +419,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_dprintf ("linux", "new lh is at %p\n", lh); grub_dprintf ("linux", "setting up cmdline\n"); - cmdline = kernel_alloc (lh->cmdline_size + 1, N_("can't allocate cmdline")); + cmdline = kernel_alloc (lh->cmdline_size + 1, + GRUB_EFI_RUNTIME_SERVICES_DATA, + N_("can't allocate cmdline")); if (!cmdline) goto fail; grub_dprintf ("linux", "cmdline = %p\n", cmdline); @@ -461,7 +467,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), max_addresses[1].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; max_addresses[2].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; kernel_size = lh->init_size; - kernel_mem = kernel_alloc (kernel_size, N_("can't allocate kernel")); + kernel_mem = kernel_alloc (kernel_size, GRUB_EFI_RUNTIME_SERVICES_CODE, + N_("can't allocate kernel")); restore_addresses(); if (!kernel_mem) goto fail; From 887f1d8fa976bb7ac4e283e896c3be9d70f8b150 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 21 Mar 2022 17:45:40 -0400 Subject: [PATCH 254/367] modules: load module sections at page-aligned addresses Currently we load module sections at whatever alignment gcc+ld happened to dump into the ELF section header, which is often pretty useless. For example, by default time.mod has these sections on a current x86_64 build: $ eu-readelf -a grub-core/time.mod |& grep ^Section -A13 Section Headers: [Nr] Name Type Addr Off Size ES Flags Lk Inf Al [ 0] NULL 0 00000000 00000000 0 0 0 0 [ 1] .text PROGBITS 0 00000040 0000015e 0 AX 0 0 1 [ 2] .rela.text RELA 0 00000458 000001e0 24 I 8 1 8 [ 3] .rodata.str1.1 PROGBITS 0 0000019e 000000a1 1 AMS 0 0 1 [ 4] .module_license PROGBITS 0 00000240 0000000f 0 A 0 0 8 [ 5] .data PROGBITS 0 0000024f 00000000 0 WA 0 0 1 [ 6] .bss NOBITS 0 00000250 00000008 0 WA 0 0 8 [ 7] .modname PROGBITS 0 00000250 00000005 0 0 0 1 [ 8] .symtab SYMTAB 0 00000258 00000150 24 9 6 8 [ 9] .strtab STRTAB 0 000003a8 000000ab 0 0 0 1 [10] .shstrtab STRTAB 0 00000638 00000059 0 0 0 1 With NX protections being page based, loading sections with either a 1 or 8 *byte* alignment does absolutely nothing to help us out. This patch switches most EFI platforms to load module sections at 4kB page-aligned addresses. To do so, it adds an new per-arch function, grub_arch_dl_min_alignment(), which returns the alignment needed for dynamically loaded sections (in bytes). Currently it sets it to 4096 when GRUB_MACHINE_EFI is true on x86_64, i386, arm, arm64, and emu, and 1-byte alignment on everything else. It then changes the allocation size computation and the loader code in grub_dl_load_segments() to align the locations and sizes up to these boundaries, and fills any added padding with zeros. All of this happens before relocations are applied, so the relocations factor that in with no change. As an aside, initially Daniel Kiper and I thought that it might be a better idea to split the modules up into top-level sections as .text.modules, .rodata.modules, .data.modules, etc., so that their page permissions would get set by the loader that's loading grub itself. This turns out to have two significant downsides: 1) either in mkimage or in grub_dl_relocate_symbols(), you wind up having to dynamically process the relocations to accommodate the moved module sections, and 2) you then need to change the permissions on the modules and change them back while relocating them in grub_dl_relocate_symbols(), which means that any loader that /does/ honor the section flags but does /not/ generally support NX with the memory attributes API will cause grub to fail. Signed-off-by: Peter Jones --- docs/grub-dev.texi | 6 +++--- grub-core/kern/arm/dl.c | 13 +++++++++++++ grub-core/kern/arm64/dl.c | 13 +++++++++++++ grub-core/kern/dl.c | 29 +++++++++++++++++++++-------- grub-core/kern/emu/full.c | 13 +++++++++++++ grub-core/kern/i386/dl.c | 13 +++++++++++++ grub-core/kern/ia64/dl.c | 9 +++++++++ grub-core/kern/mips/dl.c | 8 ++++++++ grub-core/kern/powerpc/dl.c | 9 +++++++++ grub-core/kern/riscv/dl.c | 13 +++++++++++++ grub-core/kern/sparc64/dl.c | 9 +++++++++ grub-core/kern/x86_64/dl.c | 13 +++++++++++++ include/grub/dl.h | 2 ++ 13 files changed, 139 insertions(+), 11 deletions(-) diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi index 19f708ee66..7b2455a8fe 100644 --- a/docs/grub-dev.texi +++ b/docs/grub-dev.texi @@ -755,9 +755,9 @@ declare startup asm file ($cpu_$platform_startup) as well as any other files (e.g. init.c and callwrap.S) (e.g. $cpu_$platform = kern/$cpu/$platform/init.c). At this stage you will also need to add dummy dl.c and cache.S with functions grub_err_t grub_arch_dl_check_header (void *ehdr), grub_err_t -grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) (dl.c) and -void grub_arch_sync_caches (void *address, grub_size_t len) (cache.S). They -won't be used for now. +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) (dl.c), grub_uint32_t +grub_arch_dl_min_alignment (void), and void grub_arch_sync_caches (void +*address, grub_size_t len) (cache.S). They won't be used for now. You will need to create directory include/$cpu/$platform and a file include/$cpu/types.h. The later folowing this template: diff --git a/grub-core/kern/arm/dl.c b/grub-core/kern/arm/dl.c index eab9d17ff2..9260737936 100644 --- a/grub-core/kern/arm/dl.c +++ b/grub-core/kern/arm/dl.c @@ -278,3 +278,16 @@ grub_arch_dl_check_header (void *ehdr) return GRUB_ERR_NONE; } + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ +#ifdef GRUB_MACHINE_EFI + return 4096; +#else + return 1; +#endif +} diff --git a/grub-core/kern/arm64/dl.c b/grub-core/kern/arm64/dl.c index 512e5a80b0..0d4a26857f 100644 --- a/grub-core/kern/arm64/dl.c +++ b/grub-core/kern/arm64/dl.c @@ -196,3 +196,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, return GRUB_ERR_NONE; } + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ +#ifdef GRUB_MACHINE_EFI + return 4096; +#else + return 1; +#endif +} diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index aef8af8aa7..8c7aacef39 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -277,7 +277,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) { unsigned i; const Elf_Shdr *s; - grub_size_t tsize = 0, talign = 1; + grub_size_t tsize = 0, talign = 1, arch_addralign = 1; #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) grub_size_t tramp; grub_size_t got; @@ -285,16 +285,24 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) #endif char *ptr; + arch_addralign = grub_arch_dl_min_alignment (); + for (i = 0, s = (const Elf_Shdr *)((const char *) e + e->e_shoff); i < e->e_shnum; i++, s = (const Elf_Shdr *)((const char *) s + e->e_shentsize)) { + grub_size_t sh_addralign; + grub_size_t sh_size; + if (s->sh_size == 0 || !(s->sh_flags & SHF_ALLOC)) continue; - tsize = ALIGN_UP (tsize, s->sh_addralign) + s->sh_size; - if (talign < s->sh_addralign) - talign = s->sh_addralign; + sh_addralign = ALIGN_UP(s->sh_addralign, arch_addralign); + sh_size = ALIGN_UP(s->sh_size, sh_addralign); + + tsize = ALIGN_UP (tsize, sh_addralign) + sh_size; + if (talign < sh_addralign) + talign = sh_addralign; } #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) @@ -323,6 +331,9 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) i < e->e_shnum; i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize)) { + grub_size_t sh_addralign = ALIGN_UP(s->sh_addralign, arch_addralign); + grub_size_t sh_size = ALIGN_UP(s->sh_size, sh_addralign); + if (s->sh_flags & SHF_ALLOC) { grub_dl_segment_t seg; @@ -335,17 +346,19 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) { void *addr; - ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, s->sh_addralign); + ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, sh_addralign); addr = ptr; - ptr += s->sh_size; + ptr += sh_size; switch (s->sh_type) { case SHT_PROGBITS: grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size); + grub_memset ((char *)addr + s->sh_size, 0, + sh_size - s->sh_size); break; case SHT_NOBITS: - grub_memset (addr, 0, s->sh_size); + grub_memset (addr, 0, sh_size); break; } @@ -354,7 +367,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) else seg->addr = 0; - seg->size = s->sh_size; + seg->size = sh_size; seg->section = i; seg->next = mod->segment; mod->segment = seg; diff --git a/grub-core/kern/emu/full.c b/grub-core/kern/emu/full.c index e8d63b1f5f..1de1c28eb0 100644 --- a/grub-core/kern/emu/full.c +++ b/grub-core/kern/emu/full.c @@ -67,3 +67,16 @@ grub_arch_dl_init_linker (void) } #endif + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ +#ifdef GRUB_MACHINE_EFI + return 4096; +#else + return 1; +#endif +} diff --git a/grub-core/kern/i386/dl.c b/grub-core/kern/i386/dl.c index 1346da5cc9..d6b4681fc9 100644 --- a/grub-core/kern/i386/dl.c +++ b/grub-core/kern/i386/dl.c @@ -79,3 +79,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, return GRUB_ERR_NONE; } + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ +#ifdef GRUB_MACHINE_EFI + return 4096; +#else + return 1; +#endif +} diff --git a/grub-core/kern/ia64/dl.c b/grub-core/kern/ia64/dl.c index db59300fea..92d82c5750 100644 --- a/grub-core/kern/ia64/dl.c +++ b/grub-core/kern/ia64/dl.c @@ -148,3 +148,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, } return GRUB_ERR_NONE; } + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ + return 1; +} diff --git a/grub-core/kern/mips/dl.c b/grub-core/kern/mips/dl.c index 5d7d299c74..6d83bd71e9 100644 --- a/grub-core/kern/mips/dl.c +++ b/grub-core/kern/mips/dl.c @@ -272,3 +272,11 @@ grub_arch_dl_init_linker (void) grub_dl_register_symbol ("_gp_disp", &_gp_disp_dummy, 0, 0); } +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ + return 1; +} diff --git a/grub-core/kern/powerpc/dl.c b/grub-core/kern/powerpc/dl.c index cdd61b305f..5d9ba2e158 100644 --- a/grub-core/kern/powerpc/dl.c +++ b/grub-core/kern/powerpc/dl.c @@ -167,3 +167,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, return GRUB_ERR_NONE; } + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ + return 1; +} diff --git a/grub-core/kern/riscv/dl.c b/grub-core/kern/riscv/dl.c index f26b12aaa4..aa18f9e990 100644 --- a/grub-core/kern/riscv/dl.c +++ b/grub-core/kern/riscv/dl.c @@ -343,3 +343,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, return GRUB_ERR_NONE; } + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ +#ifdef GRUB_MACHINE_EFI + return 4096; +#else + return 1; +#endif +} diff --git a/grub-core/kern/sparc64/dl.c b/grub-core/kern/sparc64/dl.c index f3d960186b..f054f08241 100644 --- a/grub-core/kern/sparc64/dl.c +++ b/grub-core/kern/sparc64/dl.c @@ -189,3 +189,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, return GRUB_ERR_NONE; } + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ + return 1; +} diff --git a/grub-core/kern/x86_64/dl.c b/grub-core/kern/x86_64/dl.c index e5a8bdcf4f..a105dc50ce 100644 --- a/grub-core/kern/x86_64/dl.c +++ b/grub-core/kern/x86_64/dl.c @@ -119,3 +119,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, return GRUB_ERR_NONE; } + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ +#ifdef GRUB_MACHINE_EFI + return 4096; +#else + return 1; +#endif +} diff --git a/include/grub/dl.h b/include/grub/dl.h index 618ae6f474..f36ed5cb17 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -280,6 +280,8 @@ grub_err_t grub_arch_dl_check_header (void *ehdr); grub_err_t grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, Elf_Shdr *s, grub_dl_segment_t seg); +grub_size_t +grub_arch_dl_min_alignment (void); #endif #if defined (_mips) From 45bfb1cc8316096a5ce1e58850ce5f8a6e0e100c Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 22 Mar 2022 10:56:21 -0400 Subject: [PATCH 255/367] nx: add memory attribute get/set API For NX, we need to set the page access permission attributes for write and execute permissions. This patch adds two new primitives, grub_set_mem_attrs() and grub_clear_mem_attrs(), and associated constant definitions, to be used for that purpose. For most platforms, it adds a dummy implementation that returns GRUB_ERR_NONE. On EFI platforms, it adds a common helper function, grub_efi_status_to_err(), which translates EFI error codes to grub error codes, adds headers for the EFI Memory Attribute Protocol (still pending standardization), and an implementation of the grub nx primitives using it. Signed-off-by: Peter Jones [rharwood: add pjones's none/nyi fixup] Signed-off-by: Robbie Harwood --- grub-core/kern/efi/efi.c | 36 +++++++++++ grub-core/kern/efi/mm.c | 131 +++++++++++++++++++++++++++++++++++++++ include/grub/efi/api.h | 25 ++++++++ include/grub/efi/efi.h | 2 + include/grub/mm.h | 32 ++++++++++ 5 files changed, 226 insertions(+) diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index 7fcca69c17..4ac2b2754e 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -1096,3 +1096,39 @@ grub_efi_compare_device_paths (const grub_efi_device_path_t *dp1, return 0; } + +grub_err_t +grub_efi_status_to_err (grub_efi_status_t status) +{ + grub_err_t err; + switch (status) + { + case GRUB_EFI_SUCCESS: + err = GRUB_ERR_NONE; + break; + case GRUB_EFI_INVALID_PARAMETER: + default: + err = GRUB_ERR_BAD_ARGUMENT; + break; + case GRUB_EFI_OUT_OF_RESOURCES: + err = GRUB_ERR_OUT_OF_MEMORY; + break; + case GRUB_EFI_DEVICE_ERROR: + err = GRUB_ERR_IO; + break; + case GRUB_EFI_WRITE_PROTECTED: + err = GRUB_ERR_WRITE_ERROR; + break; + case GRUB_EFI_SECURITY_VIOLATION: + err = GRUB_ERR_ACCESS_DENIED; + break; + case GRUB_EFI_NOT_FOUND: + err = GRUB_ERR_FILE_NOT_FOUND; + break; + case GRUB_EFI_ABORTED: + err = GRUB_ERR_WAIT; + break; + } + + return err; +} diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index e84961d078..2c33758ed7 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -738,3 +738,134 @@ grub_efi_get_ram_base(grub_addr_t *base_addr) return GRUB_ERR_NONE; } #endif + +static inline grub_uint64_t +grub_mem_attrs_to_uefi_mem_attrs (grub_uint64_t attrs) +{ + grub_uint64_t ret = GRUB_EFI_MEMORY_RP | + GRUB_EFI_MEMORY_RO | + GRUB_EFI_MEMORY_XP; + + if (attrs & GRUB_MEM_ATTR_R) + ret &= ~GRUB_EFI_MEMORY_RP; + + if (attrs & GRUB_MEM_ATTR_W) + ret &= ~GRUB_EFI_MEMORY_RO; + + if (attrs & GRUB_MEM_ATTR_X) + ret &= ~GRUB_EFI_MEMORY_XP; + + return ret; +} + +static inline grub_uint64_t +uefi_mem_attrs_to_grub_mem_attrs (grub_uint64_t attrs) +{ + grub_uint64_t ret = GRUB_MEM_ATTR_R | + GRUB_MEM_ATTR_W | + GRUB_MEM_ATTR_X; + + if (attrs & GRUB_EFI_MEMORY_RP) + ret &= ~GRUB_MEM_ATTR_R; + + if (attrs & GRUB_EFI_MEMORY_RO) + ret &= ~GRUB_MEM_ATTR_W; + + if (attrs & GRUB_EFI_MEMORY_XP) + ret &= ~GRUB_MEM_ATTR_X; + + return ret; +} + +grub_err_t +grub_get_mem_attrs (grub_addr_t addr, grub_size_t size, grub_uint64_t *attrs) +{ + grub_efi_memory_attribute_protocol_t *proto; + grub_efi_physical_address_t physaddr = addr; + grub_efi_guid_t protocol_guid = GRUB_EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; + grub_efi_status_t efi_status; + + proto = grub_efi_locate_protocol (&protocol_guid, 0); + if (!proto) + return GRUB_ERR_NOT_IMPLEMENTED_YET; + + if (physaddr & 0xfff || size & 0xfff || size == 0 || attrs == NULL) + { + grub_dprintf ("nx", "%s called on 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" and attrs %p\n", + __func__, physaddr, physaddr+size-1, attrs); + return 0; + } + + efi_status = efi_call_4(proto->get_memory_attributes, + proto, physaddr, size, attrs); + *attrs = uefi_mem_attrs_to_grub_mem_attrs (*attrs); + + return grub_efi_status_to_err (efi_status); +} + +grub_err_t +grub_update_mem_attrs (grub_addr_t addr, grub_size_t size, + grub_uint64_t set_attrs, grub_uint64_t clear_attrs) +{ + grub_efi_memory_attribute_protocol_t *proto; + grub_efi_physical_address_t physaddr = addr; + grub_efi_guid_t protocol_guid = GRUB_EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; + grub_efi_status_t efi_status = GRUB_EFI_SUCCESS; + grub_uint64_t before = 0, after = 0, uefi_set_attrs, uefi_clear_attrs; + grub_err_t err; + + proto = grub_efi_locate_protocol (&protocol_guid, 0); + if (!proto) + return GRUB_ERR_NONE; + + err = grub_get_mem_attrs (addr, size, &before); + if (err) + grub_dprintf ("nx", "grub_get_mem_attrs(0x%"PRIxGRUB_ADDR", %"PRIuGRUB_SIZE", %p) -> 0x%x\n", + addr, size, &before, err); + + if (physaddr & 0xfff || size & 0xfff || size == 0) + { + grub_dprintf ("nx", "%s called on 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" +%s%s%s -%s%s%s\n", + __func__, physaddr, physaddr + size - 1, + (set_attrs & GRUB_MEM_ATTR_R) ? "r" : "", + (set_attrs & GRUB_MEM_ATTR_W) ? "w" : "", + (set_attrs & GRUB_MEM_ATTR_X) ? "x" : "", + (clear_attrs & GRUB_MEM_ATTR_R) ? "r" : "", + (clear_attrs & GRUB_MEM_ATTR_W) ? "w" : "", + (clear_attrs & GRUB_MEM_ATTR_X) ? "x" : ""); + return 0; + } + + uefi_set_attrs = grub_mem_attrs_to_uefi_mem_attrs (set_attrs); + grub_dprintf ("nx", "translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, uefi_set_attrs); + uefi_clear_attrs = grub_mem_attrs_to_uefi_mem_attrs (clear_attrs); + grub_dprintf ("nx", "translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, uefi_clear_attrs); + if (uefi_set_attrs) + efi_status = efi_call_4(proto->set_memory_attributes, + proto, physaddr, size, uefi_set_attrs); + if (efi_status == GRUB_EFI_SUCCESS && uefi_clear_attrs) + efi_status = efi_call_4(proto->clear_memory_attributes, + proto, physaddr, size, uefi_clear_attrs); + + err = grub_get_mem_attrs (addr, size, &after); + if (err) + grub_dprintf ("nx", "grub_get_mem_attrs(0x%"PRIxGRUB_ADDR", %"PRIuGRUB_SIZE", %p) -> 0x%x\n", + addr, size, &after, err); + + grub_dprintf ("nx", "set +%s%s%s -%s%s%s on 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" before:%c%c%c after:%c%c%c\n", + (set_attrs & GRUB_MEM_ATTR_R) ? "r" : "", + (set_attrs & GRUB_MEM_ATTR_W) ? "w" : "", + (set_attrs & GRUB_MEM_ATTR_X) ? "x" : "", + (clear_attrs & GRUB_MEM_ATTR_R) ? "r" : "", + (clear_attrs & GRUB_MEM_ATTR_W) ? "w" : "", + (clear_attrs & GRUB_MEM_ATTR_X) ? "x" : "", + addr, addr + size - 1, + (before & GRUB_MEM_ATTR_R) ? 'r' : '-', + (before & GRUB_MEM_ATTR_W) ? 'w' : '-', + (before & GRUB_MEM_ATTR_X) ? 'x' : '-', + (after & GRUB_MEM_ATTR_R) ? 'r' : '-', + (after & GRUB_MEM_ATTR_W) ? 'w' : '-', + (after & GRUB_MEM_ATTR_X) ? 'x' : '-'); + + return grub_efi_status_to_err (efi_status); +} diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index f431f49973..464842ba37 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -363,6 +363,11 @@ { 0x89, 0x29, 0x48, 0xbc, 0xd9, 0x0a, 0xd3, 0x1a } \ } +#define GRUB_EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID \ + { 0xf4560cf6, 0x40ec, 0x4b4a, \ + { 0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89 } \ + } + struct grub_efi_sal_system_table { grub_uint32_t signature; @@ -2102,6 +2107,26 @@ struct grub_efi_ip6_config_manual_address { }; typedef struct grub_efi_ip6_config_manual_address grub_efi_ip6_config_manual_address_t; +struct grub_efi_memory_attribute_protocol +{ + grub_efi_status_t (*get_memory_attributes) ( + struct grub_efi_memory_attribute_protocol *this, + grub_efi_physical_address_t base_address, + grub_efi_uint64_t length, + grub_efi_uint64_t *attributes); + grub_efi_status_t (*set_memory_attributes) ( + struct grub_efi_memory_attribute_protocol *this, + grub_efi_physical_address_t base_address, + grub_efi_uint64_t length, + grub_efi_uint64_t attributes); + grub_efi_status_t (*clear_memory_attributes) ( + struct grub_efi_memory_attribute_protocol *this, + grub_efi_physical_address_t base_address, + grub_efi_uint64_t length, + grub_efi_uint64_t attributes); +}; +typedef struct grub_efi_memory_attribute_protocol grub_efi_memory_attribute_protocol_t; + #if (GRUB_TARGET_SIZEOF_VOID_P == 4) || defined (__ia64__) \ || defined (__aarch64__) || defined (__MINGW64__) || defined (__CYGWIN__) \ || defined(__riscv) diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index ec52083c49..34825c4adc 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -164,4 +164,6 @@ struct grub_net_card; grub_efi_handle_t grub_efinet_get_device_handle (struct grub_net_card *card); +grub_err_t EXPORT_FUNC(grub_efi_status_to_err) (grub_efi_status_t status); + #endif /* ! GRUB_EFI_EFI_HEADER */ diff --git a/include/grub/mm.h b/include/grub/mm.h index 9c38dd3ca5..d81623d226 100644 --- a/include/grub/mm.h +++ b/include/grub/mm.h @@ -22,6 +22,7 @@ #include #include +#include #include #ifndef NULL @@ -38,6 +39,37 @@ void *EXPORT_FUNC(grub_realloc) (void *ptr, grub_size_t size); void *EXPORT_FUNC(grub_memalign) (grub_size_t align, grub_size_t size); #endif +#define GRUB_MEM_ATTR_R 0x0000000000000004LLU +#define GRUB_MEM_ATTR_W 0x0000000000000002LLU +#define GRUB_MEM_ATTR_X 0x0000000000000001LLU + +#ifdef GRUB_MACHINE_EFI +grub_err_t EXPORT_FUNC(grub_get_mem_attrs) (grub_addr_t addr, + grub_size_t size, + grub_uint64_t *attrs); +grub_err_t EXPORT_FUNC(grub_update_mem_attrs) (grub_addr_t addr, + grub_size_t size, + grub_uint64_t set_attrs, + grub_uint64_t clear_attrs); +#else /* !GRUB_MACHINE_EFI */ +static inline grub_err_t +grub_get_mem_attrs (grub_addr_t addr __attribute__((__unused__)), + grub_size_t size __attribute__((__unused__)), + grub_uint64_t *attrs __attribute__((__unused__))) +{ + return GRUB_ERR_NONE; +} + +static inline grub_err_t +grub_update_mem_attrs (grub_addr_t addr __attribute__((__unused__)), + grub_size_t size __attribute__((__unused__)), + grub_uint64_t set_attrs __attribute__((__unused__)), + grub_uint64_t clear_attrs __attribute__((__unused__))) +{ + return GRUB_ERR_NONE; +} +#endif /* GRUB_MACHINE_EFI */ + void grub_mm_check_real (const char *file, int line); #define grub_mm_check() grub_mm_check_real (GRUB_FILE, __LINE__); From ad1b904d325b7edb651111577e065a20e6b77c74 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 21 Mar 2022 17:46:35 -0400 Subject: [PATCH 256/367] nx: set page permissions for loaded modules. For NX, we need to set write and executable permissions on the sections of grub modules when we load them. On sections with SHF_ALLOC set, which is typically everything except .modname and the symbol and string tables, this patch clears the Read Only flag on sections that have the ELF flag SHF_WRITE set, and clears the No eXecute flag on sections with SHF_EXECINSTR set. In all other cases it sets both flags. Signed-off-by: Peter Jones [rharwood: arm tgptr -> tgaddr] Signed-off-by: Robbie Harwood --- grub-core/kern/dl.c | 120 +++++++++++++++++++++++++++++++++----------- include/grub/dl.h | 44 ++++++++++++++++ 2 files changed, 134 insertions(+), 30 deletions(-) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index 8c7aacef39..d5de80186f 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -285,6 +285,8 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) #endif char *ptr; + grub_dprintf ("modules", "loading segments for \"%s\"\n", mod->name); + arch_addralign = grub_arch_dl_min_alignment (); for (i = 0, s = (const Elf_Shdr *)((const char *) e + e->e_shoff); @@ -384,6 +386,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) ptr += got; #endif + grub_dprintf ("modules", "done loading segments for \"%s\"\n", mod->name); return GRUB_ERR_NONE; } @@ -517,23 +520,6 @@ grub_dl_find_section (Elf_Ehdr *e, const char *name) return s; return NULL; } -static long -grub_dl_find_section_index (Elf_Ehdr *e, const char *name) -{ - Elf_Shdr *s; - const char *str; - unsigned i; - - s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize); - str = (char *) e + s->sh_offset; - - for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); - i < e->e_shnum; - i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) - if (grub_strcmp (str + s->sh_name, name) == 0) - return (long)i; - return -1; -} /* Me, Vladimir Serbinenko, hereby I add this module check as per new GNU module policy. Note that this license check is informative only. @@ -662,6 +648,7 @@ grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr) Elf_Shdr *s; unsigned i; + grub_dprintf ("modules", "relocating symbols for \"%s\"\n", mod->name); for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); i < e->e_shnum; i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) @@ -670,24 +657,95 @@ grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr) grub_dl_segment_t seg; grub_err_t err; - /* Find the target segment. */ - for (seg = mod->segment; seg; seg = seg->next) - if (seg->section == s->sh_info) - break; + seg = grub_dl_find_segment(mod, s->sh_info); + if (!seg) + continue; - if (seg) - { - if (!mod->symtab) - return grub_error (GRUB_ERR_BAD_MODULE, "relocation without symbol table"); + if (!mod->symtab) + return grub_error (GRUB_ERR_BAD_MODULE, "relocation without symbol table"); - err = grub_arch_dl_relocate_symbols (mod, ehdr, s, seg); - if (err) - return err; - } + err = grub_arch_dl_relocate_symbols (mod, ehdr, s, seg); + if (err) + return err; } + grub_dprintf ("modules", "done relocating symbols for \"%s\"\n", mod->name); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_dl_set_mem_attrs (grub_dl_t mod, void *ehdr) +{ + unsigned i; + const Elf_Shdr *s; + const Elf_Ehdr *e = ehdr; +#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) + grub_size_t arch_addralign = grub_arch_dl_min_alignment (); + grub_addr_t tgaddr; + grub_uint64_t tgsz; +#endif + + grub_dprintf ("modules", "updating memory attributes for \"%s\"\n", + mod->name); + for (i = 0, s = (const Elf_Shdr *)((const char *) e + e->e_shoff); + i < e->e_shnum; + i++, s = (const Elf_Shdr *)((const char *) s + e->e_shentsize)) + { + grub_dl_segment_t seg; + grub_uint64_t set_attrs = GRUB_MEM_ATTR_R; + grub_uint64_t clear_attrs = GRUB_MEM_ATTR_W|GRUB_MEM_ATTR_X; + + seg = grub_dl_find_segment(mod, i); + if (!seg) + continue; + + if (seg->size == 0 || !(s->sh_flags & SHF_ALLOC)) + continue; + + if (s->sh_flags & SHF_WRITE) + { + set_attrs |= GRUB_MEM_ATTR_W; + clear_attrs &= ~GRUB_MEM_ATTR_W; + } + + if (s->sh_flags & SHF_EXECINSTR) + { + set_attrs |= GRUB_MEM_ATTR_X; + clear_attrs &= ~GRUB_MEM_ATTR_X; + } + + grub_dprintf ("modules", "setting memory attrs for section \"%s\" to -%s%s%s+%s%s%s\n", + grub_dl_get_section_name(e, s), + (clear_attrs & GRUB_MEM_ATTR_R) ? "r" : "", + (clear_attrs & GRUB_MEM_ATTR_W) ? "w" : "", + (clear_attrs & GRUB_MEM_ATTR_X) ? "x" : "", + (set_attrs & GRUB_MEM_ATTR_R) ? "r" : "", + (set_attrs & GRUB_MEM_ATTR_W) ? "w" : "", + (set_attrs & GRUB_MEM_ATTR_X) ? "x" : ""); + grub_update_mem_attrs ((grub_addr_t)(seg->addr), seg->size, set_attrs, clear_attrs); + } + +#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) + tgaddr = grub_min((grub_addr_t)mod->tramp, (grub_addr_t)mod->got); + tgsz = grub_max((grub_addr_t)mod->trampptr, (grub_addr_t)mod->gotptr) - tgaddr; + + if (tgsz) + { + tgsz = ALIGN_UP(tgsz, arch_addralign); + + grub_dprintf ("modules", "updating attributes for GOT and trampolines\n", + mod->name); + grub_update_mem_attrs (tgaddr, tgsz, GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_X, + GRUB_MEM_ATTR_W); + } +#endif + + grub_dprintf ("modules", "done updating module memory attributes for \"%s\"\n", + mod->name); + return GRUB_ERR_NONE; } + static void grub_dl_print_gdb_info (grub_dl_t mod, Elf_Ehdr *e) { @@ -753,6 +811,7 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size) mod->ref_count = 1; grub_dprintf ("modules", "relocating to %p\n", mod); + /* Me, Vladimir Serbinenko, hereby I add this module check as per new GNU module policy. Note that this license check is informative only. Modules have to be licensed under GPLv3 or GPLv3+ (optionally @@ -766,7 +825,8 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size) || grub_dl_resolve_dependencies (mod, e) || grub_dl_load_segments (mod, e) || grub_dl_resolve_symbols (mod, e) - || grub_dl_relocate_symbols (mod, e)) + || grub_dl_relocate_symbols (mod, e) + || grub_dl_set_mem_attrs (mod, e)) { mod->fini = 0; grub_dl_unload (mod); diff --git a/include/grub/dl.h b/include/grub/dl.h index f36ed5cb17..45ac8e339f 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -27,6 +27,7 @@ #include #include #include +#include #endif /* @@ -268,6 +269,49 @@ grub_dl_is_persistent (grub_dl_t mod) return mod->persistent; } +static inline const char * +grub_dl_get_section_name (const Elf_Ehdr *e, const Elf_Shdr *s) +{ + Elf_Shdr *str_s; + const char *str; + + str_s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize); + str = (char *) e + str_s->sh_offset; + + return str + s->sh_name; +} + +static inline long +grub_dl_find_section_index (Elf_Ehdr *e, const char *name) +{ + Elf_Shdr *s; + const char *str; + unsigned i; + + s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize); + str = (char *) e + s->sh_offset; + + for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); + i < e->e_shnum; + i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) + if (grub_strcmp (str + s->sh_name, name) == 0) + return (long)i; + return -1; +} + +/* Return the segment for a section of index N */ +static inline grub_dl_segment_t +grub_dl_find_segment (grub_dl_t mod, unsigned n) +{ + grub_dl_segment_t seg; + + for (seg = mod->segment; seg; seg = seg->next) + if (seg->section == n) + return seg; + + return NULL; +} + #endif void * EXPORT_FUNC(grub_resolve_symbol) (const char *name); From a9d40561377418baa1fda22fdbe06e2b3e35cd94 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 22 Mar 2022 10:57:07 -0400 Subject: [PATCH 257/367] nx: set attrs in our kernel loaders For NX, our kernel loaders need to set write and execute page permissions on allocated pages and the stack. This patch adds those calls. Signed-off-by: Peter Jones [rharwood: fix stack_attrs undefined, fix aarch64 callsites] Signed-off-by: Robbie Harwood --- grub-core/kern/efi/mm.c | 77 ++++++++++++++ grub-core/loader/arm64/linux.c | 16 ++- grub-core/loader/arm64/xen_boot.c | 4 +- grub-core/loader/efi/chainloader.c | 11 ++ grub-core/loader/efi/linux.c | 164 ++++++++++++++++++++++++++++- grub-core/loader/i386/efi/linux.c | 26 ++++- grub-core/loader/i386/linux.c | 5 + include/grub/efi/efi.h | 6 +- include/grub/efi/linux.h | 16 ++- include/grub/efi/pe32.h | 2 + 10 files changed, 312 insertions(+), 15 deletions(-) diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index 2c33758ed7..e460b072e6 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -610,6 +610,81 @@ print_memory_map (grub_efi_memory_descriptor_t *memory_map, } #endif +grub_addr_t grub_stack_addr = (grub_addr_t)-1ll; +grub_size_t grub_stack_size = 0; + +static void +grub_nx_init (void) +{ + grub_uint64_t attrs, stack_attrs; + grub_err_t err; + grub_addr_t stack_current, stack_end; + const grub_uint64_t page_size = 4096; + const grub_uint64_t page_mask = ~(page_size - 1); + + /* + * These are to confirm that the flags are working as expected when + * debugging. + */ + attrs = 0; + stack_current = (grub_addr_t)grub_nx_init & page_mask; + err = grub_get_mem_attrs (stack_current, page_size, &attrs); + if (err) + { + grub_dprintf ("nx", + "grub_get_mem_attrs(0x%"PRIxGRUB_UINT64_T", ...) -> 0x%x\n", + stack_current, err); + grub_error_pop (); + } + else + grub_dprintf ("nx", "page attrs for grub_nx_init (%p) are %c%c%c\n", + grub_dl_load_core, + (attrs & GRUB_MEM_ATTR_R) ? 'r' : '-', + (attrs & GRUB_MEM_ATTR_R) ? 'w' : '-', + (attrs & GRUB_MEM_ATTR_R) ? 'x' : '-'); + + stack_current = (grub_addr_t)&stack_current & page_mask; + err = grub_get_mem_attrs (stack_current, page_size, &stack_attrs); + if (err) + { + grub_dprintf ("nx", + "grub_get_mem_attrs(0x%"PRIxGRUB_UINT64_T", ...) -> 0x%x\n", + stack_current, err); + grub_error_pop (); + } + else + { + attrs = stack_attrs; + grub_dprintf ("nx", "page attrs for stack (%p) are %c%c%c\n", + &attrs, + (attrs & GRUB_MEM_ATTR_R) ? 'r' : '-', + (attrs & GRUB_MEM_ATTR_R) ? 'w' : '-', + (attrs & GRUB_MEM_ATTR_R) ? 'x' : '-'); + } + for (stack_end = stack_current + page_size ; + !(attrs & GRUB_MEM_ATTR_R); + stack_end += page_size) + { + err = grub_get_mem_attrs (stack_current, page_size, &attrs); + if (err) + { + grub_dprintf ("nx", + "grub_get_mem_attrs(0x%"PRIxGRUB_UINT64_T", ...) -> 0x%x\n", + stack_current, err); + grub_error_pop (); + break; + } + } + if (stack_end > stack_current) + { + grub_stack_addr = stack_current; + grub_stack_size = stack_end - stack_current; + grub_dprintf ("nx", + "detected stack from 0x%"PRIxGRUB_ADDR" to 0x%"PRIxGRUB_ADDR"\n", + grub_stack_addr, grub_stack_addr + grub_stack_size - 1); + } +} + void grub_efi_mm_init (void) { @@ -623,6 +698,8 @@ grub_efi_mm_init (void) grub_efi_uint64_t required_pages; int mm_status; + grub_nx_init (); + /* Prepare a memory region to store two memory maps. */ memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE)); if (! memory_map) diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c index cc67f43906..de85583487 100644 --- a/grub-core/loader/arm64/linux.c +++ b/grub-core/loader/arm64/linux.c @@ -172,7 +172,8 @@ free_params (void) } grub_err_t -grub_arch_efi_linux_boot_image (grub_addr_t addr, char *args) +grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args, + int nx_supported) { grub_err_t retval; @@ -182,7 +183,8 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, char *args) grub_dprintf ("linux", "linux command line: '%s'\n", args); - retval = grub_efi_linux_boot ((char *)addr, handover_offset, (void *)addr); + retval = grub_efi_linux_boot (addr, size, handover_offset, + (void *)addr, nx_supported); /* Never reached... */ free_params(); @@ -192,7 +194,10 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, char *args) static grub_err_t grub_linux_boot (void) { - return (grub_arch_efi_linux_boot_image((grub_addr_t)kernel_addr, linux_args)); + return grub_arch_efi_linux_boot_image((grub_addr_t)kernel_addr, + (grub_size_t)kernel_size, + linux_args, + 0); } static grub_err_t @@ -340,6 +345,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_off_t filelen; grub_uint32_t align; void *kernel = NULL; + int nx_supported = 1; grub_dl_ref (my_mod); @@ -376,6 +382,10 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_dprintf ("linux", "kernel entry offset : %d\n", handover_offset); grub_dprintf ("linux", "kernel alignment : 0x%x\n", align); + err = grub_efi_check_nx_image_support((grub_addr_t)kernel, filelen, &nx_supported); + if (err != GRUB_ERR_NONE) + goto fail; + grub_loader_unset(); kernel_alloc_pages = GRUB_EFI_BYTES_TO_PAGES (kernel_size + align - 1); diff --git a/grub-core/loader/arm64/xen_boot.c b/grub-core/loader/arm64/xen_boot.c index d9b7a9ba40..6e7e920416 100644 --- a/grub-core/loader/arm64/xen_boot.c +++ b/grub-core/loader/arm64/xen_boot.c @@ -266,7 +266,9 @@ xen_boot (void) return err; return grub_arch_efi_linux_boot_image (xen_hypervisor->start, - xen_hypervisor->cmdline); + xen_hypervisor->size, + xen_hypervisor->cmdline, + 0); } static void diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index fb874f1855..dd31ac9bb3 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -1070,6 +1070,17 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), goto fail; } + /* + * The OS kernel is going to set its own permissions when it takes over + * paging a few million instructions from now, and load_image() will set up + * anything that's needed based on the section headers, so there's no point + * in doing anything but clearing the protection bits here. + */ + grub_dprintf("nx", "setting attributes for %p (%lu bytes) to %llx\n", + (void *)(grub_addr_t)address, fsize, 0llu); + grub_update_mem_attrs (address, fsize, + GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_W|GRUB_MEM_ATTR_X, 0); + #if defined (__i386__) || defined (__x86_64__) if (fsize >= (grub_ssize_t) sizeof (struct grub_macho_fat_header)) { diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c index 9265cf4200..277f352e0c 100644 --- a/grub-core/loader/efi/linux.c +++ b/grub-core/loader/efi/linux.c @@ -26,16 +26,127 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" +#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" + +grub_err_t +grub_efi_check_nx_image_support (grub_addr_t kernel_addr, + grub_size_t kernel_size, + int *nx_supported) +{ + struct grub_dos_header *doshdr; + grub_size_t sz = sizeof (*doshdr); + + struct grub_pe32_header_32 *pe32; + struct grub_pe32_header_64 *pe64; + + int image_is_compatible = 0; + int is_64_bit; + + if (kernel_size < sz) + return grub_error (GRUB_ERR_BAD_OS, N_("kernel is too small")); + + doshdr = (void *)kernel_addr; + + if ((doshdr->magic & 0xffff) != GRUB_DOS_MAGIC) + return grub_error (GRUB_ERR_BAD_OS, N_("kernel DOS magic is invalid")); + + sz = doshdr->lfanew + sizeof (*pe32); + if (kernel_size < sz) + return grub_error (GRUB_ERR_BAD_OS, N_("kernel is too small")); + + pe32 = (struct grub_pe32_header_32 *)(kernel_addr + doshdr->lfanew); + pe64 = (struct grub_pe32_header_64 *)pe32; + + if (grub_memcmp (pe32->signature, GRUB_PE32_SIGNATURE, + GRUB_PE32_SIGNATURE_SIZE) != 0) + return grub_error (GRUB_ERR_BAD_OS, N_("kernel PE magic is invalid")); + + switch (pe32->coff_header.machine) + { + case GRUB_PE32_MACHINE_ARMTHUMB_MIXED: + case GRUB_PE32_MACHINE_I386: + case GRUB_PE32_MACHINE_RISCV32: + is_64_bit = 0; + break; + case GRUB_PE32_MACHINE_ARM64: + case GRUB_PE32_MACHINE_IA64: + case GRUB_PE32_MACHINE_RISCV64: + case GRUB_PE32_MACHINE_X86_64: + is_64_bit = 1; + break; + default: + return grub_error (GRUB_ERR_BAD_OS, N_("PE machine type 0x%04hx unknown"), + pe32->coff_header.machine); + } + + if (is_64_bit) + { + sz = doshdr->lfanew + sizeof (*pe64); + if (kernel_size < sz) + return grub_error (GRUB_ERR_BAD_OS, N_("kernel is too small")); + + if (pe64->optional_header.dll_characteristics & GRUB_PE32_NX_COMPAT) + image_is_compatible = 1; + } + else + { + if (pe32->optional_header.dll_characteristics & GRUB_PE32_NX_COMPAT) + image_is_compatible = 1; + } + + *nx_supported = image_is_compatible; + return GRUB_ERR_NONE; +} + +grub_err_t +grub_efi_check_nx_required (int *nx_required) +{ + grub_efi_status_t status; + grub_efi_guid_t guid = GRUB_EFI_SHIM_LOCK_GUID; + grub_size_t mok_policy_sz = 0; + char *mok_policy = NULL; + grub_uint32_t mok_policy_attrs = 0; + + status = grub_efi_get_variable_with_attributes ("MokPolicy", &guid, + &mok_policy_sz, + (void **)&mok_policy, + &mok_policy_attrs); + if (status == GRUB_EFI_NOT_FOUND || + mok_policy_sz == 0 || + mok_policy == NULL) + { + *nx_required = 0; + return GRUB_ERR_NONE; + } + + *nx_required = 0; + if (mok_policy_sz < 1 || + mok_policy_attrs != (GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS | + GRUB_EFI_VARIABLE_RUNTIME_ACCESS) || + (mok_policy[mok_policy_sz-1] & GRUB_MOK_POLICY_NX_REQUIRED)) + *nx_required = 1; + + return GRUB_ERR_NONE; +} typedef void (*handover_func) (void *, grub_efi_system_table_t *, void *); grub_err_t -grub_efi_linux_boot (void *kernel_addr, grub_off_t handover_offset, - void *kernel_params) +grub_efi_linux_boot (grub_addr_t kernel_addr, grub_size_t kernel_size, + grub_off_t handover_offset, void *kernel_params, + int nx_supported) { grub_efi_loaded_image_t *loaded_image = NULL; handover_func hf; int offset = 0; + grub_uint64_t stack_set_attrs = GRUB_MEM_ATTR_R | + GRUB_MEM_ATTR_W | + GRUB_MEM_ATTR_X; + grub_uint64_t stack_clear_attrs = 0; + grub_uint64_t kernel_set_attrs = stack_set_attrs; + grub_uint64_t kernel_clear_attrs = stack_clear_attrs; + grub_uint64_t attrs; + int nx_required = 0; #ifdef __x86_64__ offset = 512; @@ -48,12 +159,57 @@ grub_efi_linux_boot (void *kernel_addr, grub_off_t handover_offset, */ loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle); if (loaded_image) - loaded_image->image_base = kernel_addr; + loaded_image->image_base = (void *)kernel_addr; else grub_dprintf ("linux", "Loaded Image base address could not be set\n"); grub_dprintf ("linux", "kernel_addr: %p handover_offset: %p params: %p\n", - kernel_addr, (void *)(grub_efi_uintn_t)handover_offset, kernel_params); + (void *)kernel_addr, (void *)handover_offset, kernel_params); + + + if (nx_required && !nx_supported) + return grub_error (GRUB_ERR_BAD_OS, N_("kernel does not support NX loading required by policy")); + + if (nx_supported) + { + kernel_set_attrs &= ~GRUB_MEM_ATTR_W; + kernel_clear_attrs |= GRUB_MEM_ATTR_W; + stack_set_attrs &= ~GRUB_MEM_ATTR_X; + stack_clear_attrs |= GRUB_MEM_ATTR_X; + } + + grub_dprintf ("nx", "Setting attributes for 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" to r%cx\n", + kernel_addr, kernel_addr + kernel_size - 1, + (kernel_set_attrs & GRUB_MEM_ATTR_W) ? 'w' : '-'); + grub_update_mem_attrs (kernel_addr, kernel_size, + kernel_set_attrs, kernel_clear_attrs); + + grub_get_mem_attrs (kernel_addr, 4096, &attrs); + grub_dprintf ("nx", "permissions for 0x%"PRIxGRUB_ADDR" are %s%s%s\n", + (grub_addr_t)kernel_addr, + (attrs & GRUB_MEM_ATTR_R) ? "r" : "-", + (attrs & GRUB_MEM_ATTR_W) ? "w" : "-", + (attrs & GRUB_MEM_ATTR_X) ? "x" : "-"); + if (grub_stack_addr != (grub_addr_t)-1ll) + { + grub_dprintf ("nx", "Setting attributes for stack at 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" to rw%c\n", + grub_stack_addr, grub_stack_addr + grub_stack_size - 1, + (stack_set_attrs & GRUB_MEM_ATTR_X) ? 'x' : '-'); + grub_update_mem_attrs (grub_stack_addr, grub_stack_size, + stack_set_attrs, stack_clear_attrs); + + grub_get_mem_attrs (grub_stack_addr, 4096, &attrs); + grub_dprintf ("nx", "permissions for 0x%"PRIxGRUB_ADDR" are %s%s%s\n", + grub_stack_addr, + (attrs & GRUB_MEM_ATTR_R) ? "r" : "-", + (attrs & GRUB_MEM_ATTR_W) ? "w" : "-", + (attrs & GRUB_MEM_ATTR_X) ? "x" : "-"); + } + +#if defined(__i386__) || defined(__x86_64__) + asm volatile ("cli"); +#endif + hf = (handover_func)((char *)kernel_addr + handover_offset + offset); hf (grub_efi_image_handle, grub_efi_system_table, kernel_params); diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 92b2fb5091..91ae274299 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -44,7 +44,7 @@ struct grub_linuxefi_context { grub_uint32_t handover_offset; struct linux_kernel_params *params; char *cmdline; - + int nx_supported; void *initrd_mem; }; @@ -110,13 +110,19 @@ kernel_alloc(grub_efi_uintn_t size, pages = BYTES_TO_PAGES(size); grub_dprintf ("linux", "Trying to allocate %lu pages from %p\n", (unsigned long)pages, (void *)(unsigned long)max); + size = pages * GRUB_EFI_PAGE_SIZE; prev_max = max; addr = grub_efi_allocate_pages_real (max, pages, max_addresses[i].alloc_type, memtype); if (addr) - grub_dprintf ("linux", "Allocated at %p\n", addr); + { + grub_dprintf ("linux", "Allocated at %p\n", addr); + grub_update_mem_attrs ((grub_addr_t)addr, size, + GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_W, + GRUB_MEM_ATTR_X); + } } while (grub_error_pop ()) @@ -137,9 +143,11 @@ grub_linuxefi_boot (void *data) asm volatile ("cli"); - return grub_efi_linux_boot ((char *)context->kernel_mem, + return grub_efi_linux_boot ((grub_addr_t)context->kernel_mem, + context->kernel_size, context->handover_offset, - context->params); + context->params, + context->nx_supported); } static grub_err_t @@ -304,7 +312,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_uint32_t handover_offset; struct linux_kernel_params *params = 0; char *cmdline = 0; + int nx_supported = 1; struct grub_linuxefi_context *context = 0; + grub_err_t err; grub_dl_ref (my_mod); @@ -334,6 +344,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } + err = grub_efi_check_nx_image_support ((grub_addr_t)kernel, filelen, + &nx_supported); + if (err != GRUB_ERR_NONE) + return err; + grub_dprintf ("linux", "nx is%s supported by this kernel\n", + nx_supported ? "" : " not"); + lh = (struct linux_i386_kernel_header *)kernel; grub_dprintf ("linux", "original lh is at %p\n", kernel); @@ -498,6 +515,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), context->handover_offset = handover_offset; context->params = params; context->cmdline = cmdline; + context->nx_supported = nx_supported; grub_loader_set_ex (grub_linuxefi_boot, grub_linuxefi_unload, context, 0); diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 4aeb0e4b9a..3c1ff64763 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -805,6 +805,11 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), kernel_offset += len; } + grub_dprintf("efi", "setting attributes for %p (%zu bytes) to +rw-x\n", + &linux_params, sizeof (lh) + len); + grub_update_mem_attrs ((grub_addr_t)&linux_params, sizeof (lh) + len, + GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_W, GRUB_MEM_ATTR_X); + linux_params.code32_start = prot_mode_target + lh.code32_start - GRUB_LINUX_BZIMAGE_ADDR; linux_params.kernel_alignment = (1 << align); linux_params.ps_mouse = linux_params.padding11 = 0; diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index 34825c4adc..449e55269f 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -140,12 +140,16 @@ extern void (*EXPORT_VAR(grub_efi_net_config)) (grub_efi_handle_t hnd, char **device, char **path); +extern grub_addr_t EXPORT_VAR(grub_stack_addr); +extern grub_size_t EXPORT_VAR(grub_stack_size); + #if defined(__arm__) || defined(__aarch64__) || defined(__riscv) void *EXPORT_FUNC(grub_efi_get_firmware_fdt)(void); grub_err_t EXPORT_FUNC(grub_efi_get_ram_base)(grub_addr_t *); #include grub_err_t grub_arch_efi_linux_check_image(struct linux_arch_kernel_header *lh); -grub_err_t grub_arch_efi_linux_boot_image(grub_addr_t addr, char *args); +grub_err_t grub_arch_efi_linux_boot_image(grub_addr_t addr, grub_size_t size, + char *args, int nx_enabled); #endif grub_addr_t grub_efi_section_addr (const char *section); diff --git a/include/grub/efi/linux.h b/include/grub/efi/linux.h index 887b02fd9f..b82f71006a 100644 --- a/include/grub/efi/linux.h +++ b/include/grub/efi/linux.h @@ -22,8 +22,20 @@ #include #include +#define GRUB_MOK_POLICY_NX_REQUIRED 0x1 + +grub_err_t +EXPORT_FUNC(grub_efi_linux_boot) (grub_addr_t kernel_address, + grub_size_t kernel_size, + grub_off_t handover_offset, + void *kernel_param, int nx_enabled); + +grub_err_t +EXPORT_FUNC(grub_efi_check_nx_image_support) (grub_addr_t kernel_addr, + grub_size_t kernel_size, + int *nx_supported); + grub_err_t -EXPORT_FUNC(grub_efi_linux_boot) (void *kernel_address, grub_off_t offset, - void *kernel_param); +EXPORT_FUNC(grub_efi_check_nx_required) (int *nx_required); #endif /* ! GRUB_EFI_LINUX_HEADER */ diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h index 2a5e1ee003..a5e623eb04 100644 --- a/include/grub/efi/pe32.h +++ b/include/grub/efi/pe32.h @@ -181,6 +181,8 @@ struct grub_pe32_optional_header struct grub_pe32_data_directory reserved_entry; }; +#define GRUB_PE32_NX_COMPAT 0x0100 + struct grub_pe64_optional_header { grub_uint16_t magic; From a9ec858bd62b004c331cad9b5b00071d3081b626 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 22 Mar 2022 10:57:20 -0400 Subject: [PATCH 258/367] nx: set the nx compatible flag in EFI grub images For NX, we need the grub binary to announce that it is compatible with the NX feature. This implies that when loading the executable grub image, several attributes are true: - the binary doesn't need an executable stack - the binary doesn't need sections to be both executable and writable - the binary knows how to use the EFI Memory Attributes protocol on code it is loading. This patch adds a definition for the PE DLL Characteristics flag GRUB_PE32_NX_COMPAT, and changes grub-mkimage to set that flag. Signed-off-by: Peter Jones --- util/mkimage.c | 1 + 1 file changed, 1 insertion(+) diff --git a/util/mkimage.c b/util/mkimage.c index 8319e8dfbd..c3d33aaac8 100644 --- a/util/mkimage.c +++ b/util/mkimage.c @@ -1418,6 +1418,7 @@ grub_install_generate_image (const char *dir, const char *prefix, section = (struct grub_pe32_section_table *)(o64 + 1); } + PE_OHDR (o32, o64, dll_characteristics) = grub_host_to_target16 (GRUB_PE32_NX_COMPAT); PE_OHDR (o32, o64, header_size) = grub_host_to_target32 (header_size); PE_OHDR (o32, o64, entry_addr) = grub_host_to_target32 (layout.start_address); PE_OHDR (o32, o64, image_base) = 0; From 5898517fd3c2fcc3e67b1c5c3a919479c7e002d1 Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Fri, 15 Jul 2022 15:49:25 -0400 Subject: [PATCH 259/367] grub-probe: document the behavior of multiple -v Signed-off-by: Robbie Harwood (cherry picked from commit 51a55233eed08f7f12276afd6b3724b807a0b680) --- util/grub-probe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/grub-probe.c b/util/grub-probe.c index c6fac732b4..ba867319a7 100644 --- a/util/grub-probe.c +++ b/util/grub-probe.c @@ -732,7 +732,8 @@ static struct argp_option options[] = { {"device-map", 'm', N_("FILE"), 0, N_("use FILE as the device map [default=%s]"), 0}, {"target", 't', N_("TARGET"), 0, 0, 0}, - {"verbose", 'v', 0, 0, N_("print verbose messages."), 0}, + {"verbose", 'v', 0, 0, + N_("print verbose messages (pass twice to enable debug printing)."), 0}, {0, '0', 0, 0, N_("separate items in output using ASCII NUL characters"), 0}, { 0, 0, 0, 0, 0, 0 } }; From e452c077fd703a2545628c0e4f2bf4e8bcaf5c19 Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Fri, 15 Jul 2022 15:39:41 -0400 Subject: [PATCH 260/367] grub_fs_probe(): dprint errors from filesystems When filesystem detection fails, all that's currently debug-logged is a series of messages like: grub-core/kern/fs.c:56:fs: Detecting ntfs... grub-core/kern/fs.c:76:fs: ntfs detection failed. repeated for each filesystem. Any messages provided to grub_error() by the filesystem are lost, and one has to break out gdb to figure out what went wrong. With this change, one instead sees: grub-core/kern/fs.c:56:fs: Detecting fat... grub-core/osdep/hostdisk.c:357:hostdisk: reusing open device `/path/to/device' grub-core/kern/fs.c:77:fs: error: invalid modification timestamp for /. grub-core/kern/fs.c:79:fs: fat detection failed. in the debug prints. Signed-off-by: Robbie Harwood (cherry picked from commit 838c79d658797d0662ee7f9e033e38ee88059e02) --- grub-core/kern/fs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/grub-core/kern/fs.c b/grub-core/kern/fs.c index c698295bcb..b58e2ae1d2 100644 --- a/grub-core/kern/fs.c +++ b/grub-core/kern/fs.c @@ -74,6 +74,7 @@ grub_fs_probe (grub_device_t device) if (grub_errno == GRUB_ERR_NONE) return p; + grub_dprintf ("fs", _("error: %s.\n"), grub_errmsg); grub_error_push (); grub_dprintf ("fs", "%s detection failed.\n", p->name); grub_error_pop (); From 8a1dcbfaf6ca064bc4976ffd5ec85532d0111d0a Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Fri, 15 Jul 2022 15:42:41 -0400 Subject: [PATCH 261/367] fs/fat: don't error when mtime is 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the wild, we occasionally see valid ESPs where some file modification times are 0. For instance: ├── [Dec 31 1979] EFI │ ├── [Dec 31 1979] BOOT │ │ ├── [Dec 31 1979] BOOTX64.EFI │ │ └── [Dec 31 1979] fbx64.efi │ └── [Jun 27 02:41] fedora │ ├── [Dec 31 1979] BOOTX64.CSV │ ├── [Dec 31 1979] fonts │ ├── [Mar 14 03:35] fw │ │ ├── [Mar 14 03:35] fwupd-359c1169-abd6-4a0d-8bce-e4d4713335c1.cap │ │ ├── [Mar 14 03:34] fwupd-9d255c4b-2d88-4861-860d-7ee52ade9463.cap │ │ └── [Mar 14 03:34] fwupd-b36438d8-9128-49d2-b280-487be02d948b.cap │ ├── [Dec 31 1979] fwupdx64.efi │ ├── [May 10 10:47] grub.cfg │ ├── [Jun 3 12:38] grub.cfg.new.new │ ├── [May 10 10:41] grub.cfg.old │ ├── [Jun 27 02:41] grubenv │ ├── [Dec 31 1979] grubx64.efi │ ├── [Dec 31 1979] mmx64.efi │ ├── [Dec 31 1979] shim.efi │ ├── [Dec 31 1979] shimx64.efi │ └── [Dec 31 1979] shimx64-fedora.efi └── [Dec 31 1979] FSCK0000.REC 5 directories, 17 files This causes grub-probe failure, which in turn causes grub-mkconfig failure. They are valid filesystems that appear intact, and the Linux FAT stack is able to mount and manipulate them without complaint. The check for mtime of 0 has been present since 20def1a3c3952982395cd7c3ea7e78638527962b ("fat: support file modification times"). Signed-off-by: Robbie Harwood (cherry picked from commit 0615c4887352e32d7bb7198e9ad0d695f9dc2c31) --- grub-core/fs/fat.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/grub-core/fs/fat.c b/grub-core/fs/fat.c index dd82e4ee35..ff6200c5be 100644 --- a/grub-core/fs/fat.c +++ b/grub-core/fs/fat.c @@ -1027,9 +1027,6 @@ grub_fat_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook, grub_le_to_cpu16 (ctxt.dir.w_date), &info.mtime); #endif - if (info.mtimeset == 0) - grub_error (GRUB_ERR_OUT_OF_RANGE, - "invalid modification timestamp for %s", path); if (hook (ctxt.filename, &info, hook_data)) break; From ca59646cdbebce79f71fae83ef5bae938f741042 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 29 Jul 2022 15:56:00 -0400 Subject: [PATCH 262/367] Make debug=file show which file filters get run. If one of the file filters breaks things, it's hard to figure out where it has happened. This makes grub log which filter is being run, which makes it easier to figure out where you are in the sequence of events. Signed-off-by: Peter Jones --- grub-core/kern/file.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c index db938e099d..868ce3b63e 100644 --- a/grub-core/kern/file.c +++ b/grub-core/kern/file.c @@ -30,6 +30,14 @@ void (*EXPORT_VAR (grub_grubnet_fini)) (void); grub_file_filter_t grub_file_filters[GRUB_FILE_FILTER_MAX]; +static const char *filter_names[] = { + [GRUB_FILE_FILTER_VERIFY] = "GRUB_FILE_FILTER_VERIFY", + [GRUB_FILE_FILTER_GZIO] = "GRUB_FILE_FILTER_GZIO", + [GRUB_FILE_FILTER_XZIO] = "GRUB_FILE_FILTER_XZIO", + [GRUB_FILE_FILTER_LZOPIO] = "GRUB_FILE_FILTER_LZOPIO", + [GRUB_FILE_FILTER_MAX] = "GRUB_FILE_FILTER_MAX" +}; + /* Get the device part of the filename NAME. It is enclosed by parentheses. */ char * grub_file_get_device_name (const char *name) @@ -124,6 +132,9 @@ grub_file_open (const char *name, enum grub_file_type type) if (grub_file_filters[filter]) { last_file = file; + if (filter < GRUB_FILE_FILTER_MAX) + grub_dprintf ("file", "Running %s file filter\n", + filter_names[filter]); file = grub_file_filters[filter] (file, type); if (file && file != last_file) { From c88cadb301083b4f3637031a9399e9c93f687914 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 1 Aug 2022 14:06:30 -0400 Subject: [PATCH 263/367] efi: use enumerated array positions for our allocation choices In our kernel allocator on EFI systems, we currently have a growing amount of code that references the various allocation policies by position in the array, and of course maintenance of this code scales very poorly. This patch changes them to be enumerated, so they're easier to refer to farther along in the code without confusion. Signed-off-by: Peter Jones --- grub-core/loader/i386/efi/linux.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 91ae274299..8daa070132 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -60,17 +60,26 @@ struct allocation_choice { grub_efi_allocate_type_t alloc_type; }; -static struct allocation_choice max_addresses[4] = +enum { + KERNEL_PREF_ADDRESS, + KERNEL_4G_LIMIT, + KERNEL_NO_LIMIT, +}; + +static struct allocation_choice max_addresses[] = { /* the kernel overrides this one with pref_address and * GRUB_EFI_ALLOCATE_ADDRESS */ - { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, + [KERNEL_PREF_ADDRESS] = + { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, + /* If the flag in params is set, this one gets changed to be above 4GB. */ + [KERNEL_4G_LIMIT] = + { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, /* this one is always below 4GB, which we still *prefer* even if the flag * is set. */ - { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, - /* If the flag in params is set, this one gets changed to be above 4GB. */ - { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, - { 0, 0 } + [KERNEL_NO_LIMIT] = + { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, + { NO_MEM, 0, 0 } }; static struct allocation_choice saved_addresses[4]; @@ -405,7 +414,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), if (lh->xloadflags & LINUX_XLF_CAN_BE_LOADED_ABOVE_4G) { grub_dprintf ("linux", "Loading kernel above 4GB is supported; enabling.\n"); - max_addresses[2].addr = GRUB_EFI_MAX_USABLE_ADDRESS; + max_addresses[KERNEL_NO_LIMIT].addr = GRUB_EFI_MAX_USABLE_ADDRESS; } else { @@ -478,11 +487,11 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_dprintf ("linux", "lh->pref_address: %p\n", (void *)(grub_addr_t)lh->pref_address); if (lh->pref_address < (grub_uint64_t)GRUB_EFI_MAX_ALLOCATION_ADDRESS) { - max_addresses[0].addr = lh->pref_address; - max_addresses[0].alloc_type = GRUB_EFI_ALLOCATE_ADDRESS; + max_addresses[KERNEL_PREF_ADDRESS].addr = lh->pref_address; + max_addresses[KERNEL_PREF_ADDRESS].alloc_type = GRUB_EFI_ALLOCATE_ADDRESS; } - max_addresses[1].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; - max_addresses[2].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; + max_addresses[KERNEL_4G_LIMIT].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; + max_addresses[KERNEL_NO_LIMIT].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; kernel_size = lh->init_size; kernel_mem = kernel_alloc (kernel_size, GRUB_EFI_RUNTIME_SERVICES_CODE, N_("can't allocate kernel")); From a9da903db6f510f9c0301c3f5260a16e82f878f1 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 1 Aug 2022 14:24:39 -0400 Subject: [PATCH 264/367] efi: split allocation policy for kernel vs initrd memories. Currently in our kernel allocator, we use the same set of choices for all of our various kernel and initramfs allocations, though they do not have exactly the same constraints. This patch adds the concept of an allocation purpose, which currently can be KERNEL_MEM or INITRD_MEM, and updates kernel_alloc() calls appropriately, but does not change any current policy decision. It also adds a few debug prints. Signed-off-by: Peter Jones --- grub-core/loader/i386/efi/linux.c | 35 ++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 8daa070132..e6b8998e5e 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -55,7 +55,14 @@ struct grub_linuxefi_context { #define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12) +typedef enum { + NO_MEM, + KERNEL_MEM, + INITRD_MEM, +} kernel_alloc_purpose_t; + struct allocation_choice { + kernel_alloc_purpose_t purpose; grub_efi_physical_address_t addr; grub_efi_allocate_type_t alloc_type; }; @@ -64,6 +71,7 @@ enum { KERNEL_PREF_ADDRESS, KERNEL_4G_LIMIT, KERNEL_NO_LIMIT, + INITRD_MAX_ADDRESS, }; static struct allocation_choice max_addresses[] = @@ -71,14 +79,17 @@ static struct allocation_choice max_addresses[] = /* the kernel overrides this one with pref_address and * GRUB_EFI_ALLOCATE_ADDRESS */ [KERNEL_PREF_ADDRESS] = - { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, + { KERNEL_MEM, GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, /* If the flag in params is set, this one gets changed to be above 4GB. */ [KERNEL_4G_LIMIT] = - { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, + { KERNEL_MEM, GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, /* this one is always below 4GB, which we still *prefer* even if the flag * is set. */ [KERNEL_NO_LIMIT] = - { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, + { KERNEL_MEM, GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, + /* this is for the initrd */ + [INITRD_MAX_ADDRESS] = + { INITRD_MEM, GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, { NO_MEM, 0, 0 } }; static struct allocation_choice saved_addresses[4]; @@ -95,7 +106,8 @@ kernel_free(void *addr, grub_efi_uintn_t size) } static void * -kernel_alloc(grub_efi_uintn_t size, +kernel_alloc(kernel_alloc_purpose_t purpose, + grub_efi_uintn_t size, grub_efi_memory_type_t memtype, const char * const errmsg) { @@ -108,6 +120,9 @@ kernel_alloc(grub_efi_uintn_t size, grub_uint64_t max = max_addresses[i].addr; grub_efi_uintn_t pages; + if (purpose != max_addresses[i].purpose) + continue; + /* * When we're *not* loading the kernel, or >4GB allocations aren't * supported, these entries are basically all the same, so don't re-try @@ -261,7 +276,8 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[]) } } - initrd_mem = kernel_alloc(size, GRUB_EFI_RUNTIME_SERVICES_DATA, + grub_dprintf ("linux", "Trying to allocate initrd mem\n"); + initrd_mem = kernel_alloc(INITRD_MEM, size, GRUB_EFI_RUNTIME_SERVICES_DATA, N_("can't allocate initrd")); if (initrd_mem == NULL) goto fail; @@ -422,7 +438,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } #endif - params = kernel_alloc (sizeof(*params), GRUB_EFI_RUNTIME_SERVICES_DATA, + params = kernel_alloc (KERNEL_MEM, sizeof(*params), + GRUB_EFI_RUNTIME_SERVICES_DATA, "cannot allocate kernel parameters"); if (!params) goto fail; @@ -445,7 +462,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_dprintf ("linux", "new lh is at %p\n", lh); grub_dprintf ("linux", "setting up cmdline\n"); - cmdline = kernel_alloc (lh->cmdline_size + 1, + cmdline = kernel_alloc (KERNEL_MEM, lh->cmdline_size + 1, GRUB_EFI_RUNTIME_SERVICES_DATA, N_("can't allocate cmdline")); if (!cmdline) @@ -493,7 +510,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), max_addresses[KERNEL_4G_LIMIT].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; max_addresses[KERNEL_NO_LIMIT].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; kernel_size = lh->init_size; - kernel_mem = kernel_alloc (kernel_size, GRUB_EFI_RUNTIME_SERVICES_CODE, + grub_dprintf ("linux", "Trying to allocate kernel mem\n"); + kernel_mem = kernel_alloc (KERNEL_MEM, kernel_size, + GRUB_EFI_RUNTIME_SERVICES_CODE, N_("can't allocate kernel")); restore_addresses(); if (!kernel_mem) From 8233b732246607f6cd086880c9132ad75ade78e8 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 1 Aug 2022 14:07:50 -0400 Subject: [PATCH 265/367] efi: allocate the initrd within the bounds expressed by the kernel Currently on x86, only linux kernels built with CONFIG_RELOCATABLE for x86_64 can be loaded above 4G, but the maximum address for the initramfs is specified via a HdrS field. This allows us to utilize that value, and unless loading the kernel above 4G, uses the value present there. If loading kernel above 4G is allowed, we assume loading the initramfs above 4G also works; in practice this has been true in the kernel code for quite some time. Resolves: rhbz#2112134 Signed-off-by: Peter Jones --- grub-core/loader/i386/efi/linux.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index e6b8998e5e..d003b474ee 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -190,6 +190,8 @@ grub_linuxefi_unload (void *data) cmd_initrdefi->data = 0; grub_free (context); + max_addresses[INITRD_MAX_ADDRESS].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; + return GRUB_ERR_NONE; } @@ -426,11 +428,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } #endif + max_addresses[INITRD_MAX_ADDRESS].addr = lh->initrd_addr_max; #if defined(__x86_64__) if (lh->xloadflags & LINUX_XLF_CAN_BE_LOADED_ABOVE_4G) { grub_dprintf ("linux", "Loading kernel above 4GB is supported; enabling.\n"); max_addresses[KERNEL_NO_LIMIT].addr = GRUB_EFI_MAX_USABLE_ADDRESS; + max_addresses[INITRD_MAX_ADDRESS].addr = GRUB_EFI_MAX_USABLE_ADDRESS; } else { @@ -560,6 +564,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_dl_unref (my_mod); + max_addresses[INITRD_MAX_ADDRESS].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; + if (lh) kernel_free (cmdline, lh->cmdline_size + 1); From cb965e0ad7b8a296c365ba6ca816000b952a99b4 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 1 Aug 2022 13:04:43 -0400 Subject: [PATCH 266/367] efi: use EFI_LOADER_(CODE|DATA) for kernel and initrd allocations At some point due to an erroneous kernel warning, we switched kernel and initramfs to being loaded in EFI_RUNTIME_SERVICES_CODE and EFI_RUNTIME_SERVICES_DATA memory pools. This doesn't appear to be correct according to the spec, and that kernel warning has gone away. This patch puts them back in EFI_LOADER_CODE and EFI_LOADER_DATA allocations, respectively. Resolves: rhbz#2108456 Signed-off-by: Peter Jones --- grub-core/loader/i386/efi/linux.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index d003b474ee..ac5ef50bdb 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -279,7 +279,7 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[]) } grub_dprintf ("linux", "Trying to allocate initrd mem\n"); - initrd_mem = kernel_alloc(INITRD_MEM, size, GRUB_EFI_RUNTIME_SERVICES_DATA, + initrd_mem = kernel_alloc(INITRD_MEM, size, GRUB_EFI_LOADER_DATA, N_("can't allocate initrd")); if (initrd_mem == NULL) goto fail; @@ -443,7 +443,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), #endif params = kernel_alloc (KERNEL_MEM, sizeof(*params), - GRUB_EFI_RUNTIME_SERVICES_DATA, + GRUB_EFI_LOADER_DATA, "cannot allocate kernel parameters"); if (!params) goto fail; @@ -467,7 +467,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_dprintf ("linux", "setting up cmdline\n"); cmdline = kernel_alloc (KERNEL_MEM, lh->cmdline_size + 1, - GRUB_EFI_RUNTIME_SERVICES_DATA, + GRUB_EFI_LOADER_DATA, N_("can't allocate cmdline")); if (!cmdline) goto fail; @@ -516,7 +516,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), kernel_size = lh->init_size; grub_dprintf ("linux", "Trying to allocate kernel mem\n"); kernel_mem = kernel_alloc (KERNEL_MEM, kernel_size, - GRUB_EFI_RUNTIME_SERVICES_CODE, + GRUB_EFI_LOADER_CODE, N_("can't allocate kernel")); restore_addresses(); if (!kernel_mem) From 2e2a8254a4c46ab740610b7d239a517232bf01d4 Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Tue, 2 Aug 2022 15:56:28 -0400 Subject: [PATCH 267/367] BLS: create /etc/kernel/cmdline during mkconfig Signed-off-by: Robbie Harwood --- util/grub.d/10_linux.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 865af3d6c4..9ebff661a9 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -161,6 +161,12 @@ update_bls_cmdline() local cmdline="root=${LINUX_ROOT_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" local -a files=($(get_sorted_bls)) + if [[ ! -f /etc/kernel/cmdline ]]; then + # anaconda has the correct information to do this during install; + # afterward, grubby will take care of syncing on updates. + echo "$cmdline rhgb quiet" > /etc/kernel/cmdline + fi + for bls in "${files[@]}"; do local options="${cmdline}" if [ -z "${bls##*debug*}" ]; then From 3acfee41562ebb8d714c7fa58ac510eeb6201864 Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Wed, 17 Aug 2022 10:26:07 -0400 Subject: [PATCH 268/367] squish: don't dup rhgb quiet, check mtimes Signed-off-by: Robbie Harwood --- util/grub.d/10_linux.in | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 9ebff661a9..41c6cb1dc2 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -161,10 +161,16 @@ update_bls_cmdline() local cmdline="root=${LINUX_ROOT_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" local -a files=($(get_sorted_bls)) - if [[ ! -f /etc/kernel/cmdline ]]; then - # anaconda has the correct information to do this during install; - # afterward, grubby will take care of syncing on updates. - echo "$cmdline rhgb quiet" > /etc/kernel/cmdline + if [[ ! -f /etc/kernel/cmdline ]] || + [[ /etc/kernel/cmdline -ot /etc/default/grub ]]; then + # anaconda has the correct information to create this during install; + # afterward, grubby will take care of syncing on updates. If the user + # has modified /etc/default/grub, try to cope. + if [[ ! "$cmdline" =~ "rhgb quiet" ]]; then + # ensure these only show up once + cmdline="$cmdline rhgb quiet" + fi + echo "$cmdline" > /etc/kernel/cmdline fi for bls in "${files[@]}"; do From 0720d5079307d88d9ada09a20ed308b168ae55fb Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Wed, 17 Aug 2022 11:30:30 -0400 Subject: [PATCH 269/367] squish: give up on rhgb quiet Signed-off-by: Robbie Harwood --- util/grub.d/10_linux.in | 4 ---- 1 file changed, 4 deletions(-) diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 41c6cb1dc2..5d1fa072f2 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -166,10 +166,6 @@ update_bls_cmdline() # anaconda has the correct information to create this during install; # afterward, grubby will take care of syncing on updates. If the user # has modified /etc/default/grub, try to cope. - if [[ ! "$cmdline" =~ "rhgb quiet" ]]; then - # ensure these only show up once - cmdline="$cmdline rhgb quiet" - fi echo "$cmdline" > /etc/kernel/cmdline fi From a2f76933480a3ba222cde85608faecc3ad266496 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Wed, 17 Aug 2022 10:26:03 -0400 Subject: [PATCH 270/367] squish: BLS: only write /etc/kernel/cmdline if writable On OSTree systems, `grub2-mkconfig` is run with `/etc` mounted read-only because as part of the promise of transactional updates, we want to make sure that we're not modifying the current deployment's state (`/etc` or `/var`). This conflicts with 0837dcdf1 ("BLS: create /etc/kernel/cmdline during mkconfig") which wants to write to `/etc/kernel/cmdline`. I'm not exactly sure on the background there, but based on the comment I think the intent is to fulfill grubby's expectation that the file exists. However, in systems like Silverblue, kernel arguments are managed by the rpm-ostree stack and grubby is not shipped at all. Adjust the script slightly so that we only write `/etc/kernel/cmdline` if the parent directory is writable. In the future, we're hoping to simplify things further on rpm-ostree systems by not running `grub2-mkconfig` at all since libostree already directly writes BLS entries. Doing that would also have avoided this, but ratcheting it into existing systems needs more careful thought. Signed-off-by: Jonathan Lebon Fixes: https://github.com/fedora-silverblue/issue-tracker/issues/322 --- util/grub.d/10_linux.in | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 5d1fa072f2..4795a63b4c 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -161,12 +161,13 @@ update_bls_cmdline() local cmdline="root=${LINUX_ROOT_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" local -a files=($(get_sorted_bls)) - if [[ ! -f /etc/kernel/cmdline ]] || - [[ /etc/kernel/cmdline -ot /etc/default/grub ]]; then - # anaconda has the correct information to create this during install; - # afterward, grubby will take care of syncing on updates. If the user - # has modified /etc/default/grub, try to cope. - echo "$cmdline" > /etc/kernel/cmdline + if [ -w /etc/kernel ] && + [[ ! -f /etc/kernel/cmdline || + /etc/kernel/cmdline -ot /etc/default/grub ]]; then + # anaconda has the correct information to create this during install; + # afterward, grubby will take care of syncing on updates. If the user + # has modified /etc/default/grub, try to cope. + echo "$cmdline" > /etc/kernel/cmdline fi for bls in "${files[@]}"; do From 6038557d4584b0d7f61fb292082ba6145b02dded Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Thu, 25 Aug 2022 17:57:55 -0400 Subject: [PATCH 271/367] blscfg: Don't root device in emu builds Otherwise, we end up looking for kernel/initrd in /boot/boot which doesn't work at all. Non-emu builds need to be looking in ($root)/boot/, which is what this is for. Signed-off-by: Robbie Harwood --- grub-core/commands/blscfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/commands/blscfg.c b/grub-core/commands/blscfg.c index e907a6a5d2..dbd0899acf 100644 --- a/grub-core/commands/blscfg.c +++ b/grub-core/commands/blscfg.c @@ -41,7 +41,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define GRUB_BLS_CONFIG_PATH "/loader/entries/" #ifdef GRUB_MACHINE_EMU -#define GRUB_BOOT_DEVICE "/boot" +#define GRUB_BOOT_DEVICE "" #else #define GRUB_BOOT_DEVICE "($root)" #endif From 62382eff5c44a07f8c176108807d446cbfdae609 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 11 Aug 2022 16:51:57 +0200 Subject: [PATCH 272/367] loader/arm64/linux: Remove magic number header field check The "ARM\x64" magic number in the file header identifies an image as one that implements the bare metal boot protocol, allowing the loader to simply move the file to a suitably aligned address in memory, with sufficient headroom for the trailing .bss segment (the required memory size is described in the header as well). Note of this matters for GRUB, as it only supports EFI boot. EFI does not care about this magic number, and nor should GRUB: this prevents us from booting other PE linux images, such as the generic EFI zboot decompressor, which is a pure PE/COFF image, and does not implement the bare metal boot protocol. So drop the magic number check. Signed-off-by: Ard Biesheuvel Reviewed-by: Daniel Kiper Resolves: rhbz#2125069 Signed-off-by: Jeremy Linton (cherry-picked from commit 69edb31205602c29293a8c6e67363bba2a4a1e66) Signed-off-by: Robbie Harwood --- grub-core/loader/arm64/linux.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c index de85583487..489d0c7173 100644 --- a/grub-core/loader/arm64/linux.c +++ b/grub-core/loader/arm64/linux.c @@ -55,9 +55,6 @@ static grub_addr_t initrd_end; grub_err_t grub_arch_efi_linux_check_image (struct linux_arch_kernel_header * lh) { - if (lh->magic != GRUB_LINUX_ARMXX_MAGIC_SIGNATURE) - return grub_error(GRUB_ERR_BAD_OS, "invalid magic number"); - if ((lh->code0 & 0xffff) != GRUB_DOS_MAGIC) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, N_("plain image kernel not supported - rebuild with CONFIG_(U)EFI_STUB enabled")); From 9752abcb38119b8fa52ba06e651e220c750e26c1 Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Tue, 6 Sep 2022 15:33:03 -0500 Subject: [PATCH 273/367] Correct BSS zeroing on aarch64 The aarch64 loader doesn't use efi bootservices, and therefor it has a very minimal loader which makes a lot of assumptions about the kernel layout. With the ZBOOT changes, the layout has changed a bit and we not should really be parsing the PE sections to determine how much data to copy, otherwise the BSS won't be setup properly. This code still makes a lot of assumptions about the the kernel layout, so its far from ideal, but it works. Resolves: rhbz#2125069 Signed-off-by: Jeremy Linton --- grub-core/loader/arm64/linux.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c index 489d0c7173..419f2201df 100644 --- a/grub-core/loader/arm64/linux.c +++ b/grub-core/loader/arm64/linux.c @@ -316,10 +316,12 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), static grub_err_t parse_pe_header (void *kernel, grub_uint64_t *total_size, grub_uint32_t *entry_offset, - grub_uint32_t *alignment) + grub_uint32_t *alignment,grub_uint32_t *code_size) { struct linux_arch_kernel_header *lh = kernel; struct grub_armxx_linux_pe_header *pe; + grub_uint16_t i; + struct grub_pe32_section_table *sections; pe = (void *)((unsigned long)kernel + lh->hdr_offset); @@ -329,6 +331,19 @@ parse_pe_header (void *kernel, grub_uint64_t *total_size, *total_size = pe->opt.image_size; *entry_offset = pe->opt.entry_addr; *alignment = pe->opt.section_alignment; + *code_size = pe->opt.section_alignment; + + sections = (struct grub_pe32_section_table *) ((char *)&pe->opt + + pe->coff.optional_header_size); + grub_dprintf ("linux", "num_sections : %d\n", pe->coff.num_sections ); + for (i = 0 ; i < pe->coff.num_sections; i++) + { + grub_dprintf ("linux", "raw_size : %lld\n", + (long long) sections[i].raw_data_size); + grub_dprintf ("linux", "virt_size : %lld\n", + (long long) sections[i].virtual_size); + *code_size += sections[i].raw_data_size; + } return GRUB_ERR_NONE; } @@ -341,6 +356,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_err_t err; grub_off_t filelen; grub_uint32_t align; + grub_uint32_t code_size; void *kernel = NULL; int nx_supported = 1; @@ -373,11 +389,12 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), if (grub_arch_efi_linux_check_image (kernel) != GRUB_ERR_NONE) goto fail; - if (parse_pe_header (kernel, &kernel_size, &handover_offset, &align) != GRUB_ERR_NONE) + if (parse_pe_header (kernel, &kernel_size, &handover_offset, &align, &code_size) != GRUB_ERR_NONE) goto fail; grub_dprintf ("linux", "kernel mem size : %lld\n", (long long) kernel_size); grub_dprintf ("linux", "kernel entry offset : %d\n", handover_offset); grub_dprintf ("linux", "kernel alignment : 0x%x\n", align); + grub_dprintf ("linux", "kernel size : 0x%x\n", code_size); err = grub_efi_check_nx_image_support((grub_addr_t)kernel, filelen, &nx_supported); if (err != GRUB_ERR_NONE) @@ -396,9 +413,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), kernel_addr = (void *)ALIGN_UP((grub_uint64_t)kernel_alloc_addr, align); grub_dprintf ("linux", "kernel @ %p\n", kernel_addr); - grub_memcpy (kernel_addr, kernel, grub_min(filelen, kernel_size)); - if (kernel_size > filelen) - grub_memset ((char *)kernel_addr + filelen, 0, kernel_size - filelen); + grub_memcpy (kernel_addr, kernel, grub_min(code_size, kernel_size)); + if (kernel_size > code_size) + grub_memset ((char *)kernel_addr + code_size, 0, kernel_size - code_size); grub_free(kernel); kernel = NULL; From 9ae5901bdb35b9071d2974b7a68648b41200b1d1 Mon Sep 17 00:00:00 2001 From: dann frazier Date: Thu, 25 Aug 2022 17:08:09 -0600 Subject: [PATCH 274/367] linuxefi: Invalidate i-cache before starting the kernel We need to flush the memory range of the code we are about to execute from the instruction cache before we can safely execute it. Not doing so appears to be the source of rare synchronous exceptions a user is seeing on a Cortex-A72-based platform while executing the Linux EFI stub. Notably they seem to correlate with an instruction on a cache line boundary. Signed-off-by: dann frazier --- grub-core/loader/efi/linux.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c index 277f352e0c..e413bdcc23 100644 --- a/grub-core/loader/efi/linux.c +++ b/grub-core/loader/efi/linux.c @@ -16,6 +16,7 @@ * along with GRUB. If not, see . */ +#include #include #include #include @@ -210,6 +211,9 @@ grub_efi_linux_boot (grub_addr_t kernel_addr, grub_size_t kernel_size, asm volatile ("cli"); #endif + /* Invalidate the instruction cache */ + grub_arch_sync_caches((void *)kernel_addr, kernel_size); + hf = (handover_func)((char *)kernel_addr + handover_offset + offset); hf (grub_efi_image_handle, grub_efi_system_table, kernel_params); From c5009357ed1b1907a7ac0956d3aeeee3d2260aa5 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 11 Oct 2022 17:00:50 -0400 Subject: [PATCH 275/367] x86-efi: Fix an incorrect array size in kernel allocation In 81a6ebf62bbe166ddc968463df2e8bd481bf697c ("efi: split allocation policy for kernel vs initrd memories."), I introduced a split in the kernel allocator to allow for different dynamic policies for the kernel and the initrd allocations. Unfortunately, that change increased the size of the policy data used to make decisions, but did not change the size of the temporary storage we use to back it up and restore. This results in some of .data getting clobbered at runtime, and hilarity ensues. This patch makes the size of the backup storage be based on the size of the initial policy data. Signed-off-by: Peter Jones --- grub-core/loader/i386/efi/linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index ac5ef50bdb..9854b0defa 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -92,7 +92,7 @@ static struct allocation_choice max_addresses[] = { INITRD_MEM, GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, { NO_MEM, 0, 0 } }; -static struct allocation_choice saved_addresses[4]; +static struct allocation_choice saved_addresses[sizeof(max_addresses) / sizeof(max_addresses[0])]; #define save_addresses() grub_memcpy(saved_addresses, max_addresses, sizeof(max_addresses)) #define restore_addresses() grub_memcpy(max_addresses, saved_addresses, sizeof(max_addresses)) From bf9f8670a6b8e938a3113824ccd377d78de46207 Mon Sep 17 00:00:00 2001 From: Lu Ken Date: Wed, 13 Jul 2022 10:06:10 +0800 Subject: [PATCH 276/367] commands/efi/tpm: Refine the status of log event 1. Use macro GRUB_ERR_NONE instead of hard code 0. 2. Keep lowercase of the first char for the status string of log event. Signed-off-by: Lu Ken Reviewed-by: Daniel Kiper (cherry picked from commit 922898573e37135f5dedc16f3e15a1d1d4c53f8a) --- grub-core/commands/efi/tpm.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/grub-core/commands/efi/tpm.c b/grub-core/commands/efi/tpm.c index a97d85368a..7acf510499 100644 --- a/grub-core/commands/efi/tpm.c +++ b/grub-core/commands/efi/tpm.c @@ -135,17 +135,17 @@ grub_efi_log_event_status (grub_efi_status_t status) switch (status) { case GRUB_EFI_SUCCESS: - return 0; + return GRUB_ERR_NONE; case GRUB_EFI_DEVICE_ERROR: - return grub_error (GRUB_ERR_IO, N_("Command failed")); + return grub_error (GRUB_ERR_IO, N_("command failed")); case GRUB_EFI_INVALID_PARAMETER: - return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid parameter")); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid parameter")); case GRUB_EFI_BUFFER_TOO_SMALL: - return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Output buffer too small")); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("output buffer too small")); case GRUB_EFI_NOT_FOUND: return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("TPM unavailable")); default: - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("Unknown TPM error")); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("unknown TPM error")); } } From 588414dead1389ef3720780d735d3757c5c94f5a Mon Sep 17 00:00:00 2001 From: Lu Ken Date: Wed, 13 Jul 2022 10:06:11 +0800 Subject: [PATCH 277/367] commands/efi/tpm: Use grub_strcpy() instead of grub_memcpy() The event description is a string, so using grub_strcpy() is cleaner than using grub_memcpy(). Signed-off-by: Lu Ken Reviewed-by: Daniel Kiper (cherry picked from commit ef8679b645a63eb9eb191bb9539d7d25a9d6ff3b) --- grub-core/commands/efi/tpm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/commands/efi/tpm.c b/grub-core/commands/efi/tpm.c index 7acf510499..bb59599721 100644 --- a/grub-core/commands/efi/tpm.c +++ b/grub-core/commands/efi/tpm.c @@ -175,7 +175,7 @@ grub_tpm1_log_event (grub_efi_handle_t tpm_handle, unsigned char *buf, event->PCRIndex = pcr; event->EventType = EV_IPL; event->EventSize = grub_strlen (description) + 1; - grub_memcpy (event->Event, description, event->EventSize); + grub_strcpy ((char *) event->Event, description); algorithm = TCG_ALG_SHA; status = efi_call_7 (tpm->log_extend_event, tpm, (grub_addr_t) buf, (grub_uint64_t) size, @@ -212,7 +212,7 @@ grub_tpm2_log_event (grub_efi_handle_t tpm_handle, unsigned char *buf, event->Header.EventType = EV_IPL; event->Size = sizeof (*event) - sizeof (event->Event) + grub_strlen (description) + 1; - grub_memcpy (event->Event, description, grub_strlen (description) + 1); + grub_strcpy ((char *) event->Event, description); status = efi_call_5 (tpm->hash_log_extend_event, tpm, 0, (grub_addr_t) buf, (grub_uint64_t) size, event); From fd559454a1e758954a0bc3ccc151cfe24e3e9ce5 Mon Sep 17 00:00:00 2001 From: Lu Ken Date: Wed, 13 Jul 2022 10:06:12 +0800 Subject: [PATCH 278/367] efi/tpm: Add EFI_CC_MEASUREMENT_PROTOCOL support The EFI_CC_MEASUREMENT_PROTOCOL abstracts the measurement for virtual firmware in confidential computing environment. It is similar to the EFI_TCG2_PROTOCOL. It was proposed by Intel and ARM and approved by UEFI organization. It is defined in Intel GHCI specification: https://cdrdv2.intel.com/v1/dl/getContent/726790 . The EDKII header file is available at https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/CcMeasurement.h . Signed-off-by: Lu Ken Reviewed-by: Daniel Kiper (cherry picked from commit 4c76565b6cb885b7e144dc27f3612066844e2d19) --- grub-core/commands/efi/tpm.c | 48 +++++++++++ include/grub/efi/cc.h | 151 +++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 include/grub/efi/cc.h diff --git a/grub-core/commands/efi/tpm.c b/grub-core/commands/efi/tpm.c index bb59599721..ae09c1bf8b 100644 --- a/grub-core/commands/efi/tpm.c +++ b/grub-core/commands/efi/tpm.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ typedef TCG_PCR_EVENT grub_tpm_event_t; static grub_efi_guid_t tpm_guid = EFI_TPM_GUID; static grub_efi_guid_t tpm2_guid = EFI_TPM2_GUID; +static grub_efi_guid_t cc_measurement_guid = GRUB_EFI_CC_MEASUREMENT_PROTOCOL_GUID; static grub_efi_handle_t *grub_tpm_handle; static grub_uint8_t grub_tpm_version; @@ -221,6 +223,50 @@ grub_tpm2_log_event (grub_efi_handle_t tpm_handle, unsigned char *buf, return grub_efi_log_event_status (status); } +static void +grub_cc_log_event (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, + const char *description) +{ + grub_efi_cc_event_t *event; + grub_efi_status_t status; + grub_efi_cc_protocol_t *cc; + grub_efi_cc_mr_index_t mr; + + cc = grub_efi_locate_protocol (&cc_measurement_guid, NULL); + if (cc == NULL) + return; + + status = efi_call_3 (cc->map_pcr_to_mr_index, cc, pcr, &mr); + if (status != GRUB_EFI_SUCCESS) + { + grub_efi_log_event_status (status); + return; + } + + event = grub_zalloc (sizeof (grub_efi_cc_event_t) + + grub_strlen (description) + 1); + if (event == NULL) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate CC event buffer")); + return; + } + + event->Header.HeaderSize = sizeof (grub_efi_cc_event_header_t); + event->Header.HeaderVersion = GRUB_EFI_CC_EVENT_HEADER_VERSION; + event->Header.MrIndex = mr; + event->Header.EventType = EV_IPL; + event->Size = sizeof (*event) + grub_strlen (description) + 1; + grub_strcpy ((char *) event->Event, description); + + status = efi_call_5 (cc->hash_log_extend_event, cc, 0, + (grub_efi_physical_address_t)(grub_addr_t) buf, + (grub_efi_uint64_t) size, event); + grub_free (event); + + if (status != GRUB_EFI_SUCCESS) + grub_efi_log_event_status (status); +} + grub_err_t grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, const char *description) @@ -228,6 +274,8 @@ grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, grub_efi_handle_t tpm_handle; grub_efi_uint8_t protocol_version; + grub_cc_log_event(buf, size, pcr, description); + if (!grub_tpm_handle_find (&tpm_handle, &protocol_version)) return 0; diff --git a/include/grub/efi/cc.h b/include/grub/efi/cc.h new file mode 100644 index 0000000000..8960306890 --- /dev/null +++ b/include/grub/efi/cc.h @@ -0,0 +1,151 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_EFI_CC_H +#define GRUB_EFI_CC_H 1 + +#include +#include +#include + +#define GRUB_EFI_CC_MEASUREMENT_PROTOCOL_GUID \ + { 0x96751a3d, 0x72f4, 0x41a6, \ + { 0xa7, 0x94, 0xed, 0x5d, 0x0e, 0x67, 0xae, 0x6b } \ + }; + +struct grub_efi_cc_version +{ + grub_efi_uint8_t Major; + grub_efi_uint8_t Minor; +}; +typedef struct grub_efi_cc_version grub_efi_cc_version_t; + +/* EFI_CC Type/SubType definition. */ +#define GRUB_EFI_CC_TYPE_NONE 0 +#define GRUB_EFI_CC_TYPE_SEV 1 +#define GRUB_EFI_CC_TYPE_TDX 2 + +struct grub_efi_cc_type +{ + grub_efi_uint8_t Type; + grub_efi_uint8_t SubType; +}; +typedef struct grub_efi_cc_type grub_efi_cc_type_t; + +typedef grub_efi_uint32_t grub_efi_cc_event_log_bitmap_t; +typedef grub_efi_uint32_t grub_efi_cc_event_log_format_t; +typedef grub_efi_uint32_t grub_efi_cc_event_algorithm_bitmap_t; +typedef grub_efi_uint32_t grub_efi_cc_mr_index_t; + +/* Intel TDX measure register index. */ +#define GRUB_TDX_MR_INDEX_MRTD 0 +#define GRUB_TDX_MR_INDEX_RTMR0 1 +#define GRUB_TDX_MR_INDEX_RTMR1 2 +#define GRUB_TDX_MR_INDEX_RTMR2 3 +#define GRUB_TDX_MR_INDEX_RTMR3 4 + +#define GRUB_EFI_CC_EVENT_LOG_FORMAT_TCG_2 0x00000002 +#define GRUB_EFI_CC_BOOT_HASH_ALG_SHA384 0x00000004 +#define GRUB_EFI_CC_EVENT_HEADER_VERSION 1 + +struct grub_efi_cc_event_header +{ + /* Size of the event header itself (sizeof(EFI_TD_EVENT_HEADER)). */ + grub_efi_uint32_t HeaderSize; + + /* + * Header version. For this version of this specification, + * the value shall be 1. + */ + grub_efi_uint16_t HeaderVersion; + + /* Index of the MR that shall be extended. */ + grub_efi_cc_mr_index_t MrIndex; + + /* Type of the event that shall be extended (and optionally logged). */ + grub_efi_uint32_t EventType; +} GRUB_PACKED; +typedef struct grub_efi_cc_event_header grub_efi_cc_event_header_t; + +struct grub_efi_cc_event +{ + /* Total size of the event including the Size component, the header and the Event data. */ + grub_efi_uint32_t Size; + grub_efi_cc_event_header_t Header; + grub_efi_uint8_t Event[0]; +} GRUB_PACKED; +typedef struct grub_efi_cc_event grub_efi_cc_event_t; + +struct grub_efi_cc_boot_service_capability +{ + /* Allocated size of the structure. */ + grub_efi_uint8_t Size; + + /* + * Version of the grub_efi_cc_boot_service_capability_t structure itself. + * For this version of the protocol, the Major version shall be set to 1 + * and the Minor version shall be set to 1. + */ + grub_efi_cc_version_t StructureVersion; + + /* + * Version of the EFI TD protocol. + * For this version of the protocol, the Major version shall be set to 1 + * and the Minor version shall be set to 1. + */ + grub_efi_cc_version_t ProtocolVersion; + + /* Supported hash algorithms. */ + grub_efi_cc_event_algorithm_bitmap_t HashAlgorithmBitmap; + + /* Bitmap of supported event log formats. */ + grub_efi_cc_event_log_bitmap_t SupportedEventLogs; + + /* Indicates the CC type. */ + grub_efi_cc_type_t CcType; +}; +typedef struct grub_efi_cc_boot_service_capability grub_efi_cc_boot_service_capability_t; + +struct grub_efi_cc_protocol +{ + grub_efi_status_t + (*get_capability) (struct grub_efi_cc_protocol *this, + grub_efi_cc_boot_service_capability_t *ProtocolCapability); + + grub_efi_status_t + (*get_event_log) (struct grub_efi_cc_protocol *this, + grub_efi_cc_event_log_format_t EventLogFormat, + grub_efi_physical_address_t *EventLogLocation, + grub_efi_physical_address_t *EventLogLastEntry, + grub_efi_boolean_t *EventLogTruncated); + + grub_efi_status_t + (*hash_log_extend_event) (struct grub_efi_cc_protocol *this, + grub_efi_uint64_t Flags, + grub_efi_physical_address_t DataToHash, + grub_efi_uint64_t DataToHashLen, + grub_efi_cc_event_t *EfiCcEvent); + + grub_efi_status_t + (*map_pcr_to_mr_index) (struct grub_efi_cc_protocol *this, + grub_efi_uint32_t PcrIndex, + grub_efi_cc_mr_index_t *MrIndex); +}; +typedef struct grub_efi_cc_protocol grub_efi_cc_protocol_t; + +#endif From 4197818988ac16d52696b1b474f38e274c7da732 Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Wed, 3 Aug 2022 19:45:33 +0800 Subject: [PATCH 279/367] font: Reject glyphs exceeds font->max_glyph_width or font->max_glyph_height Check glyph's width and height against limits specified in font's metadata. Reject the glyph (and font) if such limits are exceeded. Signed-off-by: Zhang Boyang Reviewed-by: Daniel Kiper (cherry picked from commit 5760fcfd466cc757540ea0d591bad6a08caeaa16) --- grub-core/font/font.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/grub-core/font/font.c b/grub-core/font/font.c index d09bb38d89..2f09a4a55b 100644 --- a/grub-core/font/font.c +++ b/grub-core/font/font.c @@ -760,7 +760,9 @@ grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code) || read_be_uint16 (font->file, &height) != 0 || read_be_int16 (font->file, &xoff) != 0 || read_be_int16 (font->file, &yoff) != 0 - || read_be_int16 (font->file, &dwidth) != 0) + || read_be_int16 (font->file, &dwidth) != 0 + || width > font->max_char_width + || height > font->max_char_height) { remove_font (font); return 0; From 1d9959733c901432e0d5347a18a70505f606c94f Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Fri, 5 Aug 2022 00:51:20 +0800 Subject: [PATCH 280/367] font: Fix size overflow in grub_font_get_glyph_internal() The length of memory allocation and file read may overflow. This patch fixes the problem by using safemath macros. There is a lot of code repetition like "(x * y + 7) / 8". It is unsafe if overflow happens. This patch introduces grub_video_bitmap_calc_1bpp_bufsz(). It is safe replacement for such code. It has safemath-like prototype. This patch also introduces grub_cast(value, pointer), it casts value to typeof(*pointer) then store the value to *pointer. It returns true when overflow occurs or false if there is no overflow. The semantics of arguments and return value are designed to be consistent with other safemath macros. Signed-off-by: Zhang Boyang Reviewed-by: Daniel Kiper (cherry picked from commit 941d10ad6f1dcbd12fb613002249e29ba035f985) --- grub-core/font/font.c | 17 +++++++++++++---- include/grub/bitmap.h | 18 ++++++++++++++++++ include/grub/safemath.h | 2 ++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/grub-core/font/font.c b/grub-core/font/font.c index 2f09a4a55b..6a3fbebbd8 100644 --- a/grub-core/font/font.c +++ b/grub-core/font/font.c @@ -739,7 +739,8 @@ grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code) grub_int16_t xoff; grub_int16_t yoff; grub_int16_t dwidth; - int len; + grub_ssize_t len; + grub_size_t sz; if (index_entry->glyph) /* Return cached glyph. */ @@ -768,9 +769,17 @@ grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code) return 0; } - len = (width * height + 7) / 8; - glyph = grub_malloc (sizeof (struct grub_font_glyph) + len); - if (!glyph) + /* Calculate real struct size of current glyph. */ + if (grub_video_bitmap_calc_1bpp_bufsz (width, height, &len) || + grub_add (sizeof (struct grub_font_glyph), len, &sz)) + { + remove_font (font); + return 0; + } + + /* Allocate and initialize the glyph struct. */ + glyph = grub_malloc (sz); + if (glyph == NULL) { remove_font (font); return 0; diff --git a/include/grub/bitmap.h b/include/grub/bitmap.h index 5728f8ca3a..0d9603f619 100644 --- a/include/grub/bitmap.h +++ b/include/grub/bitmap.h @@ -23,6 +23,7 @@ #include #include #include +#include struct grub_video_bitmap { @@ -79,6 +80,23 @@ grub_video_bitmap_get_height (struct grub_video_bitmap *bitmap) return bitmap->mode_info.height; } +/* + * Calculate and store the size of data buffer of 1bit bitmap in result. + * Equivalent to "*result = (width * height + 7) / 8" if no overflow occurs. + * Return true when overflow occurs or false if there is no overflow. + * This function is intentionally implemented as a macro instead of + * an inline function. Although a bit awkward, it preserves data types for + * safemath macros and reduces macro side effects as much as possible. + * + * XXX: Will report false overflow if width * height > UINT64_MAX. + */ +#define grub_video_bitmap_calc_1bpp_bufsz(width, height, result) \ +({ \ + grub_uint64_t _bitmap_pixels; \ + grub_mul ((width), (height), &_bitmap_pixels) ? 1 : \ + grub_cast (_bitmap_pixels / GRUB_CHAR_BIT + !!(_bitmap_pixels % GRUB_CHAR_BIT), (result)); \ +}) + void EXPORT_FUNC (grub_video_bitmap_get_mode_info) (struct grub_video_bitmap *bitmap, struct grub_video_mode_info *mode_info); diff --git a/include/grub/safemath.h b/include/grub/safemath.h index c17b89bba1..bb0f826de1 100644 --- a/include/grub/safemath.h +++ b/include/grub/safemath.h @@ -30,6 +30,8 @@ #define grub_sub(a, b, res) __builtin_sub_overflow(a, b, res) #define grub_mul(a, b, res) __builtin_mul_overflow(a, b, res) +#define grub_cast(a, res) grub_add ((a), 0, (res)) + #else #error gcc 5.1 or newer or clang 3.8 or newer is required #endif From 551e0d53b172745ed50ae7bb398c6cf76b620570 Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Fri, 5 Aug 2022 01:58:27 +0800 Subject: [PATCH 281/367] font: Fix several integer overflows in grub_font_construct_glyph() This patch fixes several integer overflows in grub_font_construct_glyph(). Glyphs of invalid size, zero or leading to an overflow, are rejected. The inconsistency between "glyph" and "max_glyph_size" when grub_malloc() returns NULL is fixed too. Fixes: CVE-2022-2601 Reported-by: Zhang Boyang Signed-off-by: Zhang Boyang Reviewed-by: Daniel Kiper (cherry picked from commit b1805f251b31a9d3cfae5c3572ddfa630145dbbf) --- grub-core/font/font.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/grub-core/font/font.c b/grub-core/font/font.c index 6a3fbebbd8..1fa181d4ca 100644 --- a/grub-core/font/font.c +++ b/grub-core/font/font.c @@ -1517,6 +1517,7 @@ grub_font_construct_glyph (grub_font_t hinted_font, struct grub_video_signed_rect bounds; static struct grub_font_glyph *glyph = 0; static grub_size_t max_glyph_size = 0; + grub_size_t cur_glyph_size; ensure_comb_space (glyph_id); @@ -1533,29 +1534,33 @@ grub_font_construct_glyph (grub_font_t hinted_font, if (!glyph_id->ncomb && !glyph_id->attributes) return main_glyph; - if (max_glyph_size < sizeof (*glyph) + (bounds.width * bounds.height + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT) + if (grub_video_bitmap_calc_1bpp_bufsz (bounds.width, bounds.height, &cur_glyph_size) || + grub_add (sizeof (*glyph), cur_glyph_size, &cur_glyph_size)) + return main_glyph; + + if (max_glyph_size < cur_glyph_size) { grub_free (glyph); - max_glyph_size = (sizeof (*glyph) + (bounds.width * bounds.height + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT) * 2; - if (max_glyph_size < 8) - max_glyph_size = 8; - glyph = grub_malloc (max_glyph_size); + if (grub_mul (cur_glyph_size, 2, &max_glyph_size)) + max_glyph_size = 0; + glyph = max_glyph_size > 0 ? grub_malloc (max_glyph_size) : NULL; } if (!glyph) { + max_glyph_size = 0; grub_errno = GRUB_ERR_NONE; return main_glyph; } - grub_memset (glyph, 0, sizeof (*glyph) - + (bounds.width * bounds.height - + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT); + grub_memset (glyph, 0, cur_glyph_size); glyph->font = main_glyph->font; - glyph->width = bounds.width; - glyph->height = bounds.height; - glyph->offset_x = bounds.x; - glyph->offset_y = bounds.y; + if (bounds.width == 0 || bounds.height == 0 || + grub_cast (bounds.width, &glyph->width) || + grub_cast (bounds.height, &glyph->height) || + grub_cast (bounds.x, &glyph->offset_x) || + grub_cast (bounds.y, &glyph->offset_y)) + return main_glyph; if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR) grub_font_blit_glyph_mirror (glyph, main_glyph, From 1dae55f9ff9378076ce50bc676c2e6407eeee223 Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Fri, 5 Aug 2022 02:13:29 +0800 Subject: [PATCH 282/367] font: Remove grub_font_dup_glyph() Remove grub_font_dup_glyph() since nobody is using it since 2013, and I'm too lazy to fix the integer overflow problem in it. Signed-off-by: Zhang Boyang Reviewed-by: Daniel Kiper (cherry picked from commit 25ad31c19c331aaa2dbd9bd2b2e2655de5766a9d) --- grub-core/font/font.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/grub-core/font/font.c b/grub-core/font/font.c index 1fa181d4ca..a115a63b0c 100644 --- a/grub-core/font/font.c +++ b/grub-core/font/font.c @@ -1055,20 +1055,6 @@ grub_font_get_glyph_with_fallback (grub_font_t font, grub_uint32_t code) return best_glyph; } -#if 0 -static struct grub_font_glyph * -grub_font_dup_glyph (struct grub_font_glyph *glyph) -{ - static struct grub_font_glyph *ret; - ret = grub_malloc (sizeof (*ret) + (glyph->width * glyph->height + 7) / 8); - if (!ret) - return NULL; - grub_memcpy (ret, glyph, sizeof (*ret) - + (glyph->width * glyph->height + 7) / 8); - return ret; -} -#endif - /* FIXME: suboptimal. */ static void grub_font_blit_glyph (struct grub_font_glyph *target, From f8852c9ca584499b79e21fbc1a8fce0b235cef40 Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Fri, 5 Aug 2022 02:27:05 +0800 Subject: [PATCH 283/367] font: Fix integer overflow in ensure_comb_space() In fact it can't overflow at all because glyph_id->ncomb is only 8-bit wide. But let's keep safe if somebody changes the width of glyph_id->ncomb in the future. This patch also fixes the inconsistency between render_max_comb_glyphs and render_combining_glyphs when grub_malloc() returns NULL. Signed-off-by: Zhang Boyang Reviewed-by: Daniel Kiper (cherry picked from commit b2740b7e4a03bb8331d48b54b119afea76bb9d5f) --- grub-core/font/font.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/grub-core/font/font.c b/grub-core/font/font.c index a115a63b0c..d0e6340404 100644 --- a/grub-core/font/font.c +++ b/grub-core/font/font.c @@ -1468,14 +1468,18 @@ ensure_comb_space (const struct grub_unicode_glyph *glyph_id) if (glyph_id->ncomb <= render_max_comb_glyphs) return; - render_max_comb_glyphs = 2 * glyph_id->ncomb; - if (render_max_comb_glyphs < 8) + if (grub_mul (glyph_id->ncomb, 2, &render_max_comb_glyphs)) + render_max_comb_glyphs = 0; + if (render_max_comb_glyphs > 0 && render_max_comb_glyphs < 8) render_max_comb_glyphs = 8; grub_free (render_combining_glyphs); - render_combining_glyphs = grub_malloc (render_max_comb_glyphs - * sizeof (render_combining_glyphs[0])); + render_combining_glyphs = (render_max_comb_glyphs > 0) ? + grub_calloc (render_max_comb_glyphs, sizeof (render_combining_glyphs[0])) : NULL; if (!render_combining_glyphs) - grub_errno = 0; + { + render_max_comb_glyphs = 0; + grub_errno = GRUB_ERR_NONE; + } } int From 86b719feeab9584d66c490303c7ecda74198897b Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Mon, 15 Aug 2022 02:04:58 +0800 Subject: [PATCH 284/367] font: Fix integer overflow in BMP index The BMP index (font->bmp_idx) is designed as a reverse lookup table of char entries (font->char_index), in order to speed up lookups for BMP chars (i.e. code < 0x10000). The values in BMP index are the subscripts of the corresponding char entries, stored in grub_uint16_t, while 0xffff means not found. This patch fixes the problem of large subscript truncated to grub_uint16_t, leading BMP index to return wrong char entry or report false miss. The code now checks for bounds and uses BMP index as a hint, and fallbacks to binary-search if necessary. On the occasion add a comment about BMP index is initialized to 0xffff. Signed-off-by: Zhang Boyang Reviewed-by: Daniel Kiper (cherry picked from commit afda8b60ba0712abe01ae1e64c5f7a067a0e6492) --- grub-core/font/font.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/grub-core/font/font.c b/grub-core/font/font.c index d0e6340404..b208a28717 100644 --- a/grub-core/font/font.c +++ b/grub-core/font/font.c @@ -300,6 +300,8 @@ load_font_index (grub_file_t file, grub_uint32_t sect_length, struct font->bmp_idx = grub_malloc (0x10000 * sizeof (grub_uint16_t)); if (!font->bmp_idx) return 1; + + /* Init the BMP index array to 0xffff. */ grub_memset (font->bmp_idx, 0xff, 0x10000 * sizeof (grub_uint16_t)); @@ -328,7 +330,7 @@ load_font_index (grub_file_t file, grub_uint32_t sect_length, struct return 1; } - if (entry->code < 0x10000) + if (entry->code < 0x10000 && i < 0xffff) font->bmp_idx[entry->code] = i; last_code = entry->code; @@ -696,9 +698,12 @@ find_glyph (const grub_font_t font, grub_uint32_t code) /* Use BMP index if possible. */ if (code < 0x10000 && font->bmp_idx) { - if (font->bmp_idx[code] == 0xffff) - return 0; - return &table[font->bmp_idx[code]]; + if (font->bmp_idx[code] < 0xffff) + return &table[font->bmp_idx[code]]; + /* + * When we are here then lookup in BMP index result in miss, + * fallthough to binary-search. + */ } /* Do a binary search in `char_index', which is ordered by code point. */ From 2f03fac569b1b67e4df5d7d9504400ca4fde89b1 Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Sun, 14 Aug 2022 18:09:38 +0800 Subject: [PATCH 285/367] font: Fix integer underflow in binary search of char index If search target is less than all entries in font->index then "hi" variable is set to -1, which translates to SIZE_MAX and leads to errors. This patch fixes the problem by replacing the entire binary search code with the libstdc++'s std::lower_bound() implementation. Signed-off-by: Zhang Boyang Reviewed-by: Daniel Kiper (cherry picked from commit c140a086838e7c9af87842036f891b8393a8c4bc) --- grub-core/font/font.c | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/grub-core/font/font.c b/grub-core/font/font.c index b208a28717..193dfec045 100644 --- a/grub-core/font/font.c +++ b/grub-core/font/font.c @@ -688,12 +688,12 @@ read_be_int16 (grub_file_t file, grub_int16_t * value) static inline struct char_index_entry * find_glyph (const grub_font_t font, grub_uint32_t code) { - struct char_index_entry *table; - grub_size_t lo; - grub_size_t hi; - grub_size_t mid; + struct char_index_entry *table, *first, *end; + grub_size_t len; table = font->char_index; + if (table == NULL) + return NULL; /* Use BMP index if possible. */ if (code < 0x10000 && font->bmp_idx) @@ -706,25 +706,29 @@ find_glyph (const grub_font_t font, grub_uint32_t code) */ } - /* Do a binary search in `char_index', which is ordered by code point. */ - lo = 0; - hi = font->num_chars - 1; - - if (!table) - return 0; + /* + * Do a binary search in char_index which is ordered by code point. + * The code below is the same as libstdc++'s std::lower_bound(). + */ + first = table; + len = font->num_chars; + end = first + len; - while (lo <= hi) + while (len > 0) { - mid = lo + (hi - lo) / 2; - if (code < table[mid].code) - hi = mid - 1; - else if (code > table[mid].code) - lo = mid + 1; + grub_size_t half = len >> 1; + struct char_index_entry *middle = first + half; + + if (middle->code < code) + { + first = middle + 1; + len = len - half - 1; + } else - return &table[mid]; + len = half; } - return 0; + return (first < end && first->code == code) ? first : NULL; } /* Get a glyph for the Unicode character CODE in FONT. The glyph is loaded From 539662956ad787fffa662720a67c98c217d78128 Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Sun, 14 Aug 2022 15:51:54 +0800 Subject: [PATCH 286/367] kern/efi/sb: Enforce verification of font files As a mitigation and hardening measure enforce verification of font files. Then only trusted font files can be load. This will reduce the attack surface at cost of losing the ability of end-users to customize fonts if e.g. UEFI Secure Boot is enabled. Vendors can always customize fonts because they have ability to pack fonts into their GRUB bundles. This goal is achieved by: * Removing GRUB_FILE_TYPE_FONT from shim lock verifier's skip-verification list. * Adding GRUB_FILE_TYPE_FONT to lockdown verifier's defer-auth list, so font files must be verified by a verifier before they can be loaded. Suggested-by: Daniel Kiper Signed-off-by: Zhang Boyang Reviewed-by: Daniel Kiper (cherry picked from commit 630deb8c0d8b02b670ced4b7030414bcf17aa080) --- grub-core/kern/efi/sb.c | 1 - grub-core/kern/lockdown.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/kern/efi/sb.c b/grub-core/kern/efi/sb.c index 89c4bb3fd1..db42c2539f 100644 --- a/grub-core/kern/efi/sb.c +++ b/grub-core/kern/efi/sb.c @@ -145,7 +145,6 @@ shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)), case GRUB_FILE_TYPE_PRINT_BLOCKLIST: case GRUB_FILE_TYPE_TESTLOAD: case GRUB_FILE_TYPE_GET_SIZE: - case GRUB_FILE_TYPE_FONT: case GRUB_FILE_TYPE_ZFS_ENCRYPTION_KEY: case GRUB_FILE_TYPE_CAT: case GRUB_FILE_TYPE_HEXCAT: diff --git a/grub-core/kern/lockdown.c b/grub-core/kern/lockdown.c index 0bc70fd42d..af6d493cd3 100644 --- a/grub-core/kern/lockdown.c +++ b/grub-core/kern/lockdown.c @@ -51,6 +51,7 @@ lockdown_verifier_init (grub_file_t io __attribute__ ((unused)), case GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE: case GRUB_FILE_TYPE_ACPI_TABLE: case GRUB_FILE_TYPE_DEVICE_TREE_IMAGE: + case GRUB_FILE_TYPE_FONT: *flags = GRUB_VERIFY_FLAGS_DEFER_AUTH; /* Fall through. */ From 7428f10c035884dac3a6327033567dc098f63be5 Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Tue, 6 Sep 2022 03:03:21 +0800 Subject: [PATCH 287/367] fbutil: Fix integer overflow Expressions like u64 = u32 * u32 are unsafe because their products are truncated to u32 even if left hand side is u64. This patch fixes all problems like that one in fbutil. To get right result not only left hand side have to be u64 but it's also necessary to cast at least one of the operands of all leaf operators of right hand side to u64, e.g. u64 = u32 * u32 + u32 * u32 should be u64 = (u64)u32 * u32 + (u64)u32 * u32. For 1-bit bitmaps grub_uint64_t have to be used. It's safe because any combination of values in (grub_uint64_t)u32 * u32 + u32 expression will not overflow grub_uint64_t. Other expressions like ptr + u32 * u32 + u32 * u32 are also vulnerable. They should be ptr + (grub_addr_t)u32 * u32 + (grub_addr_t)u32 * u32. This patch also adds a comment to grub_video_fb_get_video_ptr() which says it's arguments must be valid and no sanity check is performed (like its siblings in grub-core/video/fb/fbutil.c). Signed-off-by: Zhang Boyang Reviewed-by: Daniel Kiper (cherry picked from commit 50a11a81bc842c58962244a2dc86bbd31a426e12) --- grub-core/video/fb/fbutil.c | 4 ++-- include/grub/fbutil.h | 13 +++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/grub-core/video/fb/fbutil.c b/grub-core/video/fb/fbutil.c index b98bb51fe8..25ef39f47d 100644 --- a/grub-core/video/fb/fbutil.c +++ b/grub-core/video/fb/fbutil.c @@ -67,7 +67,7 @@ get_pixel (struct grub_video_fbblit_info *source, case 1: if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED) { - int bit_index = y * source->mode_info->width + x; + grub_uint64_t bit_index = (grub_uint64_t) y * source->mode_info->width + x; grub_uint8_t *ptr = source->data + bit_index / 8; int bit_pos = 7 - bit_index % 8; color = (*ptr >> bit_pos) & 0x01; @@ -138,7 +138,7 @@ set_pixel (struct grub_video_fbblit_info *source, case 1: if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED) { - int bit_index = y * source->mode_info->width + x; + grub_uint64_t bit_index = (grub_uint64_t) y * source->mode_info->width + x; grub_uint8_t *ptr = source->data + bit_index / 8; int bit_pos = 7 - bit_index % 8; *ptr = (*ptr & ~(1 << bit_pos)) | ((color & 0x01) << bit_pos); diff --git a/include/grub/fbutil.h b/include/grub/fbutil.h index 4205eb917f..78a1ab3b45 100644 --- a/include/grub/fbutil.h +++ b/include/grub/fbutil.h @@ -31,14 +31,19 @@ struct grub_video_fbblit_info grub_uint8_t *data; }; -/* Don't use for 1-bit bitmaps, addressing needs to be done at the bit level - and it doesn't make sense, in general, to ask for a pointer - to a particular pixel's data. */ +/* + * Don't use for 1-bit bitmaps, addressing needs to be done at the bit level + * and it doesn't make sense, in general, to ask for a pointer + * to a particular pixel's data. + * + * This function assumes that bounds checking has been done in previous phase + * and they are opted out in here. + */ static inline void * grub_video_fb_get_video_ptr (struct grub_video_fbblit_info *source, unsigned int x, unsigned int y) { - return source->data + y * source->mode_info->pitch + x * source->mode_info->bytes_per_pixel; + return source->data + (grub_addr_t) y * source->mode_info->pitch + (grub_addr_t) x * source->mode_info->bytes_per_pixel; } /* Advance pointer by VAL bytes. If there is no unaligned access available, From 7c85e1a8e19b7991ea341eeaa548ffceb55ca4af Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Mon, 24 Oct 2022 08:05:35 +0800 Subject: [PATCH 288/367] font: Fix an integer underflow in blit_comb() The expression (ctx.bounds.height - combining_glyphs[i]->height) / 2 may evaluate to a very big invalid value even if both ctx.bounds.height and combining_glyphs[i]->height are small integers. For example, if ctx.bounds.height is 10 and combining_glyphs[i]->height is 12, this expression evaluates to 2147483647 (expected -1). This is because coordinates are allowed to be negative but ctx.bounds.height is an unsigned int. So, the subtraction operates on unsigned ints and underflows to a very big value. The division makes things even worse. The quotient is still an invalid value even if converted back to int. This patch fixes the problem by casting ctx.bounds.height to int. As a result the subtraction will operate on int and grub_uint16_t which will be promoted to an int. So, the underflow will no longer happen. Other uses of ctx.bounds.height (and ctx.bounds.width) are also casted to int, to ensure coordinates are always calculated on signed integers. Fixes: CVE-2022-3775 Reported-by: Daniel Axtens Signed-off-by: Zhang Boyang Reviewed-by: Daniel Kiper (cherry picked from commit 6d2668dea3774ed74c4cd1eadd146f1b846bc3d4) --- grub-core/font/font.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/grub-core/font/font.c b/grub-core/font/font.c index 193dfec045..12a5f0d08c 100644 --- a/grub-core/font/font.c +++ b/grub-core/font/font.c @@ -1203,12 +1203,12 @@ blit_comb (const struct grub_unicode_glyph *glyph_id, ctx.bounds.height = main_glyph->height; above_rightx = main_glyph->offset_x + main_glyph->width; - above_righty = ctx.bounds.y + ctx.bounds.height; + above_righty = ctx.bounds.y + (int) ctx.bounds.height; above_leftx = main_glyph->offset_x; - above_lefty = ctx.bounds.y + ctx.bounds.height; + above_lefty = ctx.bounds.y + (int) ctx.bounds.height; - below_rightx = ctx.bounds.x + ctx.bounds.width; + below_rightx = ctx.bounds.x + (int) ctx.bounds.width; below_righty = ctx.bounds.y; comb = grub_unicode_get_comb (glyph_id); @@ -1221,7 +1221,7 @@ blit_comb (const struct grub_unicode_glyph *glyph_id, if (!combining_glyphs[i]) continue; - targetx = (ctx.bounds.width - combining_glyphs[i]->width) / 2 + ctx.bounds.x; + targetx = ((int) ctx.bounds.width - combining_glyphs[i]->width) / 2 + ctx.bounds.x; /* CGJ is to avoid diacritics reordering. */ if (comb[i].code == GRUB_UNICODE_COMBINING_GRAPHEME_JOINER) @@ -1231,8 +1231,8 @@ blit_comb (const struct grub_unicode_glyph *glyph_id, case GRUB_UNICODE_COMB_OVERLAY: do_blit (combining_glyphs[i], targetx, - (ctx.bounds.height - combining_glyphs[i]->height) / 2 - - (ctx.bounds.height + ctx.bounds.y), &ctx); + ((int) ctx.bounds.height - combining_glyphs[i]->height) / 2 + - ((int) ctx.bounds.height + ctx.bounds.y), &ctx); if (min_devwidth < combining_glyphs[i]->width) min_devwidth = combining_glyphs[i]->width; break; @@ -1305,7 +1305,7 @@ blit_comb (const struct grub_unicode_glyph *glyph_id, /* Fallthrough. */ case GRUB_UNICODE_STACK_ATTACHED_ABOVE: do_blit (combining_glyphs[i], targetx, - -(ctx.bounds.height + ctx.bounds.y + space + -((int) ctx.bounds.height + ctx.bounds.y + space + combining_glyphs[i]->height), &ctx); if (min_devwidth < combining_glyphs[i]->width) min_devwidth = combining_glyphs[i]->width; @@ -1313,7 +1313,7 @@ blit_comb (const struct grub_unicode_glyph *glyph_id, case GRUB_UNICODE_COMB_HEBREW_DAGESH: do_blit (combining_glyphs[i], targetx, - -(ctx.bounds.height / 2 + ctx.bounds.y + -((int) ctx.bounds.height / 2 + ctx.bounds.y + combining_glyphs[i]->height / 2), &ctx); if (min_devwidth < combining_glyphs[i]->width) min_devwidth = combining_glyphs[i]->width; From 5e5d8586d2cc24e524abb69645f9746481867a43 Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Mon, 24 Oct 2022 07:15:41 +0800 Subject: [PATCH 289/367] font: Harden grub_font_blit_glyph() and grub_font_blit_glyph_mirror() As a mitigation and hardening measure add sanity checks to grub_font_blit_glyph() and grub_font_blit_glyph_mirror(). This patch makes these two functions do nothing if target blitting area isn't fully contained in target bitmap. Therefore, if complex calculations in caller overflows and malicious coordinates are given, we are still safe because any coordinates which result in out-of-bound-write are rejected. However, this patch only checks for invalid coordinates, and doesn't provide any protection against invalid source glyph or destination glyph, e.g. mismatch between glyph size and buffer size. This hardening measure is designed to mitigate possible overflows in blit_comb(). If overflow occurs, it may return invalid bounding box during dry run and call grub_font_blit_glyph() with malicious coordinates during actual blitting. However, we are still safe because the scratch glyph itself is valid, although its size makes no sense, and any invalid coordinates are rejected. It would be better to call grub_fatal() if illegal parameter is detected. However, doing this may end up in a dangerous recursion because grub_fatal() would print messages to the screen and we are in the progress of drawing characters on the screen. Reported-by: Daniel Axtens Signed-off-by: Zhang Boyang Reviewed-by: Daniel Kiper (cherry picked from commit fcd7aa0c278f7cf3fb9f93f1a3966e1792339eb6) --- grub-core/font/font.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/grub-core/font/font.c b/grub-core/font/font.c index 12a5f0d08c..29fbb94294 100644 --- a/grub-core/font/font.c +++ b/grub-core/font/font.c @@ -1069,8 +1069,15 @@ static void grub_font_blit_glyph (struct grub_font_glyph *target, struct grub_font_glyph *src, unsigned dx, unsigned dy) { + grub_uint16_t max_x, max_y; unsigned src_bit, tgt_bit, src_byte, tgt_byte; unsigned i, j; + + /* Harden against out-of-bound writes. */ + if ((grub_add (dx, src->width, &max_x) || max_x > target->width) || + (grub_add (dy, src->height, &max_y) || max_y > target->height)) + return; + for (i = 0; i < src->height; i++) { src_bit = (src->width * i) % 8; @@ -1102,9 +1109,16 @@ grub_font_blit_glyph_mirror (struct grub_font_glyph *target, struct grub_font_glyph *src, unsigned dx, unsigned dy) { + grub_uint16_t max_x, max_y; unsigned tgt_bit, src_byte, tgt_byte; signed src_bit; unsigned i, j; + + /* Harden against out-of-bound writes. */ + if ((grub_add (dx, src->width, &max_x) || max_x > target->width) || + (grub_add (dy, src->height, &max_y) || max_y > target->height)) + return; + for (i = 0; i < src->height; i++) { src_bit = (src->width * i + src->width - 1) % 8; From 2cff5dbf2d5ee63d83105dcdd4d4776c690e9528 Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Fri, 28 Oct 2022 17:29:16 +0800 Subject: [PATCH 290/367] font: Assign null_font to glyphs in ascii_font_glyph[] The calculations in blit_comb() need information from glyph's font, e.g. grub_font_get_xheight(main_glyph->font). However, main_glyph->font is NULL if main_glyph comes from ascii_font_glyph[]. Therefore grub_font_get_*() crashes because of NULL pointer. There is already a solution, the null_font. So, assign it to those glyphs in ascii_font_glyph[]. Reported-by: Daniel Axtens Signed-off-by: Zhang Boyang Reviewed-by: Daniel Kiper (cherry picked from commit dd539d695482069d28b40f2d3821f710cdcf6ee6) --- grub-core/font/font.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/font/font.c b/grub-core/font/font.c index 29fbb94294..e6616e610c 100644 --- a/grub-core/font/font.c +++ b/grub-core/font/font.c @@ -137,7 +137,7 @@ ascii_glyph_lookup (grub_uint32_t code) ascii_font_glyph[current]->offset_x = 0; ascii_font_glyph[current]->offset_y = -2; ascii_font_glyph[current]->device_width = 8; - ascii_font_glyph[current]->font = NULL; + ascii_font_glyph[current]->font = &null_font; grub_memcpy (ascii_font_glyph[current]->bitmap, &ascii_bitmaps[current * ASCII_BITMAP_SIZE], From e1f86f14f780f8537a54cfb1ec12adc33f91956e Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Fri, 28 Oct 2022 21:31:39 +0800 Subject: [PATCH 291/367] normal/charset: Fix an integer overflow in grub_unicode_aglomerate_comb() The out->ncomb is a bit-field of 8 bits. So, the max possible value is 255. However, code in grub_unicode_aglomerate_comb() doesn't check for an overflow when incrementing out->ncomb. If out->ncomb is already 255, after incrementing it will get 0 instead of 256, and cause illegal memory access in subsequent processing. This patch introduces GRUB_UNICODE_NCOMB_MAX to represent the max acceptable value of ncomb. The code now checks for this limit and ignores additional combining characters when limit is reached. Reported-by: Daniel Axtens Signed-off-by: Zhang Boyang Reviewed-by: Daniel Kiper (cherry picked from commit da90d62316a3b105d2fbd7334d6521936bd6dcf6) --- grub-core/normal/charset.c | 3 +++ include/grub/unicode.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/grub-core/normal/charset.c b/grub-core/normal/charset.c index 7a5a7c153c..c243ca6dae 100644 --- a/grub-core/normal/charset.c +++ b/grub-core/normal/charset.c @@ -472,6 +472,9 @@ grub_unicode_aglomerate_comb (const grub_uint32_t *in, grub_size_t inlen, if (!haveout) continue; + if (out->ncomb == GRUB_UNICODE_NCOMB_MAX) + continue; + if (comb_type == GRUB_UNICODE_COMB_MC || comb_type == GRUB_UNICODE_COMB_ME || comb_type == GRUB_UNICODE_COMB_MN) diff --git a/include/grub/unicode.h b/include/grub/unicode.h index 4de986a857..c4f6fca043 100644 --- a/include/grub/unicode.h +++ b/include/grub/unicode.h @@ -147,7 +147,9 @@ struct grub_unicode_glyph grub_uint8_t bidi_level:6; /* minimum: 6 */ enum grub_bidi_type bidi_type:5; /* minimum: :5 */ +#define GRUB_UNICODE_NCOMB_MAX ((1 << 8) - 1) unsigned ncomb:8; + /* Hint by unicode subsystem how wide this character usually is. Real width is determined by font. Set only in UTF-8 stream. */ int estimated_width:8; From 10c5a7b0c1aa7811f98feb05b942e1f902a7ef70 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Wed, 16 Nov 2022 14:40:04 +0000 Subject: [PATCH 292/367] font: Try opening fonts from the bundled memdisk Signed-off-by: Robbie Harwood --- grub-core/font/font.c | 48 ++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/grub-core/font/font.c b/grub-core/font/font.c index e6616e610c..e421d1ae6f 100644 --- a/grub-core/font/font.c +++ b/grub-core/font/font.c @@ -409,6 +409,27 @@ read_section_as_short (struct font_file_section *section, return 0; } +static grub_file_t +try_open_from_prefix (const char *prefix, const char *filename) +{ + grub_file_t file; + char *fullname, *ptr; + + fullname = grub_malloc (grub_strlen (prefix) + grub_strlen (filename) + 1 + + sizeof ("/fonts/") + sizeof (".pf2")); + if (!fullname) + return 0; + ptr = grub_stpcpy (fullname, prefix); + ptr = grub_stpcpy (ptr, "/fonts/"); + ptr = grub_stpcpy (ptr, filename); + ptr = grub_stpcpy (ptr, ".pf2"); + *ptr = 0; + + file = grub_buffile_open (fullname, GRUB_FILE_TYPE_FONT, 1024); + grub_free (fullname); + return file; +} + /* Load a font and add it to the beginning of the global font list. Returns 0 upon success, nonzero upon failure. */ grub_font_t @@ -427,25 +448,18 @@ grub_font_load (const char *filename) file = grub_buffile_open (filename, GRUB_FILE_TYPE_FONT, 1024); else { - const char *prefix = grub_env_get ("prefix"); - char *fullname, *ptr; - if (!prefix) + file = try_open_from_prefix ("(memdisk)", filename); + if (!file) { - grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), - "prefix"); - goto fail; + const char *prefix = grub_env_get ("prefix"); + if (!prefix) + { + grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), + "prefix"); + goto fail; + } + file = try_open_from_prefix (prefix, filename); } - fullname = grub_malloc (grub_strlen (prefix) + grub_strlen (filename) + 1 - + sizeof ("/fonts/") + sizeof (".pf2")); - if (!fullname) - goto fail; - ptr = grub_stpcpy (fullname, prefix); - ptr = grub_stpcpy (ptr, "/fonts/"); - ptr = grub_stpcpy (ptr, filename); - ptr = grub_stpcpy (ptr, ".pf2"); - *ptr = 0; - file = grub_buffile_open (fullname, GRUB_FILE_TYPE_FONT, 1024); - grub_free (fullname); } if (!file) goto fail; From ae3a5833c27fc52f6dad6896b18b22372684c1c3 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Thu, 25 Nov 2021 02:22:46 +1100 Subject: [PATCH 293/367] mm: Clarify grub_real_malloc() When iterating through the singly linked list of free blocks, grub_real_malloc() uses p and q for the current and previous blocks respectively. This isn't super clear, so swap to using prev and cur. This makes another quirk more obvious. The comment at the top of grub_real_malloc() might lead you to believe that the function will allocate from *first if there is space in that block. It actually doesn't do that, and it can't do that with the current data structures. If we used up all of *first, we would need to change the ->next of the previous block to point to *first->next, but we can't do that because it's a singly linked list and we don't have access to *first's previous block. What grub_real_malloc() actually does is set *first to the initial previous block, and *first->next is the block we try to allocate from. That allows us to keep all the data structures consistent. Document that. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit 246ad6a44c281bb13486ddea0a26bb661db73106) --- grub-core/kern/mm.c | 76 ++++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c index d8c8377578..fb20e93acf 100644 --- a/grub-core/kern/mm.c +++ b/grub-core/kern/mm.c @@ -178,13 +178,20 @@ grub_mm_init_region (void *addr, grub_size_t size) } /* Allocate the number of units N with the alignment ALIGN from the ring - buffer starting from *FIRST. ALIGN must be a power of two. Both N and - ALIGN are in units of GRUB_MM_ALIGN. Return a non-NULL if successful, - otherwise return NULL. */ + * buffer given in *FIRST. ALIGN must be a power of two. Both N and + * ALIGN are in units of GRUB_MM_ALIGN. Return a non-NULL if successful, + * otherwise return NULL. + * + * Note: because in certain circumstances we need to adjust the ->next + * pointer of the previous block, we iterate over the singly linked + * list with the pair (prev, cur). *FIRST is our initial previous, and + * *FIRST->next is our initial current pointer. So we will actually + * allocate from *FIRST->next first and *FIRST itself last. + */ static void * grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align) { - grub_mm_header_t p, q; + grub_mm_header_t cur, prev; /* When everything is allocated side effect is that *first will have alloc magic marked, meaning that there is no room in this region. */ @@ -192,24 +199,24 @@ grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align) return 0; /* Try to search free slot for allocation in this memory region. */ - for (q = *first, p = q->next; ; q = p, p = p->next) + for (prev = *first, cur = prev->next; ; prev = cur, cur = cur->next) { grub_off_t extra; - extra = ((grub_addr_t) (p + 1) >> GRUB_MM_ALIGN_LOG2) & (align - 1); + extra = ((grub_addr_t) (cur + 1) >> GRUB_MM_ALIGN_LOG2) & (align - 1); if (extra) extra = align - extra; - if (! p) + if (! cur) grub_fatal ("null in the ring"); - if (p->magic != GRUB_MM_FREE_MAGIC) - grub_fatal ("free magic is broken at %p: 0x%x", p, p->magic); + if (cur->magic != GRUB_MM_FREE_MAGIC) + grub_fatal ("free magic is broken at %p: 0x%x", cur, cur->magic); - if (p->size >= n + extra) + if (cur->size >= n + extra) { - extra += (p->size - extra - n) & (~(align - 1)); - if (extra == 0 && p->size == n) + extra += (cur->size - extra - n) & (~(align - 1)); + if (extra == 0 && cur->size == n) { /* There is no special alignment requirement and memory block is complete match. @@ -222,9 +229,9 @@ grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align) | alloc, size=n | | +---------------+ v */ - q->next = p->next; + prev->next = cur->next; } - else if (align == 1 || p->size == n + extra) + else if (align == 1 || cur->size == n + extra) { /* There might be alignment requirement, when taking it into account memory block fits in. @@ -241,23 +248,22 @@ grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align) | alloc, size=n | | +---------------+ v */ - - p->size -= n; - p += p->size; + cur->size -= n; + cur += cur->size; } else if (extra == 0) { grub_mm_header_t r; - r = p + extra + n; + r = cur + extra + n; r->magic = GRUB_MM_FREE_MAGIC; - r->size = p->size - extra - n; - r->next = p->next; - q->next = r; + r->size = cur->size - extra - n; + r->next = cur->next; + prev->next = r; - if (q == p) + if (prev == cur) { - q = r; + prev = r; r->next = r; } } @@ -284,32 +290,32 @@ grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align) */ grub_mm_header_t r; - r = p + extra + n; + r = cur + extra + n; r->magic = GRUB_MM_FREE_MAGIC; - r->size = p->size - extra - n; - r->next = p; + r->size = cur->size - extra - n; + r->next = cur; - p->size = extra; - q->next = r; - p += extra; + cur->size = extra; + prev->next = r; + cur += extra; } - p->magic = GRUB_MM_ALLOC_MAGIC; - p->size = n; + cur->magic = GRUB_MM_ALLOC_MAGIC; + cur->size = n; /* Mark find as a start marker for next allocation to fasten it. This will have side effect of fragmenting memory as small pieces before this will be un-used. */ /* So do it only for chunks under 64K. */ if (n < (0x8000 >> GRUB_MM_ALIGN_LOG2) - || *first == p) - *first = q; + || *first == cur) + *first = prev; - return p + 1; + return cur + 1; } /* Search was completed without result. */ - if (p == *first) + if (cur == *first) break; } From b51f0d45a088e00f4c7486708f63282f2c8e80f2 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Thu, 25 Nov 2021 02:22:47 +1100 Subject: [PATCH 294/367] mm: grub_real_malloc(): Make small allocs comment match code Small allocations move the region's *first pointer. The comment says that this happens for allocations under 64K. The code says it's for allocations under 32K. Commit 45bf8b3a7549 changed the code intentionally: make the comment match. Fixes: 45bf8b3a7549 (* grub-core/kern/mm.c (grub_real_malloc): Decrease cut-off of moving the) Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit a847895a8d000bdf27ad4d4326f883a0eed769ca) --- grub-core/kern/mm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c index fb20e93acf..db7e0b2a5b 100644 --- a/grub-core/kern/mm.c +++ b/grub-core/kern/mm.c @@ -306,7 +306,7 @@ grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align) /* Mark find as a start marker for next allocation to fasten it. This will have side effect of fragmenting memory as small pieces before this will be un-used. */ - /* So do it only for chunks under 64K. */ + /* So do it only for chunks under 32K. */ if (n < (0x8000 >> GRUB_MM_ALIGN_LOG2) || *first == cur) *first = prev; From 280a656aa05d84a9a4ef31842d95506da62e283b Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Thu, 25 Nov 2021 02:22:48 +1100 Subject: [PATCH 295/367] mm: Document grub_free() The grub_free() possesses a surprising number of quirks, and also uses single-letter variable names confusingly to iterate through the free list. Document what's going on. Use prev and cur to iterate over the free list. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit 1f8d0b01738e49767d662d6426af3570a64565f0) --- grub-core/kern/mm.c | 63 +++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c index db7e0b2a5b..0351171cf9 100644 --- a/grub-core/kern/mm.c +++ b/grub-core/kern/mm.c @@ -446,54 +446,73 @@ grub_free (void *ptr) } else { - grub_mm_header_t q, s; + grub_mm_header_t cur, prev; #if 0 - q = r->first; + cur = r->first; do { grub_printf ("%s:%d: q=%p, q->size=0x%x, q->magic=0x%x\n", - GRUB_FILE, __LINE__, q, q->size, q->magic); - q = q->next; + GRUB_FILE, __LINE__, cur, cur->size, cur->magic); + cur = cur->next; } - while (q != r->first); + while (cur != r->first); #endif - - for (s = r->first, q = s->next; q <= p || q->next >= p; s = q, q = s->next) + /* Iterate over all blocks in the free ring. + * + * The free ring is arranged from high addresses to low + * addresses, modulo wraparound. + * + * We are looking for a block with a higher address than p or + * whose next address is lower than p. + */ + for (prev = r->first, cur = prev->next; cur <= p || cur->next >= p; + prev = cur, cur = prev->next) { - if (q->magic != GRUB_MM_FREE_MAGIC) - grub_fatal ("free magic is broken at %p: 0x%x", q, q->magic); + if (cur->magic != GRUB_MM_FREE_MAGIC) + grub_fatal ("free magic is broken at %p: 0x%x", cur, cur->magic); - if (q <= q->next && (q > p || q->next < p)) + /* Deal with wrap-around */ + if (cur <= cur->next && (cur > p || cur->next < p)) break; } + /* mark p as free and insert it between cur and cur->next */ p->magic = GRUB_MM_FREE_MAGIC; - p->next = q->next; - q->next = p; + p->next = cur->next; + cur->next = p; + /* + * If the block we are freeing can be merged with the next + * free block, do that. + */ if (p->next + p->next->size == p) { p->magic = 0; p->next->size += p->size; - q->next = p->next; + cur->next = p->next; p = p->next; } - r->first = q; + r->first = cur; - if (q == p + p->size) + /* Likewise if can be merged with the preceeding free block */ + if (cur == p + p->size) { - q->magic = 0; - p->size += q->size; - if (q == s) - s = p; - s->next = p; - q = s; + cur->magic = 0; + p->size += cur->size; + if (cur == prev) + prev = p; + prev->next = p; + cur = prev; } - r->first = q; + /* + * Set r->first such that the just free()d block is tried first. + * (An allocation is tried from *first->next, and cur->next == p.) + */ + r->first = cur; } } From 5fc6860118d8b142011042bd61e7f0992837c3ce Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Thu, 25 Nov 2021 02:22:49 +1100 Subject: [PATCH 296/367] mm: Document grub_mm_init_region() The grub_mm_init_region() does some things that seem magical, especially around region merging. Make it a bit clearer. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit 246d69b7ea619fc1e77dcc5960e37aea45a9808c) --- grub-core/kern/mm.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c index 0351171cf9..1cbf98c7ab 100644 --- a/grub-core/kern/mm.c +++ b/grub-core/kern/mm.c @@ -128,23 +128,52 @@ grub_mm_init_region (void *addr, grub_size_t size) if (((grub_addr_t) addr + 0x1000) > ~(grub_addr_t) size) size = ((grub_addr_t) -0x1000) - (grub_addr_t) addr; + /* Attempt to merge this region with every existing region */ for (p = &grub_mm_base, q = *p; q; p = &(q->next), q = *p) + /* + * Is the new region immediately below an existing region? That + * is, is the address of the memory we're adding now (addr) + size + * of the memory we're adding (size) + the bytes we couldn't use + * at the start of the region we're considering (q->pre_size) + * equal to the address of q? In other words, does the memory + * looks like this? + * + * addr q + * |----size-----|-q->pre_size-|| + */ if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q) { + /* + * Yes, we can merge the memory starting at addr into the + * existing region from below. Align up addr to GRUB_MM_ALIGN + * so that our new region has proper alignment. + */ r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN); + /* Copy the region data across */ *r = *q; + /* Consider all the new size as pre-size */ r->pre_size += size; - + + /* + * If we have enough pre-size to create a block, create a + * block with it. Mark it as allocated and pass it to + * grub_free (), which will sort out getting it into the free + * list. + */ if (r->pre_size >> GRUB_MM_ALIGN_LOG2) { h = (grub_mm_header_t) (r + 1); + /* block size is pre-size converted to cells */ h->size = (r->pre_size >> GRUB_MM_ALIGN_LOG2); h->magic = GRUB_MM_ALLOC_MAGIC; + /* region size grows by block size converted back to bytes */ r->size += h->size << GRUB_MM_ALIGN_LOG2; + /* adjust pre_size to be accurate */ r->pre_size &= (GRUB_MM_ALIGN - 1); *p = r; grub_free (h + 1); } + /* Replace the old region with the new region */ *p = r; return; } From 9cfee085d810d483a2e5c159b699947988066f8b Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Thu, 25 Nov 2021 02:22:45 +1100 Subject: [PATCH 297/367] mm: Document GRUB internal memory management structures I spent more than a trivial quantity of time figuring out pre_size and whether a memory region's size contains the header cell or not. Document the meanings of all the properties. Hopefully now no-one else has to figure it out! Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit a6c5c52ccffd2674d43db25fb4baa9c528526aa0) --- include/grub/mm_private.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/include/grub/mm_private.h b/include/grub/mm_private.h index c2c4cb1511..203533cc3d 100644 --- a/include/grub/mm_private.h +++ b/include/grub/mm_private.h @@ -21,15 +21,27 @@ #include +/* For context, see kern/mm.c */ + /* Magic words. */ #define GRUB_MM_FREE_MAGIC 0x2d3c2808 #define GRUB_MM_ALLOC_MAGIC 0x6db08fa4 +/* A header describing a block of memory - either allocated or free */ typedef struct grub_mm_header { + /* + * The 'next' free block in this region's circular free list. + * Only meaningful if the block is free. + */ struct grub_mm_header *next; + /* The block size, not in bytes but the number of cells of + * GRUB_MM_ALIGN bytes. Includes the header cell. + */ grub_size_t size; + /* either free or alloc magic, depending on the block type. */ grub_size_t magic; + /* pad to cell size: see the top of kern/mm.c. */ #if GRUB_CPU_SIZEOF_VOID_P == 4 char padding[4]; #elif GRUB_CPU_SIZEOF_VOID_P == 8 @@ -48,11 +60,27 @@ typedef struct grub_mm_header #define GRUB_MM_ALIGN (1 << GRUB_MM_ALIGN_LOG2) +/* A region from which we can make allocations. */ typedef struct grub_mm_region { + /* The first free block in this region. */ struct grub_mm_header *first; + + /* + * The next region in the linked list of regions. Regions are initially + * sorted in order of increasing size, but can grow, in which case the + * ordering may not be preserved. + */ struct grub_mm_region *next; + + /* + * A grub_mm_region will always be aligned to cell size. The pre-size is + * the number of bytes we were given but had to skip in order to get that + * alignment. + */ grub_size_t pre_size; + + /* How many bytes are in this region? (free and allocated) */ grub_size_t size; } *grub_mm_region_t; From 3ec3df3bc8993391fdcc9e5cec972c14c1f2daea Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Thu, 21 Apr 2022 15:24:14 +1000 Subject: [PATCH 298/367] mm: Assert that we preserve header vs region alignment grub_mm_region_init() does: h = (grub_mm_header_t) (r + 1); where h is a grub_mm_header_t and r is a grub_mm_region_t. Cells are supposed to be GRUB_MM_ALIGN aligned, but while grub_mm_dump ensures this vs the region header, grub_mm_region_init() does not. It's better to be explicit than implicit here: rather than changing grub_mm_region_init() to ALIGN_UP(), require that the struct is explicitly a multiple of the header size. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper Tested-by: Patrick Steinhardt (cherry picked from commit 1df8fe66c57087eb33bd6dc69f786ed124615aa7) --- include/grub/mm_private.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/grub/mm_private.h b/include/grub/mm_private.h index 203533cc3d..a688b92a83 100644 --- a/include/grub/mm_private.h +++ b/include/grub/mm_private.h @@ -20,6 +20,7 @@ #define GRUB_MM_PRIVATE_H 1 #include +#include /* For context, see kern/mm.c */ @@ -89,4 +90,17 @@ typedef struct grub_mm_region extern grub_mm_region_t EXPORT_VAR (grub_mm_base); #endif +static inline void +grub_mm_size_sanity_check (void) { + /* Ensure we preserve alignment when doing h = (grub_mm_header_t) (r + 1). */ + COMPILE_TIME_ASSERT ((sizeof (struct grub_mm_region) % + sizeof (struct grub_mm_header)) == 0); + + /* + * GRUB_MM_ALIGN is supposed to represent cell size, and a mm_header is + * supposed to be 1 cell. + */ + COMPILE_TIME_ASSERT (sizeof (struct grub_mm_header) == GRUB_MM_ALIGN); +} + #endif From 77371ff81654aee61c68bda1cac3d21c38e2dfa1 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Thu, 21 Apr 2022 15:24:15 +1000 Subject: [PATCH 299/367] mm: When adding a region, merge with region after as well as before On x86_64-efi (at least) regions seem to be added from top down. The mm code will merge a new region with an existing region that comes immediately before the new region. This allows larger allocations to be satisfied that would otherwise be the case. On powerpc-ieee1275, however, regions are added from bottom up. So if we add 3x 32MB regions, we can still only satisfy a 32MB allocation, rather than the 96MB allocation we might otherwise be able to satisfy. * Define 'post_size' as being bytes lost to the end of an allocation due to being given weird sizes from firmware that are not multiples of GRUB_MM_ALIGN. * Allow merging of regions immediately _after_ existing regions, not just before. As with the other approach, we create an allocated block to represent the new space and the pass it to grub_free() to get the metadata right. Signed-off-by: Daniel Axtens Tested-by: Stefan Berger Reviewed-by: Daniel Kiper Tested-by: Patrick Steinhardt (cherry picked from commit 052e6068be622ff53f1238b449c300dbd0a8abcd) --- grub-core/kern/mm.c | 130 ++++++++++++++++++++++++-------------- include/grub/mm_private.h | 9 +++ 2 files changed, 92 insertions(+), 47 deletions(-) diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c index 1cbf98c7ab..7be33e23bf 100644 --- a/grub-core/kern/mm.c +++ b/grub-core/kern/mm.c @@ -130,53 +130,88 @@ grub_mm_init_region (void *addr, grub_size_t size) /* Attempt to merge this region with every existing region */ for (p = &grub_mm_base, q = *p; q; p = &(q->next), q = *p) - /* - * Is the new region immediately below an existing region? That - * is, is the address of the memory we're adding now (addr) + size - * of the memory we're adding (size) + the bytes we couldn't use - * at the start of the region we're considering (q->pre_size) - * equal to the address of q? In other words, does the memory - * looks like this? - * - * addr q - * |----size-----|-q->pre_size-|| - */ - if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q) - { - /* - * Yes, we can merge the memory starting at addr into the - * existing region from below. Align up addr to GRUB_MM_ALIGN - * so that our new region has proper alignment. - */ - r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN); - /* Copy the region data across */ - *r = *q; - /* Consider all the new size as pre-size */ - r->pre_size += size; - - /* - * If we have enough pre-size to create a block, create a - * block with it. Mark it as allocated and pass it to - * grub_free (), which will sort out getting it into the free - * list. - */ - if (r->pre_size >> GRUB_MM_ALIGN_LOG2) - { - h = (grub_mm_header_t) (r + 1); - /* block size is pre-size converted to cells */ - h->size = (r->pre_size >> GRUB_MM_ALIGN_LOG2); - h->magic = GRUB_MM_ALLOC_MAGIC; - /* region size grows by block size converted back to bytes */ - r->size += h->size << GRUB_MM_ALIGN_LOG2; - /* adjust pre_size to be accurate */ - r->pre_size &= (GRUB_MM_ALIGN - 1); - *p = r; - grub_free (h + 1); - } - /* Replace the old region with the new region */ - *p = r; - return; - } + { + /* + * Is the new region immediately below an existing region? That + * is, is the address of the memory we're adding now (addr) + size + * of the memory we're adding (size) + the bytes we couldn't use + * at the start of the region we're considering (q->pre_size) + * equal to the address of q? In other words, does the memory + * looks like this? + * + * addr q + * |----size-----|-q->pre_size-|| + */ + if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q) + { + /* + * Yes, we can merge the memory starting at addr into the + * existing region from below. Align up addr to GRUB_MM_ALIGN + * so that our new region has proper alignment. + */ + r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN); + /* Copy the region data across */ + *r = *q; + /* Consider all the new size as pre-size */ + r->pre_size += size; + + /* + * If we have enough pre-size to create a block, create a + * block with it. Mark it as allocated and pass it to + * grub_free (), which will sort out getting it into the free + * list. + */ + if (r->pre_size >> GRUB_MM_ALIGN_LOG2) + { + h = (grub_mm_header_t) (r + 1); + /* block size is pre-size converted to cells */ + h->size = (r->pre_size >> GRUB_MM_ALIGN_LOG2); + h->magic = GRUB_MM_ALLOC_MAGIC; + /* region size grows by block size converted back to bytes */ + r->size += h->size << GRUB_MM_ALIGN_LOG2; + /* adjust pre_size to be accurate */ + r->pre_size &= (GRUB_MM_ALIGN - 1); + *p = r; + grub_free (h + 1); + } + /* Replace the old region with the new region */ + *p = r; + return; + } + + /* + * Is the new region immediately above an existing region? That + * is: + * q addr + * ||-q->post_size-|----size-----| + */ + if ((grub_uint8_t *) q + sizeof (*q) + q->size + q->post_size == + (grub_uint8_t *) addr) + { + /* + * Yes! Follow a similar pattern to above, but simpler. + * Our header starts at address - post_size, which should align us + * to a cell boundary. + * + * Cast to (void *) first to avoid the following build error: + * kern/mm.c: In function ‘grub_mm_init_region’: + * kern/mm.c:211:15: error: cast increases required alignment of target type [-Werror=cast-align] + * 211 | h = (grub_mm_header_t) ((grub_uint8_t *) addr - q->post_size); + * | ^ + * It is safe to do that because proper alignment is enforced in grub_mm_size_sanity_check(). + */ + h = (grub_mm_header_t)(void *) ((grub_uint8_t *) addr - q->post_size); + /* our size is the allocated size plus post_size, in cells */ + h->size = (size + q->post_size) >> GRUB_MM_ALIGN_LOG2; + h->magic = GRUB_MM_ALLOC_MAGIC; + /* region size grows by block size converted back to bytes */ + q->size += h->size << GRUB_MM_ALIGN_LOG2; + /* adjust new post_size to be accurate */ + q->post_size = (q->post_size + size) & (GRUB_MM_ALIGN - 1); + grub_free (h + 1); + return; + } + } /* Allocate a region from the head. */ r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN); @@ -195,6 +230,7 @@ grub_mm_init_region (void *addr, grub_size_t size) r->first = h; r->pre_size = (grub_addr_t) r - (grub_addr_t) addr; r->size = (h->size << GRUB_MM_ALIGN_LOG2); + r->post_size = size - r->size; /* Find where to insert this region. Put a smaller one before bigger ones, to prevent fragmentation. */ diff --git a/include/grub/mm_private.h b/include/grub/mm_private.h index a688b92a83..96c2d816be 100644 --- a/include/grub/mm_private.h +++ b/include/grub/mm_private.h @@ -81,8 +81,17 @@ typedef struct grub_mm_region */ grub_size_t pre_size; + /* + * Likewise, the post-size is the number of bytes we wasted at the end + * of the allocation because it wasn't a multiple of GRUB_MM_ALIGN + */ + grub_size_t post_size; + /* How many bytes are in this region? (free and allocated) */ grub_size_t size; + + /* pad to a multiple of cell size */ + char padding[3 * GRUB_CPU_SIZEOF_VOID_P]; } *grub_mm_region_t; From 474d21230355ffed38b2ced5afa471afb7c22438 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Thu, 21 Apr 2022 15:24:16 +1000 Subject: [PATCH 300/367] mm: Debug support for region operations This is handy for debugging. Enable with "set debug=regions". Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper Tested-by: Patrick Steinhardt (cherry picked from commit 8afa5ef45b797ba5d8147ceee85ac2c59dcc7f09) --- grub-core/kern/mm.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c index 7be33e23bf..38bfb01df9 100644 --- a/grub-core/kern/mm.c +++ b/grub-core/kern/mm.c @@ -115,9 +115,8 @@ grub_mm_init_region (void *addr, grub_size_t size) grub_mm_header_t h; grub_mm_region_t r, *p, q; -#if 0 - grub_printf ("Using memory for heap: start=%p, end=%p\n", addr, addr + (unsigned int) size); -#endif + grub_dprintf ("regions", "Using memory for heap: start=%p, end=%p\n", + addr, (char *) addr + (unsigned int) size); /* Exclude last 4K to avoid overflows. */ /* If addr + 0x1000 overflows then whole region is in excluded zone. */ @@ -142,8 +141,14 @@ grub_mm_init_region (void *addr, grub_size_t size) * addr q * |----size-----|-q->pre_size-|| */ + grub_dprintf ("regions", "Can we extend into region above?" + " %p + %" PRIxGRUB_SIZE " + %" PRIxGRUB_SIZE " ?=? %p\n", + (grub_uint8_t *) addr, size, q->pre_size, (grub_uint8_t *) q); if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q) { + grub_dprintf ("regions", "Yes: extending a region: (%p -> %p) -> (%p -> %p)\n", + q, (grub_uint8_t *) q + sizeof (*q) + q->size, + addr, (grub_uint8_t *) q + sizeof (*q) + q->size); /* * Yes, we can merge the memory starting at addr into the * existing region from below. Align up addr to GRUB_MM_ALIGN @@ -185,9 +190,15 @@ grub_mm_init_region (void *addr, grub_size_t size) * q addr * ||-q->post_size-|----size-----| */ + grub_dprintf ("regions", "Can we extend into region below?" + " %p + %" PRIxGRUB_SIZE " + %" PRIxGRUB_SIZE " + %" PRIxGRUB_SIZE " ?=? %p\n", + (grub_uint8_t *) q, sizeof(*q), q->size, q->post_size, (grub_uint8_t *) addr); if ((grub_uint8_t *) q + sizeof (*q) + q->size + q->post_size == (grub_uint8_t *) addr) { + grub_dprintf ("regions", "Yes: extending a region: (%p -> %p) -> (%p -> %p)\n", + q, (grub_uint8_t *) q + sizeof (*q) + q->size, + q, (grub_uint8_t *) addr + size); /* * Yes! Follow a similar pattern to above, but simpler. * Our header starts at address - post_size, which should align us @@ -213,6 +224,8 @@ grub_mm_init_region (void *addr, grub_size_t size) } } + grub_dprintf ("regions", "No: considering a new region at %p of size %" PRIxGRUB_SIZE "\n", + addr, size); /* Allocate a region from the head. */ r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN); From 629bce3847f2a1ed2a6bc6c724756e01f57181ec Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 21 Apr 2022 15:24:17 +1000 Subject: [PATCH 301/367] mm: Drop unused unloading of modules on OOM In grub_memalign(), there's a commented section which would allow for unloading of unneeded modules in case where there is not enough free memory available to satisfy a request. Given that this code is never compiled in, let's remove it together with grub_dl_unload_unneeded(). Signed-off-by: Patrick Steinhardt Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper Tested-by: Patrick Steinhardt (cherry picked from commit 139fd9b134a01e0b5fe0ebefafa7f48d1ffb6d60) --- grub-core/kern/dl.c | 20 -------------------- grub-core/kern/mm.c | 8 -------- include/grub/dl.h | 1 - 3 files changed, 29 deletions(-) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index d5de80186f..ab9101a5ad 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -998,23 +998,3 @@ grub_dl_unload (grub_dl_t mod) grub_free (mod); return 1; } - -/* Unload unneeded modules. */ -void -grub_dl_unload_unneeded (void) -{ - /* Because grub_dl_remove modifies the list of modules, this - implementation is tricky. */ - grub_dl_t p = grub_dl_head; - - while (p) - { - if (grub_dl_unload (p)) - { - p = grub_dl_head; - continue; - } - - p = p->next; - } -} diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c index 38bfb01df9..1825dc8289 100644 --- a/grub-core/kern/mm.c +++ b/grub-core/kern/mm.c @@ -444,14 +444,6 @@ grub_memalign (grub_size_t align, grub_size_t size) count++; goto again; -#if 0 - case 1: - /* Unload unneeded modules. */ - grub_dl_unload_unneeded (); - count++; - goto again; -#endif - default: break; } diff --git a/include/grub/dl.h b/include/grub/dl.h index 45ac8e339f..6bc2560bf0 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -206,7 +206,6 @@ grub_dl_t EXPORT_FUNC(grub_dl_load) (const char *name); grub_dl_t grub_dl_load_core (void *addr, grub_size_t size); grub_dl_t EXPORT_FUNC(grub_dl_load_core_noinit) (void *addr, grub_size_t size); int EXPORT_FUNC(grub_dl_unload) (grub_dl_t mod); -extern void grub_dl_unload_unneeded (void); extern int EXPORT_FUNC(grub_dl_ref) (grub_dl_t mod); extern int EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod); extern int EXPORT_FUNC(grub_dl_ref_count) (grub_dl_t mod); From dcbf777928f9404e235cfa3db6124d530261312a Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 21 Apr 2022 15:24:18 +1000 Subject: [PATCH 302/367] mm: Allow dynamically requesting additional memory regions Currently, all platforms will set up their heap on initialization of the platform code. While this works mostly fine, it poses some limitations on memory management on us. Most notably, allocating big chunks of memory in the gigabyte range would require us to pre-request this many bytes from the firmware and add it to the heap from the beginning on some platforms like EFI. As this isn't needed for most configurations, it is inefficient and may even negatively impact some usecases when, e.g., chainloading. Nonetheless, allocating big chunks of memory is required sometimes, where one example is the upcoming support for the Argon2 key derival function in LUKS2. In order to avoid pre-allocating big chunks of memory, this commit implements a runtime mechanism to add more pages to the system. When a given allocation cannot be currently satisfied, we'll call a given callback set up by the platform's own memory management subsystem, asking it to add a memory area with at least "n" bytes. If this succeeds, we retry searching for a valid memory region, which should now succeed. If this fails, we try asking for "n" bytes, possibly spread across multiple regions, in hopes that region merging means that we end up with enough memory for things to work out. Signed-off-by: Patrick Steinhardt Signed-off-by: Daniel Axtens Tested-by: Stefan Berger Reviewed-by: Daniel Kiper Tested-by: Patrick Steinhardt (cherry picked from commit 887f98f0db43e33fba4ec1f85e42fae1185700bc) --- grub-core/kern/mm.c | 30 ++++++++++++++++++++++++++++++ include/grub/mm.h | 18 ++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c index 1825dc8289..f2e27f263b 100644 --- a/grub-core/kern/mm.c +++ b/grub-core/kern/mm.c @@ -28,6 +28,9 @@ - multiple regions may be used as free space. They may not be contiguous. + - if existing regions are insufficient to satisfy an allocation, a new + region can be requested from firmware. + Regions are managed by a singly linked list, and the meta information is stored in the beginning of each region. Space after the meta information is used to allocate memory. @@ -81,6 +84,7 @@ grub_mm_region_t grub_mm_base; +grub_mm_add_region_func_t grub_mm_add_region_fn; /* Get a header from the pointer PTR, and set *P and *R to a pointer to the header and a pointer to its region, respectively. PTR must @@ -444,6 +448,32 @@ grub_memalign (grub_size_t align, grub_size_t size) count++; goto again; + case 1: + /* Request additional pages, contiguous */ + count++; + + if (grub_mm_add_region_fn != NULL && + grub_mm_add_region_fn (size, GRUB_MM_ADD_REGION_CONSECUTIVE) == GRUB_ERR_NONE) + goto again; + + /* fallthrough */ + + case 2: + /* Request additional pages, anything at all */ + count++; + + if (grub_mm_add_region_fn != NULL) + { + /* + * Try again even if this fails, in case it was able to partially + * satisfy the request + */ + grub_mm_add_region_fn (size, GRUB_MM_ADD_REGION_NONE); + goto again; + } + + /* fallthrough */ + default: break; } diff --git a/include/grub/mm.h b/include/grub/mm.h index d81623d226..7c6f925ffd 100644 --- a/include/grub/mm.h +++ b/include/grub/mm.h @@ -20,6 +20,7 @@ #ifndef GRUB_MM_H #define GRUB_MM_H 1 +#include #include #include #include @@ -29,6 +30,23 @@ # define NULL ((void *) 0) #endif +#define GRUB_MM_ADD_REGION_NONE 0 +#define GRUB_MM_ADD_REGION_CONSECUTIVE (1 << 0) + +/* + * Function used to request memory regions of `grub_size_t` bytes. The second + * parameter is a bitfield of `GRUB_MM_ADD_REGION` flags. + */ +typedef grub_err_t (*grub_mm_add_region_func_t) (grub_size_t, unsigned int); + +/* + * Set this function pointer to enable adding memory-regions at runtime in case + * a memory allocation cannot be satisfied with existing regions. + */ +#ifndef GRUB_MACHINE_EMU +extern grub_mm_add_region_func_t EXPORT_VAR(grub_mm_add_region_fn); +#endif + void grub_mm_init_region (void *addr, grub_size_t size); void *EXPORT_FUNC(grub_calloc) (grub_size_t nmemb, grub_size_t size); void *EXPORT_FUNC(grub_malloc) (grub_size_t size); From 65f6aaa8d2bb91d31611ccfb989853fe9e8b0837 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 21 Apr 2022 15:24:19 +1000 Subject: [PATCH 303/367] kern/efi/mm: Always request a fixed number of pages on init When initializing the EFI memory subsystem, we will by default request a quarter of the available memory, bounded by a minimum/maximum value. Given that we're about to extend the EFI memory system to dynamically request additional pages from the firmware as required, this scaling of requested memory based on available memory will not make a lot of sense anymore. Remove this logic as a preparatory patch such that we'll instead defer to the runtime memory allocator. Note that ideally, we'd want to change this after dynamic requesting of pages has been implemented for the EFI platform. But because we'll need to split up initialization of the memory subsystem and the request of pages from the firmware, we'd have to duplicate quite some logic at first only to remove it afterwards again. This seems quite pointless, so we instead have patches slightly out of order. Signed-off-by: Patrick Steinhardt Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper Tested-by: Patrick Steinhardt (cherry picked from commit 938c3760b8c0fca759140be48307179b50107ff6) --- grub-core/kern/efi/mm.c | 35 +++-------------------------------- 1 file changed, 3 insertions(+), 32 deletions(-) diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index e460b072e6..782a1365a1 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -38,9 +38,8 @@ a multiplier of 4KB. */ #define MEMORY_MAP_SIZE 0x3000 -/* The minimum and maximum heap size for GRUB itself. */ -#define MIN_HEAP_SIZE 0x100000 -#define MAX_HEAP_SIZE (1600 * 0x100000) +/* The default heap size for GRUB itself in bytes. */ +#define DEFAULT_HEAP_SIZE 0x100000 static void *finish_mmap_buf = 0; static grub_efi_uintn_t finish_mmap_size = 0; @@ -514,23 +513,6 @@ filter_memory_map (grub_efi_memory_descriptor_t *memory_map, return filtered_desc; } -/* Return the total number of pages. */ -static grub_efi_uint64_t -get_total_pages (grub_efi_memory_descriptor_t *memory_map, - grub_efi_uintn_t desc_size, - grub_efi_memory_descriptor_t *memory_map_end) -{ - grub_efi_memory_descriptor_t *desc; - grub_efi_uint64_t total = 0; - - for (desc = memory_map; - desc < memory_map_end; - desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size)) - total += desc->num_pages; - - return total; -} - /* Add memory regions. */ static void add_memory_regions (grub_efi_memory_descriptor_t *memory_map, @@ -694,8 +676,6 @@ grub_efi_mm_init (void) grub_efi_memory_descriptor_t *filtered_memory_map_end; grub_efi_uintn_t map_size; grub_efi_uintn_t desc_size; - grub_efi_uint64_t total_pages; - grub_efi_uint64_t required_pages; int mm_status; grub_nx_init (); @@ -737,22 +717,13 @@ grub_efi_mm_init (void) filtered_memory_map_end = filter_memory_map (memory_map, filtered_memory_map, desc_size, memory_map_end); - /* By default, request a quarter of the available memory. */ - total_pages = get_total_pages (filtered_memory_map, desc_size, - filtered_memory_map_end); - required_pages = (total_pages >> 2); - if (required_pages < BYTES_TO_PAGES (MIN_HEAP_SIZE)) - required_pages = BYTES_TO_PAGES (MIN_HEAP_SIZE); - else if (required_pages > BYTES_TO_PAGES (MAX_HEAP_SIZE)) - required_pages = BYTES_TO_PAGES (MAX_HEAP_SIZE); - /* Sort the filtered descriptors, so that GRUB can allocate pages from smaller regions. */ sort_memory_map (filtered_memory_map, desc_size, filtered_memory_map_end); /* Allocate memory regions for GRUB's memory management. */ add_memory_regions (filtered_memory_map, desc_size, - filtered_memory_map_end, required_pages); + filtered_memory_map_end, BYTES_TO_PAGES (DEFAULT_HEAP_SIZE)); #if 0 /* For debug. */ From 8d87a6ff257979895acdb5c88c9cc36827fa6711 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 21 Apr 2022 15:24:20 +1000 Subject: [PATCH 304/367] kern/efi/mm: Extract function to add memory regions In preparation of support for runtime-allocating additional memory region, this patch extracts the function to retrieve the EFI memory map and add a subset of it to GRUB's own memory regions. Signed-off-by: Patrick Steinhardt Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper Tested-by: Patrick Steinhardt (cherry picked from commit 96a7ea29e3cb61b6c2302e260e8e6a6117e17fa3) [rharwood: backport around our nx] --- grub-core/kern/efi/mm.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index 782a1365a1..a1d3b51fe6 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -667,8 +667,8 @@ grub_nx_init (void) } } -void -grub_efi_mm_init (void) +static grub_err_t +grub_efi_mm_add_regions (grub_size_t required_bytes) { grub_efi_memory_descriptor_t *memory_map; grub_efi_memory_descriptor_t *memory_map_end; @@ -683,7 +683,7 @@ grub_efi_mm_init (void) /* Prepare a memory region to store two memory maps. */ memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE)); if (! memory_map) - grub_fatal ("cannot allocate memory"); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory for memory map"); /* Obtain descriptors for available memory. */ map_size = MEMORY_MAP_SIZE; @@ -701,14 +701,14 @@ grub_efi_mm_init (void) memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (map_size)); if (! memory_map) - grub_fatal ("cannot allocate memory"); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory for new memory map"); mm_status = grub_efi_get_memory_map (&map_size, memory_map, 0, &desc_size, 0); } if (mm_status < 0) - grub_fatal ("cannot get memory map"); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "error fetching memory map from EFI"); memory_map_end = NEXT_MEMORY_DESCRIPTOR (memory_map, map_size); @@ -723,7 +723,7 @@ grub_efi_mm_init (void) /* Allocate memory regions for GRUB's memory management. */ add_memory_regions (filtered_memory_map, desc_size, - filtered_memory_map_end, BYTES_TO_PAGES (DEFAULT_HEAP_SIZE)); + filtered_memory_map_end, BYTES_TO_PAGES (required_bytes)); #if 0 /* For debug. */ @@ -741,6 +741,15 @@ grub_efi_mm_init (void) /* Release the memory maps. */ grub_efi_free_pages ((grub_addr_t) memory_map, 2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE)); + + return GRUB_ERR_NONE; +} + +void +grub_efi_mm_init (void) +{ + if (grub_efi_mm_add_regions (DEFAULT_HEAP_SIZE) != GRUB_ERR_NONE) + grub_fatal ("%s", grub_errmsg); } #if defined (__aarch64__) || defined (__arm__) || defined (__riscv) From 47bd9393d8603441635f4cfc8bc7f36be1d5ac70 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 21 Apr 2022 15:24:21 +1000 Subject: [PATCH 305/367] kern/efi/mm: Pass up errors from add_memory_regions() The function add_memory_regions() is currently only called on system initialization to allocate a fixed amount of pages. As such, it didn't need to return any errors: in case it failed, we cannot proceed anyway. This will change with the upcoming support for requesting more memory from the firmware at runtime, where it doesn't make sense anymore to fail hard. Refactor the function to return an error to prepare for this. Note that this does not change the behaviour when initializing the memory system because grub_efi_mm_init() knows to call grub_fatal() in case grub_efi_mm_add_regions() returns an error. Signed-off-by: Patrick Steinhardt Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper Tested-by: Patrick Steinhardt (cherry picked from commit 15a015698921240adc1ac266a3b5bc5fcbd81521) --- grub-core/kern/efi/mm.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index a1d3b51fe6..e0ebc65dba 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -514,7 +514,7 @@ filter_memory_map (grub_efi_memory_descriptor_t *memory_map, } /* Add memory regions. */ -static void +static grub_err_t add_memory_regions (grub_efi_memory_descriptor_t *memory_map, grub_efi_uintn_t desc_size, grub_efi_memory_descriptor_t *memory_map_end, @@ -542,9 +542,9 @@ add_memory_regions (grub_efi_memory_descriptor_t *memory_map, GRUB_EFI_ALLOCATE_ADDRESS, GRUB_EFI_LOADER_CODE); if (! addr) - grub_fatal ("cannot allocate conventional memory %p with %u pages", - (void *) ((grub_addr_t) start), - (unsigned) pages); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Memory starting at %p (%u pages) marked as free, but EFI would not allocate", + (void *) ((grub_addr_t) start), (unsigned) pages); grub_mm_init_region (addr, PAGES_TO_BYTES (pages)); @@ -554,7 +554,11 @@ add_memory_regions (grub_efi_memory_descriptor_t *memory_map, } if (required_pages > 0) - grub_fatal ("too little memory"); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not allocate all requested memory: %" PRIuGRUB_UINT64_T " pages still required after iterating EFI memory map", + required_pages); + + return GRUB_ERR_NONE; } void @@ -676,6 +680,7 @@ grub_efi_mm_add_regions (grub_size_t required_bytes) grub_efi_memory_descriptor_t *filtered_memory_map_end; grub_efi_uintn_t map_size; grub_efi_uintn_t desc_size; + grub_err_t err; int mm_status; grub_nx_init (); @@ -722,8 +727,11 @@ grub_efi_mm_add_regions (grub_size_t required_bytes) sort_memory_map (filtered_memory_map, desc_size, filtered_memory_map_end); /* Allocate memory regions for GRUB's memory management. */ - add_memory_regions (filtered_memory_map, desc_size, - filtered_memory_map_end, BYTES_TO_PAGES (required_bytes)); + err = add_memory_regions (filtered_memory_map, desc_size, + filtered_memory_map_end, + BYTES_TO_PAGES (required_bytes)); + if (err != GRUB_ERR_NONE) + return err; #if 0 /* For debug. */ From e74c93ab3d237f14951fc67087f72503295ba096 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 21 Apr 2022 15:24:22 +1000 Subject: [PATCH 306/367] kern/efi/mm: Implement runtime addition of pages Adjust the interface of grub_efi_mm_add_regions() to take a set of GRUB_MM_ADD_REGION_* flags, which most notably is currently only the GRUB_MM_ADD_REGION_CONSECUTIVE flag. This allows us to set the function up as callback for the memory subsystem and have it call out to us in case there's not enough pages available in the current heap. Signed-off-by: Patrick Steinhardt Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper Tested-by: Patrick Steinhardt (cherry picked from commit 1df2934822df4c1170dde069d97cfbf7a9572bba) --- grub-core/kern/efi/mm.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index e0ebc65dba..016ba6cf2f 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -518,7 +518,8 @@ static grub_err_t add_memory_regions (grub_efi_memory_descriptor_t *memory_map, grub_efi_uintn_t desc_size, grub_efi_memory_descriptor_t *memory_map_end, - grub_efi_uint64_t required_pages) + grub_efi_uint64_t required_pages, + unsigned int flags) { grub_efi_memory_descriptor_t *desc; @@ -532,6 +533,10 @@ add_memory_regions (grub_efi_memory_descriptor_t *memory_map, start = desc->physical_start; pages = desc->num_pages; + + if (pages < required_pages && (flags & GRUB_MM_ADD_REGION_CONSECUTIVE)) + continue; + if (pages > required_pages) { start += PAGES_TO_BYTES (pages - required_pages); @@ -672,7 +677,7 @@ grub_nx_init (void) } static grub_err_t -grub_efi_mm_add_regions (grub_size_t required_bytes) +grub_efi_mm_add_regions (grub_size_t required_bytes, unsigned int flags) { grub_efi_memory_descriptor_t *memory_map; grub_efi_memory_descriptor_t *memory_map_end; @@ -729,7 +734,8 @@ grub_efi_mm_add_regions (grub_size_t required_bytes) /* Allocate memory regions for GRUB's memory management. */ err = add_memory_regions (filtered_memory_map, desc_size, filtered_memory_map_end, - BYTES_TO_PAGES (required_bytes)); + BYTES_TO_PAGES (required_bytes), + flags); if (err != GRUB_ERR_NONE) return err; @@ -756,8 +762,9 @@ grub_efi_mm_add_regions (grub_size_t required_bytes) void grub_efi_mm_init (void) { - if (grub_efi_mm_add_regions (DEFAULT_HEAP_SIZE) != GRUB_ERR_NONE) + if (grub_efi_mm_add_regions (DEFAULT_HEAP_SIZE, GRUB_MM_ADD_REGION_NONE) != GRUB_ERR_NONE) grub_fatal ("%s", grub_errmsg); + grub_mm_add_region_fn = grub_efi_mm_add_regions; } #if defined (__aarch64__) || defined (__arm__) || defined (__riscv) From a1d4228230589767d7ea9a96afe450befc0de88a Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 20 Sep 2022 00:30:30 +1000 Subject: [PATCH 307/367] efi: Increase default memory allocation to 32 MiB We have multiple reports of things being slower with a 1 MiB initial static allocation, and a report (more difficult to nail down) of a boot failure as a result of the smaller initial allocation. Make the initial memory allocation 32 MiB. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit 75e38e86e7d9202f050b093f20500d9ad4c6dad9) --- grub-core/kern/efi/mm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index 016ba6cf2f..b27e966e1f 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -39,7 +39,7 @@ #define MEMORY_MAP_SIZE 0x3000 /* The default heap size for GRUB itself in bytes. */ -#define DEFAULT_HEAP_SIZE 0x100000 +#define DEFAULT_HEAP_SIZE 0x2000000 static void *finish_mmap_buf = 0; static grub_efi_uintn_t finish_mmap_size = 0; From 477fa2a08d9aeba1617d12a9d02c2fbe4c19bb13 Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Sat, 15 Oct 2022 22:15:11 +0800 Subject: [PATCH 308/367] mm: Try invalidate disk caches last when out of memory Every heap grow will cause all disk caches invalidated which decreases performance severely. This patch moves disk cache invalidation code to the last of memory squeezing measures. So, disk caches are released only when there are no other ways to get free memory. Signed-off-by: Zhang Boyang Reviewed-by: Daniel Kiper Reviewed-by: Patrick Steinhardt (cherry picked from commit 17975d10a80e2457e5237f87fa58a7943031983e) --- grub-core/kern/mm.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c index f2e27f263b..da1ac9427c 100644 --- a/grub-core/kern/mm.c +++ b/grub-core/kern/mm.c @@ -443,12 +443,6 @@ grub_memalign (grub_size_t align, grub_size_t size) switch (count) { case 0: - /* Invalidate disk caches. */ - grub_disk_cache_invalidate_all (); - count++; - goto again; - - case 1: /* Request additional pages, contiguous */ count++; @@ -458,7 +452,7 @@ grub_memalign (grub_size_t align, grub_size_t size) /* fallthrough */ - case 2: + case 1: /* Request additional pages, anything at all */ count++; @@ -474,6 +468,12 @@ grub_memalign (grub_size_t align, grub_size_t size) /* fallthrough */ + case 2: + /* Invalidate disk caches. */ + grub_disk_cache_invalidate_all (); + count++; + goto again; + default: break; } From de735a453aa3573fa9ca45c24b0f619cef5bf0d5 Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Wed, 25 Jan 2023 16:10:58 -0500 Subject: [PATCH 309/367] ppc64le: signed boot media changes Skip mdraid < 1.1 on isos since mdraid* can't even Prior to this change, on ppc64le with part_msdos and the mdraid* modules enabled, we see: disk/diskfilter.c:191: scanning ieee1275/cdrom kern/disk.c:196: Opening `ieee1275/cdrom'... disk/ieee1275/ofdisk.c:477: Opening `cdrom'. disk/ieee1275/ofdisk.c:502: MAX_RETRIES set to 20 kern/disk.c:288: Opening `ieee1275/cdrom' succeeded. disk/diskfilter.c:136: Scanning for DISKFILTER devices on disk ieee1275/cdrom partmap/msdos.c:184: partition 0: flag 0x80, type 0x96, start 0x0, len 0x6a5d70 disk/diskfilter.c:136: Scanning for DISKFILTER devices on disk ieee1275/cdrom SCSI-DISK: Access beyond end of device ! SCSI-DISK: Access beyond end of device ! SCSI-DISK: Access beyond end of device ! SCSI-DISK: Access beyond end of device ! SCSI-DISK: Access beyond end of device ! disk/ieee1275/ofdisk.c:578: MAX_RETRIES set to 20 These latter two lines repeat many times, eventually ending in: kern/disk.c:388: ieee1275/cdrom read failed error: ../../grub-core/disk/ieee1275/ofdisk.c:608:failure reading sector 0x1a9720 from `ieee1275/cdrom'. and the system drops to a "grub>" prompt. Prior to 1.1, mdraid stored the superblock offset from the end of the disk, and the firmware really doesn't like reads there. Best guess was that the firmware and the iso image appear to diagree on the blocksize (512 vs. 2048), and the diskfilter RAID probing is too much for it. It's tempting to just skip probing for cdroms, but unfortunately isos can be virtualized elsewhere - such as regular disks. Also fix detection of root, and try the chrp path as a fallback if the built prefix doesn't work. Signed-off-by: Robbie Harwood wip --- grub-core/disk/mdraid1x_linux.c | 8 +++++++- grub-core/disk/mdraid_linux.c | 5 +++++ grub-core/kern/ieee1275/openfw.c | 2 +- grub-core/normal/main.c | 5 +++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/grub-core/disk/mdraid1x_linux.c b/grub-core/disk/mdraid1x_linux.c index 38444b02c7..08c57ae16e 100644 --- a/grub-core/disk/mdraid1x_linux.c +++ b/grub-core/disk/mdraid1x_linux.c @@ -129,7 +129,13 @@ grub_mdraid_detect (grub_disk_t disk, grub_uint32_t level; struct grub_diskfilter_vg *array; char *uuid; - + +#ifdef __powerpc__ + /* Firmware will yell at us for reading too far. */ + if (minor_version == 0) + continue; +#endif + if (size == GRUB_DISK_SIZE_UNKNOWN && minor_version == 0) continue; diff --git a/grub-core/disk/mdraid_linux.c b/grub-core/disk/mdraid_linux.c index e40216f511..98fcfb1be6 100644 --- a/grub-core/disk/mdraid_linux.c +++ b/grub-core/disk/mdraid_linux.c @@ -189,6 +189,11 @@ grub_mdraid_detect (grub_disk_t disk, grub_uint32_t level; struct grub_diskfilter_vg *ret; +#ifdef __powerpc__ + /* Firmware will yell at us for reading too far. */ + return NULL; +#endif + /* The sector where the mdraid 0.90 superblock is stored, if available. */ size = grub_disk_native_sectors (disk); if (size == GRUB_DISK_SIZE_UNKNOWN) diff --git a/grub-core/kern/ieee1275/openfw.c b/grub-core/kern/ieee1275/openfw.c index 3a6689abb1..0278054c61 100644 --- a/grub-core/kern/ieee1275/openfw.c +++ b/grub-core/kern/ieee1275/openfw.c @@ -499,7 +499,7 @@ grub_ieee1275_encode_devname (const char *path) *optr++ ='\\'; *optr++ = *iptr++; } - if (partition && partition[0]) + if (partition && partition[0] >= '0' && partition[0] <= '9') { unsigned int partno = grub_strtoul (partition, 0, 0); diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 8f5fd81003..d59145f861 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -372,6 +372,7 @@ grub_try_normal_prefix (const char *prefix) file = grub_file_open (config, GRUB_FILE_TYPE_CONFIG); if (file) { + grub_env_set ("prefix", prefix); grub_file_close (file); err = GRUB_ERR_NONE; } @@ -447,6 +448,10 @@ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)), err = grub_try_normal ("fw_path"); if (err == GRUB_ERR_FILE_NOT_FOUND) err = grub_try_normal ("prefix"); +#ifdef __powerpc__ + if (err == GRUB_ERR_FILE_NOT_FOUND) + err = grub_try_normal_prefix ("/boot/grub"); +#endif if (err == GRUB_ERR_FILE_NOT_FOUND) err = grub_try_normal_discover (); if (err == GRUB_ERR_FILE_NOT_FOUND) From f7d929ea8e0d49686bed0e3e448f76d0ef37fd33 Mon Sep 17 00:00:00 2001 From: Arjun Shankar Date: Sun, 5 Feb 2023 11:13:55 +0100 Subject: [PATCH 310/367] core: Fix several implicit function declarations These #include lines ensure that grub2 continues to build with C99 where implicit function declarations are removed. Related to: --- grub-core/commands/efi/connectefi.c | 1 + grub-core/net/http.c | 1 + grub-core/term/at_keyboard.c | 1 + 3 files changed, 3 insertions(+) diff --git a/grub-core/commands/efi/connectefi.c b/grub-core/commands/efi/connectefi.c index 8ab75bd51b..3752ae17ed 100644 --- a/grub-core/commands/efi/connectefi.c +++ b/grub-core/commands/efi/connectefi.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/grub-core/net/http.c b/grub-core/net/http.c index 57d2721719..5f956b743e 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -26,6 +26,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); diff --git a/grub-core/term/at_keyboard.c b/grub-core/term/at_keyboard.c index dac0f946fe..de3e4abe44 100644 --- a/grub-core/term/at_keyboard.c +++ b/grub-core/term/at_keyboard.c @@ -25,6 +25,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); From ba26c2deef3e3761d4e8c31c33b2905627525747 Mon Sep 17 00:00:00 2001 From: Raymund Will Date: Mon, 24 Oct 2022 14:33:50 -0400 Subject: [PATCH 311/367] loader: Add support for grub-emu to kexec Linux menu entries The GRUB emulator is used as a debugging utility but it could also be used as a user-space bootloader if there is support to boot an operating system. The Linux kernel is already able to (re)boot another kernel via the kexec boot mechanism. So the grub-emu tool could rely on this feature and have linux and initrd commands that are used to pass a kernel, initramfs image and command line parameters to kexec for booting a selected menu entry. By default the systemctl kexec option is used so systemd can shutdown all of the running services before doing a reboot using kexec. But if this is not present, it can fall back to executing the kexec user-space tool directly. The ability to force a kexec-reboot when systemctl kexec fails must only be used in controlled environments to avoid possible filesystem corruption and data loss. Signed-off-by: Raymund Will Signed-off-by: John Jolly Signed-off-by: Javier Martinez Canillas Signed-off-by: Robbie Harwood Reviewed-by: Daniel Kiper (cherry picked from commit e364307f6acc2f631b4c1fefda0791b9ce1f205f) [rharwood: conflicts around makefile and grub_exit return code] --- docs/grub.texi | 30 ++++-- grub-core/Makefile.am | 1 + grub-core/Makefile.core.def | 3 - grub-core/kern/emu/main.c | 4 + grub-core/kern/emu/misc.c | 18 +++- grub-core/loader/emu/linux.c | 178 +++++++++++++++++++++++++++++++++++ include/grub/emu/exec.h | 4 +- include/grub/emu/hostfile.h | 3 +- include/grub/emu/misc.h | 3 + 9 files changed, 230 insertions(+), 14 deletions(-) create mode 100644 grub-core/loader/emu/linux.c diff --git a/docs/grub.texi b/docs/grub.texi index a4da9c2a1b..1750b72ee9 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -923,17 +923,17 @@ magic. @node General boot methods @section How to boot operating systems -GRUB has two distinct boot methods. One of the two is to load an -operating system directly, and the other is to chain-load another boot -loader which then will load an operating system actually. Generally -speaking, the former is more desirable, because you don't need to -install or maintain other boot loaders and GRUB is flexible enough to -load an operating system from an arbitrary disk/partition. However, -the latter is sometimes required, since GRUB doesn't support all the -existing operating systems natively. +GRUB has three distinct boot methods: loading an operating system +directly, using kexec from userspace, and chainloading another +bootloader. Generally speaking, the first two are more desirable +because you don't need to install or maintain other boot loaders and +GRUB is flexible enough to load an operating system from an arbitrary +disk/partition. However, chainloading is sometimes required, as GRUB +doesn't support all existing operating systems natively. @menu * Loading an operating system directly:: +* Kexec:: * Chain-loading:: @end menu @@ -959,6 +959,20 @@ use more complicated instructions. @xref{DOS/Windows}, for more information. +@node Kexec +@subsection Kexec with grub2-emu + +GRUB can be run in userspace by invoking the grub2-emu tool. It will +read all configuration scripts as if booting directly (see @xref{Loading +an operating system directly}). With the @code{--kexec} flag, and +kexec(8) support from the operating system, the @command{linux} command +will directly boot the target image. For systems that lack working +systemctl(1) support for kexec, passing the @code{--kexec} flag twice +will fallback to invoking kexec(8) directly; note however that this +fallback may be unsafe outside read-only environments, as it does not +invoke shutdown machinery. + + @node Chain-loading @subsection Chain-loading an OS diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index c2e8a82bce..dd49939aaa 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -309,6 +309,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/net.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/hostdisk.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/hostfile.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/exec.h if COND_GRUB_EMU_SDL KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/sdl.h endif diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 741a033978..f21da23213 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1864,11 +1864,8 @@ module = { riscv32 = loader/riscv/linux.c; riscv64 = loader/riscv/linux.c; emu = loader/emu/linux.c; - common = loader/linux.c; common = lib/cmdline.c; - enable = noemu; - efi = loader/efi/linux.c; }; diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c index 12277c34d2..68e2b283bb 100644 --- a/grub-core/kern/emu/main.c +++ b/grub-core/kern/emu/main.c @@ -107,6 +107,7 @@ static struct argp_option options[] = { N_("use GRUB files in the directory DIR [default=%s]"), 0}, {"verbose", 'v', 0, 0, N_("print verbose messages."), 0}, {"hold", 'H', N_("SECS"), OPTION_ARG_OPTIONAL, N_("wait until a debugger will attach"), 0}, + {"kexec", 'X', 0, 0, N_("use kexec to boot Linux kernels via systemctl (pass twice to enable dangerous fallback to non-systemctl)."), 0}, { 0, 0, 0, 0, 0, 0 } }; @@ -164,6 +165,9 @@ argp_parser (int key, char *arg, struct argp_state *state) case 'v': verbosity++; break; + case 'X': + grub_util_set_kexecute (); + break; case ARGP_KEY_ARG: { diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c index d278c2921f..02d27c3440 100644 --- a/grub-core/kern/emu/misc.c +++ b/grub-core/kern/emu/misc.c @@ -39,6 +39,7 @@ #include int verbosity; +int kexecute; void grub_util_warn (const char *fmt, ...) @@ -82,7 +83,7 @@ grub_util_error (const char *fmt, ...) vfprintf (stderr, fmt, ap); va_end (ap); fprintf (stderr, ".\n"); - exit (1); + grub_exit (1); } void * @@ -154,6 +155,9 @@ void __attribute__ ((noreturn)) grub_exit (int rc) { +#if defined (GRUB_KERNEL) + grub_reboot (); +#endif exit (rc < 0 ? 1 : rc); } #endif @@ -215,3 +219,15 @@ grub_util_load_image (const char *path, char *buf) fclose (fp); } + +void +grub_util_set_kexecute (void) +{ + kexecute++; +} + +int +grub_util_get_kexecute (void) +{ + return kexecute; +} diff --git a/grub-core/loader/emu/linux.c b/grub-core/loader/emu/linux.c new file mode 100644 index 0000000000..0cf378a376 --- /dev/null +++ b/grub-core/loader/emu/linux.c @@ -0,0 +1,178 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_dl_t my_mod; + +static char *kernel_path; +static char *initrd_path; +static char *boot_cmdline; + +static grub_err_t +grub_linux_boot (void) +{ + grub_err_t rc = GRUB_ERR_NONE; + char *initrd_param; + const char *kexec[] = {"kexec", "-la", kernel_path, boot_cmdline, NULL, NULL}; + const char *systemctl[] = {"systemctl", "kexec", NULL}; + int kexecute = grub_util_get_kexecute (); + + if (initrd_path) + { + initrd_param = grub_xasprintf ("--initrd=%s", initrd_path); + kexec[3] = initrd_param; + kexec[4] = boot_cmdline; + } + else + initrd_param = grub_xasprintf ("%s", ""); + + grub_dprintf ("linux", "%serforming 'kexec -la %s %s %s'\n", + (kexecute) ? "P" : "Not p", + kernel_path, initrd_param, boot_cmdline); + + if (kexecute) + rc = grub_util_exec (kexec); + + grub_free (initrd_param); + + if (rc != GRUB_ERR_NONE) + { + grub_error (rc, N_("error trying to perform kexec load operation")); + grub_sleep (3); + return rc; + } + + if (kexecute < 1) + grub_fatal (N_("use '"PACKAGE"-emu --kexec' to force a system restart")); + + grub_dprintf ("linux", "Performing 'systemctl kexec' (%s) ", + (kexecute==1) ? "do-or-die" : "just-in-case"); + rc = grub_util_exec (systemctl); + + if (kexecute == 1) + grub_fatal (N_("error trying to perform 'systemctl kexec': %d"), rc); + + /* + * WARNING: forcible reset should only be used in read-only environments. + * grub-emu cannot check for these - users beware. + */ + grub_dprintf ("linux", "Performing 'kexec -ex'"); + kexec[1] = "-ex"; + kexec[2] = NULL; + rc = grub_util_exec (kexec); + if (rc != GRUB_ERR_NONE) + grub_fatal (N_("error trying to directly perform 'kexec -ex': %d"), rc); + + return rc; +} + +static grub_err_t +grub_linux_unload (void) +{ + /* Unloading: we're no longer in use. */ + grub_dl_unref (my_mod); + grub_free (boot_cmdline); + boot_cmdline = NULL; + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), int argc, + char *argv[]) +{ + int i; + char *tempstr; + + /* Mark ourselves as in-use. */ + grub_dl_ref (my_mod); + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + if (!grub_util_is_regular (argv[0])) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, + N_("cannot find kernel file %s"), argv[0]); + + grub_free (kernel_path); + kernel_path = grub_xasprintf ("%s", argv[0]); + + grub_free (boot_cmdline); + boot_cmdline = NULL; + + if (argc > 1) + { + boot_cmdline = grub_xasprintf ("--command-line=%s", argv[1]); + for (i = 2; i < argc; i++) + { + tempstr = grub_xasprintf ("%s %s", boot_cmdline, argv[i]); + grub_free (boot_cmdline); + boot_cmdline = tempstr; + } + } + + grub_loader_set (grub_linux_boot, grub_linux_unload, 0); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), int argc, + char *argv[]) +{ + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + if (!grub_util_is_regular (argv[0])) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, + N_("Cannot find initrd file %s"), argv[0]); + + grub_free (initrd_path); + initrd_path = grub_xasprintf ("%s", argv[0]); + + /* We are done - mark ourselves as on longer in use. */ + grub_dl_unref (my_mod); + + return GRUB_ERR_NONE; +} + +static grub_command_t cmd_linux, cmd_initrd; + +GRUB_MOD_INIT (linux) +{ + cmd_linux = grub_register_command ("linux", grub_cmd_linux, 0, + N_("Load Linux.")); + cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, 0, + N_("Load initrd.")); + my_mod = mod; +} + +GRUB_MOD_FINI (linux) +{ + grub_unregister_command (cmd_linux); + grub_unregister_command (cmd_initrd); +} diff --git a/include/grub/emu/exec.h b/include/grub/emu/exec.h index d1073ef86a..1b61b4a2e5 100644 --- a/include/grub/emu/exec.h +++ b/include/grub/emu/exec.h @@ -23,6 +23,8 @@ #include #include +#include + pid_t grub_util_exec_pipe (const char *const *argv, int *fd); pid_t @@ -32,7 +34,7 @@ int grub_util_exec_redirect_all (const char *const *argv, const char *stdin_file, const char *stdout_file, const char *stderr_file); int -grub_util_exec (const char *const *argv); +EXPORT_FUNC(grub_util_exec) (const char *const *argv); int grub_util_exec_redirect (const char *const *argv, const char *stdin_file, const char *stdout_file); diff --git a/include/grub/emu/hostfile.h b/include/grub/emu/hostfile.h index cfb1e2b566..a61568e36e 100644 --- a/include/grub/emu/hostfile.h +++ b/include/grub/emu/hostfile.h @@ -22,6 +22,7 @@ #include #include #include +#include #include int @@ -29,7 +30,7 @@ grub_util_is_directory (const char *path); int grub_util_is_special_file (const char *path); int -grub_util_is_regular (const char *path); +EXPORT_FUNC(grub_util_is_regular) (const char *path); char * grub_util_path_concat (size_t n, ...); diff --git a/include/grub/emu/misc.h b/include/grub/emu/misc.h index ff9c48a649..01056954b9 100644 --- a/include/grub/emu/misc.h +++ b/include/grub/emu/misc.h @@ -57,6 +57,9 @@ void EXPORT_FUNC(grub_util_warn) (const char *fmt, ...) __attribute__ ((format ( void EXPORT_FUNC(grub_util_info) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2))); void EXPORT_FUNC(grub_util_error) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2), noreturn)); +void EXPORT_FUNC(grub_util_set_kexecute) (void); +int EXPORT_FUNC(grub_util_get_kexecute) (void) WARN_UNUSED_RESULT; + grub_uint64_t EXPORT_FUNC (grub_util_get_cpu_time_ms) (void); #ifdef HAVE_DEVICE_MAPPER From 5434d57b6cf1812e1e10af881f13330f17a26871 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Mon, 6 Sep 2021 15:46:12 +1000 Subject: [PATCH 312/367] powerpc: Drop Open Hack'Ware - remove GRUB_IEEE1275_FLAG_FORCE_CLAIM Open Hack'Ware was the only user. It added a lot of complexity. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit 333e63b356f1ce833cda1937ed8351618cbdf9d3) --- grub-core/kern/ieee1275/init.c | 6 +----- grub-core/lib/ieee1275/relocator.c | 4 ---- grub-core/loader/powerpc/ieee1275/linux.c | 14 -------------- include/grub/ieee1275/ieee1275.h | 11 ----------- 4 files changed, 1 insertion(+), 34 deletions(-) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 0dcd114ce5..6581c2c996 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -207,11 +207,7 @@ grub_claim_heap (void) { unsigned long total = 0; - if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM)) - heap_init (GRUB_IEEE1275_STATIC_HEAP_START, GRUB_IEEE1275_STATIC_HEAP_LEN, - 1, &total); - else - grub_machine_mmap_iterate (heap_init, &total); + grub_machine_mmap_iterate (heap_init, &total); } #endif diff --git a/grub-core/lib/ieee1275/relocator.c b/grub-core/lib/ieee1275/relocator.c index c6dd8facb0..d1bb45c75e 100644 --- a/grub-core/lib/ieee1275/relocator.c +++ b/grub-core/lib/ieee1275/relocator.c @@ -38,8 +38,6 @@ grub_relocator_firmware_get_max_events (void) { int counter = 0; - if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM)) - return 0; grub_machine_mmap_iterate (count, &counter); return 2 * counter; } @@ -92,8 +90,6 @@ grub_relocator_firmware_fill_events (struct grub_relocator_mmap_event *events) .counter = 0 }; - if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM)) - return 0; grub_machine_mmap_iterate (grub_relocator_firmware_fill_events_iter, &ctx); return ctx.counter; } diff --git a/grub-core/loader/powerpc/ieee1275/linux.c b/grub-core/loader/powerpc/ieee1275/linux.c index 818b2a86d1..6fdd863130 100644 --- a/grub-core/loader/powerpc/ieee1275/linux.c +++ b/grub-core/loader/powerpc/ieee1275/linux.c @@ -111,20 +111,6 @@ grub_linux_claimmap_iterate (grub_addr_t target, grub_size_t size, .found_addr = (grub_addr_t) -1 }; - if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM)) - { - grub_uint64_t addr = target; - if (addr < GRUB_IEEE1275_STATIC_HEAP_START - + GRUB_IEEE1275_STATIC_HEAP_LEN) - addr = GRUB_IEEE1275_STATIC_HEAP_START - + GRUB_IEEE1275_STATIC_HEAP_LEN; - addr = ALIGN_UP (addr, align); - if (grub_claimmap (addr, size) == GRUB_ERR_NONE) - return addr; - return (grub_addr_t) -1; - } - - grub_machine_mmap_iterate (alloc_mem, &ctx); return ctx.found_addr; diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h index b5a1d49bbc..6a1d3e5d70 100644 --- a/include/grub/ieee1275/ieee1275.h +++ b/include/grub/ieee1275/ieee1275.h @@ -85,14 +85,6 @@ extern grub_ieee1275_ihandle_t EXPORT_VAR(grub_ieee1275_mmu); extern int (* EXPORT_VAR(grub_ieee1275_entry_fn)) (void *) GRUB_IEEE1275_ENTRY_FN_ATTRIBUTE; -/* Static heap, used only if FORCE_CLAIM is set, - happens on Open Hack'Ware. Should be in platform-specific - header but is used only on PPC anyway. -*/ -#define GRUB_IEEE1275_STATIC_HEAP_START 0x1000000 -#define GRUB_IEEE1275_STATIC_HEAP_LEN 0x1000000 - - enum grub_ieee1275_flag { /* Old World Macintosh firmware fails seek when "dev:0" is opened. */ @@ -119,9 +111,6 @@ enum grub_ieee1275_flag /* Open Hack'Ware stops when grub_ieee1275_interpret is used. */ GRUB_IEEE1275_FLAG_CANNOT_INTERPRET, - /* Open Hack'Ware has no memory map, just claim what we need. */ - GRUB_IEEE1275_FLAG_FORCE_CLAIM, - /* Open Hack'Ware don't support the ANSI sequence. */ GRUB_IEEE1275_FLAG_NO_ANSI, From e6503c3a10eb6173ee90239554fb1838d1ac9257 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Mon, 6 Feb 2023 10:03:20 -0500 Subject: [PATCH 313/367] ieee1275: request memory with ibm, client-architecture-support On PowerVM, the first time we boot a Linux partition, we may only get 256MB of real memory area, even if the partition has more memory. This isn't enough to reliably verify a kernel. Fortunately, the Power Architecture Platform Reference (PAPR) defines a method we can call to ask for more memory: the broad and powerful ibm,client-architecture-support (CAS) method. CAS can do an enormous amount of things on a PAPR platform: as well as asking for memory, you can set the supported processor level, the interrupt controller, hash vs radix mmu, and so on. If: - we are running under what we think is PowerVM (compatible property of / begins with "IBM"), and - the full amount of RMA is less than 512MB (as determined by the reg property of /memory) then call CAS as follows: (refer to the Linux on Power Architecture Reference, LoPAR, which is public, at B.5.2.3): - Use the "any" PVR value and supply 2 option vectors. - Set option vector 1 (PowerPC Server Processor Architecture Level) to "ignore". - Set option vector 2 with default or Linux-like options, including a min-rma-size of 512MB. - Set option vector 3 to request Floating Point, VMX and Decimal Floating point, but don't abort the boot if we can't get them. - Set option vector 4 to request a minimum VP percentage to 1%, which is what Linux requests, and is below the default of 10%. Without this, some systems with very large or very small configurations fail to boot. This will cause a CAS reboot and the partition will restart with 512MB of RMA. Importantly, grub will notice the 512MB and not call CAS again. Notes about the choices of parameters: - A partition can be configured with only 256MB of memory, which would mean this request couldn't be satisfied, but PFW refuses to load with only 256MB of memory, so it's a bit moot. SLOF will run fine with 256MB, but we will never call CAS under qemu/SLOF because /compatible won't begin with "IBM".) - unspecified CAS vectors take on default values. Some of these values might restrict the ability of certain hardware configurations to boot. This is why we need to specify the VP percentage in vector 4, which is in turn why we need to specify vector 3. Finally, we should have enough memory to verify a kernel, and we will reach Linux. One of the first things Linux does while still running under OpenFirmware is to call CAS with a much fuller set of options (including asking for 512MB of memory). Linux includes a much more restrictive set of PVR values and processor support levels, and this CAS invocation will likely induce another reboot. On this reboot grub will again notice the higher RMA, and not call CAS. We will get to Linux again, Linux will call CAS again, but because the values are now set for Linux this will not induce another CAS reboot and we will finally boot all the way to userspace. On all subsequent boots, everything will be configured with 512MB of RMA, so there will be no further CAS reboots from grub. (phyp is super sticky with the RMA size - it persists even on cold boots. So if you've ever booted Linux in a partition, you'll probably never have grub call CAS. It'll only ever fire the first time a partition loads grub, or if you deliberately lower the amount of memory your partition has below 512MB.) Signed-off-by: Daniel Axtens Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper (cherry picked from commit d5571590b7de61887efac1c298901455697ba307) --- grub-core/kern/ieee1275/cmain.c | 5 + grub-core/kern/ieee1275/init.c | 167 ++++++++++++++++++++++++++++++- include/grub/ieee1275/ieee1275.h | 12 ++- 3 files changed, 182 insertions(+), 2 deletions(-) diff --git a/grub-core/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c index 04df9d2c66..dce7b84922 100644 --- a/grub-core/kern/ieee1275/cmain.c +++ b/grub-core/kern/ieee1275/cmain.c @@ -127,6 +127,11 @@ grub_ieee1275_find_options (void) break; } } + +#if defined(__powerpc__) + if (grub_strncmp (tmp, "IBM,", 4) == 0) + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY); +#endif } if (is_smartfirmware) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 6581c2c996..8ae405bc79 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -202,11 +202,176 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, return 0; } -static void +/* + * How much memory does OF believe it has? (regardless of whether + * it's accessible or not) + */ +static grub_err_t +grub_ieee1275_total_mem (grub_uint64_t *total) +{ + grub_ieee1275_phandle_t root; + grub_ieee1275_phandle_t memory; + grub_uint32_t reg[4]; + grub_ssize_t reg_size; + grub_uint32_t address_cells = 1; + grub_uint32_t size_cells = 1; + grub_uint64_t size; + + /* If we fail to get to the end, report 0. */ + *total = 0; + + /* Determine the format of each entry in `reg'. */ + if (grub_ieee1275_finddevice ("/", &root)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't find / node"); + if (grub_ieee1275_get_integer_property (root, "#address-cells", &address_cells, + sizeof (address_cells), 0)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't examine #address-cells"); + if (grub_ieee1275_get_integer_property (root, "#size-cells", &size_cells, + sizeof (size_cells), 0)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't examine #size-cells"); + + if (size_cells > address_cells) + address_cells = size_cells; + + /* Load `/memory/reg'. */ + if (grub_ieee1275_finddevice ("/memory", &memory)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't find /memory node"); + if (grub_ieee1275_get_integer_property (memory, "reg", reg, + sizeof (reg), ®_size)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't examine /memory/reg property"); + if (reg_size < 0 || (grub_size_t) reg_size > sizeof (reg)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "/memory response buffer exceeded"); + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_ADDRESS_CELLS)) + { + address_cells = 1; + size_cells = 1; + } + + /* Decode only the size */ + size = reg[address_cells]; + if (size_cells == 2) + size = (size << 32) | reg[address_cells + 1]; + + *total = size; + + return grub_errno; +} + +#if defined(__powerpc__) + +/* See PAPR or arch/powerpc/kernel/prom_init.c */ +struct option_vector2 +{ + grub_uint8_t byte1; + grub_uint16_t reserved; + grub_uint32_t real_base; + grub_uint32_t real_size; + grub_uint32_t virt_base; + grub_uint32_t virt_size; + grub_uint32_t load_base; + grub_uint32_t min_rma; + grub_uint32_t min_load; + grub_uint8_t min_rma_percent; + grub_uint8_t max_pft_size; +} GRUB_PACKED; + +struct pvr_entry +{ + grub_uint32_t mask; + grub_uint32_t entry; +}; + +struct cas_vector +{ + struct + { + struct pvr_entry terminal; + } pvr_list; + grub_uint8_t num_vecs; + grub_uint8_t vec1_size; + grub_uint8_t vec1; + grub_uint8_t vec2_size; + struct option_vector2 vec2; + grub_uint8_t vec3_size; + grub_uint16_t vec3; + grub_uint8_t vec4_size; + grub_uint16_t vec4; +} GRUB_PACKED; + +/* + * Call ibm,client-architecture-support to try to get more RMA. + * We ask for 512MB which should be enough to verify a distro kernel. + * We ignore most errors: if we don't succeed we'll proceed with whatever + * memory we have. + */ +static void +grub_ieee1275_ibm_cas (void) +{ + int rc; + grub_ieee1275_ihandle_t root; + struct cas_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_ihandle_t ihandle; + grub_ieee1275_cell_t cas_addr; + grub_ieee1275_cell_t result; + } args; + struct cas_vector vector = + { + .pvr_list = { { 0x00000000, 0xffffffff } }, /* any processor */ + .num_vecs = 4 - 1, + .vec1_size = 0, + .vec1 = 0x80, /* ignore */ + .vec2_size = 1 + sizeof (struct option_vector2) - 2, + .vec2 = { + 0, 0, -1, -1, -1, -1, -1, 512, -1, 0, 48 + }, + .vec3_size = 2 - 1, + .vec3 = 0x00e0, /* ask for FP + VMX + DFP but don't halt if unsatisfied */ + .vec4_size = 2 - 1, + .vec4 = 0x0001, /* set required minimum capacity % to the lowest value */ + }; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 3, 2); + args.method = (grub_ieee1275_cell_t) "ibm,client-architecture-support"; + rc = grub_ieee1275_open ("/", &root); + if (rc) + { + grub_error (GRUB_ERR_IO, "could not open root when trying to call CAS"); + return; + } + args.ihandle = root; + args.cas_addr = (grub_ieee1275_cell_t) &vector; + + grub_printf ("Calling ibm,client-architecture-support from grub..."); + IEEE1275_CALL_ENTRY_FN (&args); + grub_printf ("done\n"); + + grub_ieee1275_close (root); +} + +#endif /* __powerpc__ */ + +static void grub_claim_heap (void) { unsigned long total = 0; +#if defined(__powerpc__) + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY)) + { + grub_uint64_t rma_size; + grub_err_t err; + + err = grub_ieee1275_total_mem (&rma_size); + /* if we have an error, don't call CAS, just hope for the best */ + if (err == GRUB_ERR_NONE && rma_size < (512 * 1024 * 1024)) + grub_ieee1275_ibm_cas (); + } +#endif + grub_machine_mmap_iterate (heap_init, &total); } #endif diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h index 6a1d3e5d70..560c968460 100644 --- a/include/grub/ieee1275/ieee1275.h +++ b/include/grub/ieee1275/ieee1275.h @@ -138,7 +138,17 @@ enum grub_ieee1275_flag GRUB_IEEE1275_FLAG_RAW_DEVNAMES, - GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT + GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT, + +#if defined(__powerpc__) + /* + * On PFW, the first time we boot a Linux partition, we may only get 256MB of + * real memory area, even if the partition has more memory. Set this flag if + * we think we're running under PFW. Then, if this flag is set, and the RMA is + * only 256MB in size, try asking for more with CAS. + */ + GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY, +#endif }; extern int EXPORT_FUNC(grub_ieee1275_test_flag) (enum grub_ieee1275_flag flag); From acedefbdd20b7184026ad95c8a4a4470002e8971 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Mon, 6 Feb 2023 10:03:21 -0500 Subject: [PATCH 314/367] ieee1275: drop len -= 1 quirk in heap_init This was apparently 'required by some firmware': commit dc9468500919 ("2007-02-12 Hollis Blanchard "). It's not clear what firmware that was, and what platform from 14 years ago which exhibited the bug then is still both in use and buggy now. It doesn't cause issues on qemu (mac99 or pseries) or under PFW for Power8. I don't have access to old Mac hardware, but if anyone feels especially strongly we can put it under some feature flag. I really want to disable it under pseries because it will mess with region merging. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit fc639d430297321ee4f77c5d2d698f698cec0dc7) --- grub-core/kern/ieee1275/init.c | 1 - 1 file changed, 1 deletion(-) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 8ae405bc79..c8d551759d 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -168,7 +168,6 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, addr = 0x180000; } } - len -= 1; /* Required for some firmware. */ /* Never exceed HEAP_MAX_SIZE */ if (*total + len > HEAP_MAX_SIZE) From 23971c4c4ae84b110207b76d4d2d13e416207d54 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Mon, 6 Feb 2023 10:03:22 -0500 Subject: [PATCH 315/367] ieee1275: support runtime memory claiming On powerpc-ieee1275, we are running out of memory trying to verify anything. This is because: - we have to load an entire file into memory to verify it. This is difficult to change with appended signatures. - We only have 32MB of heap. - Distro kernels are now often around 30MB. So we want to be able to claim more memory from OpenFirmware for our heap at runtime. There are some complications: - The grub mm code isn't the only thing that will make claims on memory from OpenFirmware: * PFW/SLOF will have claimed some for their own use. * The ieee1275 loader will try to find other bits of memory that we haven't claimed to place the kernel and initrd when we go to boot. * Once we load Linux, it will also try to claim memory. It claims memory without any reference to /memory/available, it just starts at min(top of RMO, 768MB) and works down. So we need to avoid this area. See arch/powerpc/kernel/prom_init.c as of v5.11. - The smallest amount of memory a ppc64 KVM guest can have is 256MB. It doesn't work with distro kernels but can work with custom kernels. We should maintain support for that. (ppc32 can boot with even less, and we shouldn't break that either.) - Even if a VM has more memory, the memory OpenFirmware makes available as Real Memory Area can be restricted. Even with our CAS work, an LPAR on a PowerVM box is likely to have only 512MB available to OpenFirmware even if it has many gigabytes of memory allocated. What should we do? We don't know in advance how big the kernel and initrd are going to be, which makes figuring out how much memory we can take a bit tricky. To figure out how much memory we should leave unused, I looked at: - an Ubuntu 20.04.1 ppc64le pseries KVM guest: vmlinux: ~30MB initrd: ~50MB - a RHEL8.2 ppc64le pseries KVM guest: vmlinux: ~30MB initrd: ~30MB So to give us a little wriggle room, I think we want to leave at least 128MB for the loader to put vmlinux and initrd in memory and leave Linux with space to satisfy its early allocations. Allow other space to be allocated at runtime. Tested-by: Stefan Berger Signed-off-by: Daniel Axtens (cherry picked from commit a5c710789ccdd27a84ae4a34c7d453bd585e2b66) [rharwood: _start?] --- docs/grub-dev.texi | 7 +- grub-core/kern/ieee1275/init.c | 270 ++++++++++++++++++++++++++++++--- 2 files changed, 257 insertions(+), 20 deletions(-) diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi index 7b2455a8fe..7edc5b7e2b 100644 --- a/docs/grub-dev.texi +++ b/docs/grub-dev.texi @@ -1047,7 +1047,10 @@ space is limited to 4GiB. GRUB allocates pages from EFI for its heap, at most 1.6 GiB. On i386-ieee1275 and powerpc-ieee1275 GRUB uses same stack as IEEE1275. -It allocates at most 32MiB for its heap. + +On i386-ieee1275 and powerpc-ieee1275, GRUB will allocate 32MiB for its heap on +startup. It may allocate more at runtime, as long as at least 128MiB remain free +in OpenFirmware. On sparc64-ieee1275 stack is 256KiB and heap is 2MiB. @@ -1075,7 +1078,7 @@ In short: @item i386-qemu @tab 60 KiB @tab < 4 GiB @item *-efi @tab ? @tab < 1.6 GiB @item i386-ieee1275 @tab ? @tab < 32 MiB -@item powerpc-ieee1275 @tab ? @tab < 32 MiB +@item powerpc-ieee1275 @tab ? @tab available memory - 128MiB @item sparc64-ieee1275 @tab 256KiB @tab 2 MiB @item arm-uboot @tab 256KiB @tab 2 MiB @item mips(el)-qemu_mips @tab 2MiB @tab 253 MiB diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index c8d551759d..85af8fa97b 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -46,13 +46,26 @@ #endif #include -/* The maximum heap size we're going to claim */ +/* The maximum heap size we're going to claim at boot. Not used by sparc. */ #ifdef __i386__ #define HEAP_MAX_SIZE (unsigned long) (64 * 1024 * 1024) -#else +#else /* __powerpc__ */ #define HEAP_MAX_SIZE (unsigned long) (32 * 1024 * 1024) #endif +/* RMO max. address at 768 MB */ +#define RMO_ADDR_MAX (grub_uint64_t) (768 * 1024 * 1024) + +/* + * The amount of OF space we will not claim here so as to leave space for + * the loader and linux to service early allocations. + * + * In 2021, Daniel Axtens claims that we should leave at least 128MB to + * ensure we can load a stock kernel and initrd on a pseries guest with + * a 512MB real memory area under PowerVM. + */ +#define RUNTIME_MIN_SPACE (128UL * 1024 * 1024) + extern char _end[]; #ifdef __sparc__ @@ -147,16 +160,52 @@ grub_claim_heap (void) + GRUB_KERNEL_MACHINE_STACK_SIZE), 0x200000); } #else -/* Helper for grub_claim_heap. */ +/* Helpers for mm on powerpc. */ + +/* + * How much memory does OF believe exists in total? + * + * This isn't necessarily the true total. It can be the total memory + * accessible in real mode for a pseries guest, for example. + */ +static grub_uint64_t rmo_top; + static int -heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, - void *data) +count_free (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, + void *data) { - unsigned long *total = data; + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + + /* Do not consider memory beyond 4GB */ + if (addr > 0xffffffffULL) + return 0; + + if (addr + len > 0xffffffffULL) + len = 0xffffffffULL - addr; + + *(grub_uint32_t *) data += len; + + return 0; +} + +static int +regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, + unsigned int flags, void *data) +{ + grub_uint32_t total = *(grub_uint32_t *) data; + grub_uint64_t linux_rmo_save; if (type != GRUB_MEMORY_AVAILABLE) return 0; + /* Do not consider memory beyond 4GB */ + if (addr > 0xffffffffULL) + return 0; + + if (addr + len > 0xffffffffULL) + len = 0xffffffffULL - addr; + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_PRE1_5M_CLAIM)) { if (addr + len <= 0x180000) @@ -169,10 +218,6 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, } } - /* Never exceed HEAP_MAX_SIZE */ - if (*total + len > HEAP_MAX_SIZE) - len = HEAP_MAX_SIZE - *total; - /* In theory, firmware should already prevent this from happening by not listing our own image in /memory/available. The check below is intended as a safeguard in case that doesn't happen. However, it doesn't protect @@ -184,6 +229,108 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, len = 0; } + /* + * Linux likes to claim memory at min(RMO top, 768MB) and works down + * without reference to /memory/available. (See prom_init.c::alloc_down) + * + * If this block contains min(RMO top, 768MB), do not claim below that for + * at least a few MB (this is where RTAS, SML and potentially TCEs live). + * + * We also need to leave enough space for the DT in the RMA. (See + * prom_init.c::alloc_up) + * + * Finally, we also want to make sure that when grub loads the kernel, + * it isn't going to use up all the memory we're trying to reserve! So + * enforce our entire RUNTIME_MIN_SPACE here: + * + * |---------- Top of memory ----------| + * | | + * | available | + * | | + * |---------- 768 MB ----------| + * | | + * | reserved | + * | | + * |--- 768 MB - runtime min space ---| + * | | + * | available | + * | | + * |---------- 0 MB ----------| + * + * Edge cases: + * + * - Total memory less than RUNTIME_MIN_SPACE: only claim up to HEAP_MAX_SIZE. + * (enforced elsewhere) + * + * - Total memory between RUNTIME_MIN_SPACE and 768MB: + * + * |---------- Top of memory ----------| + * | | + * | reserved | + * | | + * |---- top - runtime min space ----| + * | | + * | available | + * | | + * |---------- 0 MB ----------| + * + * This by itself would not leave us with RUNTIME_MIN_SPACE of free bytes: if + * rmo_top < 768MB, we will almost certainly have FW claims in the reserved + * region. We try to address that elsewhere: grub_ieee1275_mm_add_region will + * not call us if the resulting free space would be less than RUNTIME_MIN_SPACE. + */ + linux_rmo_save = grub_min (RMO_ADDR_MAX, rmo_top) - RUNTIME_MIN_SPACE; + if (rmo_top > RUNTIME_MIN_SPACE) + { + if (rmo_top <= RMO_ADDR_MAX) + { + if (addr > linux_rmo_save) + { + grub_dprintf ("ieee1275", "rejecting region in RUNTIME_MIN_SPACE reservation (%llx)\n", + addr); + return 0; + } + else if (addr + len > linux_rmo_save) + { + grub_dprintf ("ieee1275", "capping region: (%llx -> %llx) -> (%llx -> %llx)\n", + addr, addr + len, addr, rmo_top - RUNTIME_MIN_SPACE); + len = linux_rmo_save - addr; + } + } + else + { + /* + * we order these cases to prefer higher addresses and avoid some + * splitting issues + */ + if (addr < RMO_ADDR_MAX && (addr + len) > RMO_ADDR_MAX) + { + grub_dprintf ("ieee1275", + "adjusting region for RUNTIME_MIN_SPACE: (%llx -> %llx) -> (%llx -> %llx)\n", + addr, addr + len, RMO_ADDR_MAX, addr + len); + len = (addr + len) - RMO_ADDR_MAX; + addr = RMO_ADDR_MAX; + } + else if ((addr < linux_rmo_save) && ((addr + len) > linux_rmo_save)) + { + grub_dprintf ("ieee1275", "capping region: (%llx -> %llx) -> (%llx -> %llx)\n", + addr, addr + len, addr, linux_rmo_save); + len = linux_rmo_save - addr; + } + else if (addr >= linux_rmo_save && (addr + len) <= RMO_ADDR_MAX) + { + grub_dprintf ("ieee1275", "rejecting region in RUNTIME_MIN_SPACE reservation (%llx)\n", + addr); + return 0; + } + } + } + if (flags & GRUB_MM_ADD_REGION_CONSECUTIVE && len < total) + return 0; + + if (len > total) + len = total; + if (len) { grub_err_t err; @@ -192,15 +339,95 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, if (err) return err; grub_mm_init_region ((void *) (grub_addr_t) addr, len); + total -= len; } - *total += len; - if (*total >= HEAP_MAX_SIZE) + *(grub_uint32_t *) data = total; + + if (total == 0) return 1; return 0; } +static int +heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, + void *data) +{ + return regions_claim (addr, len, type, GRUB_MM_ADD_REGION_NONE, data); +} + +static int +region_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, + void *data) +{ + return regions_claim (addr, len, type, GRUB_MM_ADD_REGION_CONSECUTIVE, data); +} + +static grub_err_t +grub_ieee1275_mm_add_region (grub_size_t size, unsigned int flags) +{ + grub_uint32_t free_memory = 0; + grub_uint32_t avail = 0; + grub_uint32_t total; + + grub_dprintf ("ieee1275", "mm requested region of size %x, flags %x\n", + size, flags); + + /* + * Update free memory each time, which is a bit inefficient but guards us + * against a situation where some OF driver goes out to firmware for + * memory and we don't realise. + */ + grub_machine_mmap_iterate (count_free, &free_memory); + + /* Ensure we leave enough space to boot. */ + if (free_memory <= RUNTIME_MIN_SPACE + size) + { + grub_dprintf ("ieee1275", "Cannot satisfy allocation and retain minimum runtime space\n"); + return GRUB_ERR_OUT_OF_MEMORY; + } + + if (free_memory > RUNTIME_MIN_SPACE) + avail = free_memory - RUNTIME_MIN_SPACE; + + grub_dprintf ("ieee1275", "free = 0x%x available = 0x%x\n", free_memory, avail); + + if (flags & GRUB_MM_ADD_REGION_CONSECUTIVE) + { + /* first try rounding up hard for the sake of speed */ + total = grub_max (ALIGN_UP (size, 1024 * 1024) + 1024 * 1024, 32 * 1024 * 1024); + total = grub_min (avail, total); + + grub_dprintf ("ieee1275", "looking for %x bytes of memory (%x requested)\n", total, size); + + grub_machine_mmap_iterate (region_claim, &total); + grub_dprintf ("ieee1275", "get memory from fw %s\n", total == 0 ? "succeeded" : "failed"); + + if (total != 0) + { + total = grub_min (avail, size); + + grub_dprintf ("ieee1275", "fallback for %x bytes of memory (%x requested)\n", total, size); + + grub_machine_mmap_iterate (region_claim, &total); + grub_dprintf ("ieee1275", "fallback from fw %s\n", total == 0 ? "succeeded" : "failed"); + } + } + else + { + /* provide padding for a grub_mm_header_t and region */ + total = grub_min (avail, size); + grub_machine_mmap_iterate (heap_init, &total); + grub_dprintf ("ieee1275", "get noncontig memory from fw %s\n", total == 0 ? "succeeded" : "failed"); + } + + if (total == 0) + return GRUB_ERR_NONE; + else + return GRUB_ERR_OUT_OF_MEMORY; +} + /* * How much memory does OF believe it has? (regardless of whether * it's accessible or not) @@ -356,17 +583,24 @@ grub_ieee1275_ibm_cas (void) static void grub_claim_heap (void) { - unsigned long total = 0; + grub_err_t err; + grub_uint32_t total = HEAP_MAX_SIZE; + + err = grub_ieee1275_total_mem (&rmo_top); + + /* + * If we cannot size the available memory, we can't be sure we're leaving + * space for the kernel, initrd and things Linux loads early in boot. So only + * allow further allocations from firmware on success + */ + if (err == GRUB_ERR_NONE) + grub_mm_add_region_fn = grub_ieee1275_mm_add_region; #if defined(__powerpc__) if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY)) { - grub_uint64_t rma_size; - grub_err_t err; - - err = grub_ieee1275_total_mem (&rma_size); /* if we have an error, don't call CAS, just hope for the best */ - if (err == GRUB_ERR_NONE && rma_size < (512 * 1024 * 1024)) + if (err == GRUB_ERR_NONE && rmo_top < (512 * 1024 * 1024)) grub_ieee1275_ibm_cas (); } #endif From ee9c11dcfc5e70730b05e5a048cfd0e4a91e659d Mon Sep 17 00:00:00 2001 From: Diego Domingos Date: Mon, 6 Feb 2023 10:03:23 -0500 Subject: [PATCH 316/367] ieee1275: implement vec5 for cas negotiation As a legacy support, if the vector 5 is not implemented, Power Hypervisor will consider the max CPUs as 64 instead 256 currently supported during client-architecture-support negotiation. This patch implements the vector 5 and set the MAX CPUs to 256 while setting the others values to 0 (default). Signed-off-by: Diego Domingos Acked-by: Daniel Axtens Signed-off-by: Stefan Berger Signed-off-by: Avnish Chouhan (cherry picked from commit 942f19959fe7465fb52a1da39ff271a7ab704892) --- grub-core/kern/ieee1275/init.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 85af8fa97b..72d4fed312 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -502,6 +502,19 @@ struct option_vector2 grub_uint8_t max_pft_size; } GRUB_PACKED; +struct option_vector5 +{ + grub_uint8_t byte1; + grub_uint8_t byte2; + grub_uint8_t byte3; + grub_uint8_t cmo; + grub_uint8_t associativity; + grub_uint8_t bin_opts; + grub_uint8_t micro_checkpoint; + grub_uint8_t reserved0; + grub_uint32_t max_cpus; +} GRUB_PACKED; + struct pvr_entry { grub_uint32_t mask; @@ -523,6 +536,8 @@ struct cas_vector grub_uint16_t vec3; grub_uint8_t vec4_size; grub_uint16_t vec4; + grub_uint8_t vec5_size; + struct option_vector5 vec5; } GRUB_PACKED; /* @@ -547,7 +562,7 @@ grub_ieee1275_ibm_cas (void) struct cas_vector vector = { .pvr_list = { { 0x00000000, 0xffffffff } }, /* any processor */ - .num_vecs = 4 - 1, + .num_vecs = 5 - 1, .vec1_size = 0, .vec1 = 0x80, /* ignore */ .vec2_size = 1 + sizeof (struct option_vector2) - 2, @@ -558,6 +573,10 @@ grub_ieee1275_ibm_cas (void) .vec3 = 0x00e0, /* ask for FP + VMX + DFP but don't halt if unsatisfied */ .vec4_size = 2 - 1, .vec4 = 0x0001, /* set required minimum capacity % to the lowest value */ + .vec5_size = 1 + sizeof (struct option_vector5) - 2, + .vec5 = { + 0, 192, 0, 128, 0, 0, 0, 0, 256 + } }; INIT_IEEE1275_COMMON (&args.common, "call-method", 3, 2); From 5142bbe4678e5196b2594858d734c3a4307a4b64 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 6 Feb 2023 10:03:25 -0500 Subject: [PATCH 317/367] ibmvtpm: Add support for trusted boot using a vTPM 2.0 Add support for trusted boot using a vTPM 2.0 on the IBM IEEE1275 PowerPC platform. With this patch grub now measures text and binary data into the TPM's PCRs 8 and 9 in the same way as the x86_64 platform does. This patch requires Daniel Axtens's patches for claiming more memory. Note: The tpm_init() function cannot be called from GRUB_MOD_INIT() since it does not find the device nodes upon module initialization and therefore the call to tpm_init() must be deferred to grub_tpm_measure(). For vTPM support to work on PowerVM, system driver levels 1010.30 or 1020.00 are required. Note: Previous versions of firmware levels with the 2hash-ext-log API call have a bug that, once this API call is invoked, has the effect of disabling the vTPM driver under Linux causing an error message to be displayed in the Linux kernel log. Those users will have to update their machines to the firmware levels mentioned above. Cc: Eric Snowberg Signed-off-by: Stefan Berger Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit 2aa5ef83743dfea79377309ff4f5e9c9a55de355) --- docs/grub.texi | 3 +- grub-core/Makefile.core.def | 7 ++ grub-core/commands/ieee1275/ibmvtpm.c | 155 ++++++++++++++++++++++++++ include/grub/ieee1275/ieee1275.h | 3 + 4 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 grub-core/commands/ieee1275/ibmvtpm.c diff --git a/docs/grub.texi b/docs/grub.texi index 1750b72ee9..825278a7f3 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -6235,7 +6235,8 @@ tpm module is loaded. As such it is recommended that the tpm module be built into @file{core.img} in order to avoid a potential gap in measurement between @file{core.img} being loaded and the tpm module being loaded. -Measured boot is currently only supported on EFI platforms. +Measured boot is currently only supported on EFI and IBM IEEE1275 PowerPC +platforms. @node Lockdown @section Lockdown when booting on a secure setup diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index f21da23213..02ea718652 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1175,6 +1175,13 @@ module = { enable = powerpc_ieee1275; }; +module = { + name = tpm; + common = commands/tpm.c; + ieee1275 = commands/ieee1275/ibmvtpm.c; + enable = powerpc_ieee1275; +}; + module = { name = terminal; common = commands/terminal.c; diff --git a/grub-core/commands/ieee1275/ibmvtpm.c b/grub-core/commands/ieee1275/ibmvtpm.c new file mode 100644 index 0000000000..239942d27e --- /dev/null +++ b/grub-core/commands/ieee1275/ibmvtpm.c @@ -0,0 +1,155 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Free Software Foundation, Inc. + * Copyright (C) 2022 IBM Corporation + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + * + * IBM vTPM support code. + */ + +#include +#include +#include +#include +#include +#include + +static grub_ieee1275_ihandle_t tpm_ihandle; +static grub_uint8_t tpm_version; + +#define IEEE1275_IHANDLE_INVALID ((grub_ieee1275_ihandle_t) 0) + +static void +tpm_get_tpm_version (void) +{ + grub_ieee1275_phandle_t vtpm; + char buffer[20]; + + if (!grub_ieee1275_finddevice ("/vdevice/vtpm", &vtpm) && + !grub_ieee1275_get_property (vtpm, "compatible", buffer, + sizeof (buffer), NULL) && + !grub_strcmp (buffer, "IBM,vtpm20")) + tpm_version = 2; +} + +static grub_err_t +tpm_init (void) +{ + static int init_success = 0; + + if (!init_success) + { + if (grub_ieee1275_open ("/vdevice/vtpm", &tpm_ihandle) < 0) + { + tpm_ihandle = IEEE1275_IHANDLE_INVALID; + return GRUB_ERR_UNKNOWN_DEVICE; + } + + init_success = 1; + + tpm_get_tpm_version (); + } + + return GRUB_ERR_NONE; +} + +static int +ibmvtpm_2hash_ext_log (grub_uint8_t pcrindex, + grub_uint32_t eventtype, + const char *description, + grub_size_t description_size, + void *buf, grub_size_t size) +{ + struct tpm_2hash_ext_log + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t size; + grub_ieee1275_cell_t buf; + grub_ieee1275_cell_t description_size; + grub_ieee1275_cell_t description; + grub_ieee1275_cell_t eventtype; + grub_ieee1275_cell_t pcrindex; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t rc; + }; + struct tpm_2hash_ext_log args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 8, 2); + args.method = (grub_ieee1275_cell_t) "2hash-ext-log"; + args.ihandle = tpm_ihandle; + args.pcrindex = pcrindex; + args.eventtype = eventtype; + args.description = (grub_ieee1275_cell_t) description; + args.description_size = description_size; + args.buf = (grub_ieee1275_cell_t) buf; + args.size = (grub_ieee1275_cell_t) size; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + + /* + * catch_result is set if firmware does not support 2hash-ext-log + * rc is GRUB_IEEE1275_CELL_FALSE (0) on failure + */ + if ((args.catch_result) || args.rc == GRUB_IEEE1275_CELL_FALSE) + return -1; + + return 0; +} + +static grub_err_t +tpm2_log_event (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, + const char *description) +{ + static int error_displayed = 0; + int rc; + + rc = ibmvtpm_2hash_ext_log (pcr, EV_IPL, + description, grub_strlen(description) + 1, + buf, size); + if (rc && !error_displayed) + { + error_displayed++; + return grub_error (GRUB_ERR_BAD_DEVICE, + "2HASH-EXT-LOG failed: Firmware is likely too old.\n"); + } + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, + const char *description) +{ + /* + * Call tpm_init() 'late' rather than from GRUB_MOD_INIT() so that device nodes + * can be found. + */ + grub_err_t err = tpm_init (); + + /* Absence of a TPM isn't a failure. */ + if (err != GRUB_ERR_NONE) + return GRUB_ERR_NONE; + + grub_dprintf ("tpm", "log_event, pcr = %d, size = 0x%" PRIxGRUB_SIZE ", %s\n", + pcr, size, description); + + if (tpm_version == 2) + return tpm2_log_event (buf, size, pcr, description); + + return GRUB_ERR_NONE; +} diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h index 560c968460..27b9cf259b 100644 --- a/include/grub/ieee1275/ieee1275.h +++ b/include/grub/ieee1275/ieee1275.h @@ -24,6 +24,9 @@ #include #include +#define GRUB_IEEE1275_CELL_FALSE ((grub_ieee1275_cell_t) 0) +#define GRUB_IEEE1275_CELL_TRUE ((grub_ieee1275_cell_t) -1) + struct grub_ieee1275_mem_region { unsigned int start; From 7f8daed761c0f16e7d1ee97c972cfc580ad78553 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Mon, 6 Sep 2021 15:46:11 +1000 Subject: [PATCH 318/367] powerpc: Drop Open Hack'Ware Open Hack'Ware was an alternative firmware of powerpc under QEMU. The last commit to any Open Hack'Ware repo I can find is from 2014 [1]. Open Hack'Ware was used for the QEMU "prep" machine type, which was deprecated in QEMU in commit 54c86f5a4844 (hw/ppc: deprecate the machine type 'prep', replaced by '40p') in QEMU v3.1, and had reportedly been broken for years before without anyone noticing. Support was removed in February 2020 by commit b2ce76a0730e (hw/ppc/prep: Remove the deprecated "prep" machine and the OpenHackware BIOS). Open Hack'Ware's limitations require some messy code in GRUB. This complexity is not worth carrying any more. Remove detection of Open Hack'Ware. We will clean up the feature flags in following commits. [1]: https://github.com/qemu/openhackware and https://repo.or.cz/w/openhackware.git are QEMU submodules. They have only small changes on top of OHW v0.4.1, which was imported into QEMU SCM in 2010. I can't find anything resembling an official repo any more. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper (cherry picked from commit f9ce538eec88c5cffbfde021c4e8a95a5e9d0e8f) --- grub-core/kern/ieee1275/cmain.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/grub-core/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c index dce7b84922..cb42f60ebe 100644 --- a/grub-core/kern/ieee1275/cmain.c +++ b/grub-core/kern/ieee1275/cmain.c @@ -49,7 +49,6 @@ grub_ieee1275_find_options (void) grub_ieee1275_phandle_t root; grub_ieee1275_phandle_t options; grub_ieee1275_phandle_t openprom; - grub_ieee1275_phandle_t bootrom; int rc; grub_uint32_t realmode = 0; char tmp[256]; @@ -198,21 +197,6 @@ grub_ieee1275_find_options (void) grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_HAS_CURSORONOFF); } - - if (! grub_ieee1275_finddevice ("/rom/boot-rom", &bootrom) - || ! grub_ieee1275_finddevice ("/boot-rom", &bootrom)) - { - rc = grub_ieee1275_get_property (bootrom, "model", tmp, sizeof (tmp), 0); - if (rc >= 0 && !grub_strncmp (tmp, "PPC Open Hack'Ware", - sizeof ("PPC Open Hack'Ware") - 1)) - { - grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_BROKEN_OUTPUT); - grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CANNOT_SET_COLORS); - grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CANNOT_INTERPRET); - grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM); - grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_ANSI); - } - } } void From de07df28d0cc3f506ac56b35803d283b1b4484d7 Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Chaurasiya Date: Thu, 9 Feb 2023 13:09:16 +0530 Subject: [PATCH 319/367] osdep/linux/hostdisk: Modify sector by sysfs as disk sector The disk sector size provided by sysfs file system considers the sector size of 512 irrespective of disk sector size, thus causing the read by the GRUB to an incorrect offset from what was originally intended. Considering the 512 sector size of sysfs data the actual sector needs to be modified corresponding to disk sector size. Signed-off-by: Mukesh Kumar Chaurasiya Reviewed-by: Daniel Kiper (cherry picked from commit f7564844f82b57078d601befadc438b5bc1fa01b) --- grub-core/osdep/linux/hostdisk.c | 7 ++++--- include/grub/disk.h | 7 +++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/grub-core/osdep/linux/hostdisk.c b/grub-core/osdep/linux/hostdisk.c index 7bc99ac1c1..a9ea0bb465 100644 --- a/grub-core/osdep/linux/hostdisk.c +++ b/grub-core/osdep/linux/hostdisk.c @@ -240,7 +240,8 @@ have_devfs (void) #pragma GCC diagnostic ignored "-Wformat-nonliteral" static int -grub_hostdisk_linux_find_partition (char *dev, grub_disk_addr_t sector) +grub_hostdisk_linux_find_partition (const grub_disk_t disk, char *dev, + grub_disk_addr_t sector) { size_t len = strlen (dev); const char *format; @@ -305,7 +306,7 @@ grub_hostdisk_linux_find_partition (char *dev, grub_disk_addr_t sector) if (fstat (fd, &st) < 0 || !grub_util_device_is_mapped_stat (&st) || !grub_util_get_dm_node_linear_info (st.st_rdev, 0, 0, &start)) - start = grub_util_find_partition_start_os (real_dev); + start = grub_disk_to_native_sector (disk, grub_util_find_partition_start_os (real_dev)); /* We don't care about errors here. */ grub_errno = GRUB_ERR_NONE; @@ -386,7 +387,7 @@ grub_util_fd_open_device (const grub_disk_t disk, grub_disk_addr_t sector, int f && strncmp (dev, "/dev/", 5) == 0) { if (sector >= part_start) - is_partition = grub_hostdisk_linux_find_partition (dev, part_start); + is_partition = grub_hostdisk_linux_find_partition (disk, dev, part_start); else *max = part_start - sector; } diff --git a/include/grub/disk.h b/include/grub/disk.h index 06210a7049..881addcc77 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -208,6 +208,13 @@ grub_disk_from_native_sector (grub_disk_t disk, grub_disk_addr_t sector) return sector << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS); } +/* Convert from GRUB native disk sized sector to disk sized sector. */ +static inline grub_disk_addr_t +grub_disk_to_native_sector (grub_disk_t disk, grub_disk_addr_t sector) +{ + return sector >> (disk->log_sector_size - GRUB_DISK_SECTOR_BITS); +} + /* This is called from the memory manager. */ void grub_disk_cache_invalidate_all (void); From 9a2887f50f0c4fb0907ac691e3830df33003fa2a Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Sun, 29 Jan 2023 19:49:31 +0800 Subject: [PATCH 320/367] mm: Adjust new region size to take management overhead into account When grub_memalign() encounters out-of-memory, it will try grub_mm_add_region_fn() to request more memory from system firmware. However, the size passed to it doesn't take region management overhead into account. Adding a memory area of "size" bytes may result in a heap region of less than "size" bytes really available. Thus, the new region may not be adequate for current allocation request, confusing out-of-memory handling code. This patch introduces GRUB_MM_MGMT_OVERHEAD to address the region management overhead (e.g. metadata, padding). The value of this new constant must be large enough to make sure grub_memalign(align, size) always succeeds after a successful call to grub_mm_init_region(addr, size + align + GRUB_MM_MGMT_OVERHEAD), for any given addr and size (assuming no integer overflow). The size passed to grub_mm_add_region_fn() is now correctly adjusted, thus if grub_mm_add_region_fn() succeeded, current allocation request can always succeed. Signed-off-by: Zhang Boyang Reviewed-by: Daniel Kiper (cherry picked from commit 2282cbfe5aa1ff6c1bbcbdcd2003089ad7c03ba3) --- grub-core/kern/mm.c | 64 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c index da1ac9427c..f29a3e5cbd 100644 --- a/grub-core/kern/mm.c +++ b/grub-core/kern/mm.c @@ -83,6 +83,46 @@ +/* + * GRUB_MM_MGMT_OVERHEAD is an upper bound of management overhead of + * each region, with any possible padding taken into account. + * + * The value must be large enough to make sure grub_memalign(align, size) + * always succeeds after a successful call to + * grub_mm_init_region(addr, size + align + GRUB_MM_MGMT_OVERHEAD), + * for any given addr, align and size (assuming no interger overflow). + * + * The worst case which has maximum overhead is shown in the figure below: + * + * +-- addr + * v |<- size + align ->| + * +---------+----------------+----------------+------------------+---------+ + * | padding | grub_mm_region | grub_mm_header | usable bytes | padding | + * +---------+----------------+----------------+------------------+---------+ + * |<- a ->|<- b ->|<- c ->|<- d ->|<- e ->| + * ^ + * b == sizeof (struct grub_mm_region) | / Assuming no other suitable + * c == sizeof (struct grub_mm_header) | | block is available, then: + * d == size + align +-| If align == 0, this will be + * | the pointer returned by next + * Assuming addr % GRUB_MM_ALIGN == 1, then: | grub_memalign(align, size). + * a == GRUB_MM_ALIGN - 1 | If align > 0, this chunk may + * | need to be split to fulfill + * Assuming d % GRUB_MM_ALIGN == 1, then: | alignment requirements, and + * e == GRUB_MM_ALIGN - 1 | the returned pointer may be + * \ inside these usable bytes. + * Therefore, the maximum overhead is: + * a + b + c + e == (GRUB_MM_ALIGN - 1) + sizeof (struct grub_mm_region) + * + sizeof (struct grub_mm_header) + (GRUB_MM_ALIGN - 1) + */ +#define GRUB_MM_MGMT_OVERHEAD ((GRUB_MM_ALIGN - 1) \ + + sizeof (struct grub_mm_region) \ + + sizeof (struct grub_mm_header) \ + + (GRUB_MM_ALIGN - 1)) + +/* The size passed to grub_mm_add_region_fn() is aligned up by this value. */ +#define GRUB_MM_HEAP_GROW_ALIGN 4096 + grub_mm_region_t grub_mm_base; grub_mm_add_region_func_t grub_mm_add_region_fn; @@ -230,6 +270,11 @@ grub_mm_init_region (void *addr, grub_size_t size) grub_dprintf ("regions", "No: considering a new region at %p of size %" PRIxGRUB_SIZE "\n", addr, size); + /* + * If you want to modify the code below, please also take a look at + * GRUB_MM_MGMT_OVERHEAD and make sure it is synchronized with the code. + */ + /* Allocate a region from the head. */ r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN); @@ -410,6 +455,7 @@ grub_memalign (grub_size_t align, grub_size_t size) { grub_mm_region_t r; grub_size_t n = ((size + GRUB_MM_ALIGN - 1) >> GRUB_MM_ALIGN_LOG2) + 1; + grub_size_t grow; int count = 0; if (!grub_mm_base) @@ -418,10 +464,22 @@ grub_memalign (grub_size_t align, grub_size_t size) if (size > ~(grub_size_t) align) goto fail; + /* + * Pre-calculate the necessary size of heap growth (if applicable), + * with region management overhead taken into account. + */ + if (grub_add (size + align, GRUB_MM_MGMT_OVERHEAD, &grow)) + goto fail; + + /* Align up heap growth to make it friendly to CPU/MMU. */ + if (grow > ~(grub_size_t) (GRUB_MM_HEAP_GROW_ALIGN - 1)) + goto fail; + grow = ALIGN_UP (grow, GRUB_MM_HEAP_GROW_ALIGN); + /* We currently assume at least a 32-bit grub_size_t, so limiting allocations to - 1MiB in name of sanity is beneficial. */ - if ((size + align) > ~(grub_size_t) 0x100000) + if (grow > ~(grub_size_t) 0x100000) goto fail; align = (align >> GRUB_MM_ALIGN_LOG2); @@ -447,7 +505,7 @@ grub_memalign (grub_size_t align, grub_size_t size) count++; if (grub_mm_add_region_fn != NULL && - grub_mm_add_region_fn (size, GRUB_MM_ADD_REGION_CONSECUTIVE) == GRUB_ERR_NONE) + grub_mm_add_region_fn (grow, GRUB_MM_ADD_REGION_CONSECUTIVE) == GRUB_ERR_NONE) goto again; /* fallthrough */ @@ -462,7 +520,7 @@ grub_memalign (grub_size_t align, grub_size_t size) * Try again even if this fails, in case it was able to partially * satisfy the request */ - grub_mm_add_region_fn (size, GRUB_MM_ADD_REGION_NONE); + grub_mm_add_region_fn (grow, GRUB_MM_ADD_REGION_NONE); goto again; } From c9176aebab53644c1a029052d03ee4aeac40318f Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Sun, 29 Jan 2023 19:49:32 +0800 Subject: [PATCH 321/367] mm: Preallocate some space when adding new regions When grub_memalign() encounters out-of-memory, it will try grub_mm_add_region_fn() to request more memory from system firmware. However, it doesn't preallocate memory space for future allocation requests. In extreme cases, it requires one call to grub_mm_add_region_fn() for each memory allocation request. This can be very slow. This patch introduces GRUB_MM_HEAP_GROW_EXTRA, the minimal heap growth granularity. The new region size is now set to the bigger one of its original value and GRUB_MM_HEAP_GROW_EXTRA. Thus, it will result in some memory space preallocated if current allocations request is small. The value of GRUB_MM_HEAP_GROW_EXTRA is set to 1MB. If this value is smaller, the cost of small memory allocations will be higher. If this value is larger, more memory will be wasted and it might cause out-of-memory on machines with small amount of RAM. Signed-off-by: Zhang Boyang Reviewed-by: Daniel Kiper (cherry picked from commit 21869baec15239b6d99122b32b14a778af4c754f) --- grub-core/kern/mm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c index f29a3e5cbd..cc8a4703bc 100644 --- a/grub-core/kern/mm.c +++ b/grub-core/kern/mm.c @@ -123,6 +123,9 @@ /* The size passed to grub_mm_add_region_fn() is aligned up by this value. */ #define GRUB_MM_HEAP_GROW_ALIGN 4096 +/* Minimal heap growth granularity when existing heap space is exhausted. */ +#define GRUB_MM_HEAP_GROW_EXTRA 0x100000 + grub_mm_region_t grub_mm_base; grub_mm_add_region_func_t grub_mm_add_region_fn; @@ -471,6 +474,9 @@ grub_memalign (grub_size_t align, grub_size_t size) if (grub_add (size + align, GRUB_MM_MGMT_OVERHEAD, &grow)) goto fail; + /* Preallocate some extra space if heap growth is small. */ + grow = grub_max (grow, GRUB_MM_HEAP_GROW_EXTRA); + /* Align up heap growth to make it friendly to CPU/MMU. */ if (grow > ~(grub_size_t) (GRUB_MM_HEAP_GROW_ALIGN - 1)) goto fail; From 61616e1efb5b4fcc7835711a6cdf7affb72d8801 Mon Sep 17 00:00:00 2001 From: Zhang Boyang Date: Sun, 29 Jan 2023 19:49:33 +0800 Subject: [PATCH 322/367] mm: Avoid complex heap growth math in hot path We do a lot of math about heap growth in hot path of grub_memalign(). However, the result is only used if out of memory is encountered, which is seldom. This patch moves these calculations away from hot path. These calculations are now only done if out of memory is encountered. This change can also help compiler to optimize integer overflow checks away. Signed-off-by: Zhang Boyang Reviewed-by: Daniel Kiper (cherry picked from commit 65bc45963014773e2062ccc63ff34a089d2e352e) --- grub-core/kern/mm.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c index cc8a4703bc..630d7be0e2 100644 --- a/grub-core/kern/mm.c +++ b/grub-core/kern/mm.c @@ -467,20 +467,7 @@ grub_memalign (grub_size_t align, grub_size_t size) if (size > ~(grub_size_t) align) goto fail; - /* - * Pre-calculate the necessary size of heap growth (if applicable), - * with region management overhead taken into account. - */ - if (grub_add (size + align, GRUB_MM_MGMT_OVERHEAD, &grow)) - goto fail; - - /* Preallocate some extra space if heap growth is small. */ - grow = grub_max (grow, GRUB_MM_HEAP_GROW_EXTRA); - - /* Align up heap growth to make it friendly to CPU/MMU. */ - if (grow > ~(grub_size_t) (GRUB_MM_HEAP_GROW_ALIGN - 1)) - goto fail; - grow = ALIGN_UP (grow, GRUB_MM_HEAP_GROW_ALIGN); + grow = size + align; /* We currently assume at least a 32-bit grub_size_t, so limiting allocations to - 1MiB @@ -510,6 +497,25 @@ grub_memalign (grub_size_t align, grub_size_t size) /* Request additional pages, contiguous */ count++; + /* + * Calculate the necessary size of heap growth (if applicable), + * with region management overhead taken into account. + */ + if (grub_add (grow, GRUB_MM_MGMT_OVERHEAD, &grow)) + goto fail; + + /* Preallocate some extra space if heap growth is small. */ + grow = grub_max (grow, GRUB_MM_HEAP_GROW_EXTRA); + + /* Align up heap growth to make it friendly to CPU/MMU. */ + if (grow > ~(grub_size_t) (GRUB_MM_HEAP_GROW_ALIGN - 1)) + goto fail; + grow = ALIGN_UP (grow, GRUB_MM_HEAP_GROW_ALIGN); + + /* Do the same sanity check again. */ + if (grow > ~(grub_size_t) 0x100000) + goto fail; + if (grub_mm_add_region_fn != NULL && grub_mm_add_region_fn (grow, GRUB_MM_ADD_REGION_CONSECUTIVE) == GRUB_ERR_NONE) goto again; From 68005f2cdec457882b24173b5a93b36322d138ea Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Thu, 9 Mar 2023 11:18:19 -0500 Subject: [PATCH 323/367] hostdisk: work around /proc not reporting size fstat(2) of files in /proc will yield st_size == 0 regardless of file contents. Use a negative value in grub_file_t's size to denote "ignore" and plumb through. Signed-off-by: Robbie Harwood --- grub-core/kern/file.c | 28 ++++++++++++++++------------ grub-core/lib/progress.c | 2 +- grub-core/osdep/unix/hostdisk.c | 6 ++++++ 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c index 868ce3b63e..4ea6d1ce95 100644 --- a/grub-core/kern/file.c +++ b/grub-core/kern/file.c @@ -172,26 +172,30 @@ grub_file_read (grub_file_t file, void *buf, grub_size_t len) grub_disk_read_hook_t read_hook; void *read_hook_data; - if (file->offset > file->size) - { - grub_error (GRUB_ERR_OUT_OF_RANGE, - N_("attempt to read past the end of file")); - return -1; - } - if (len == 0) return 0; - if (len > file->size - file->offset) - len = file->size - file->offset; +#ifdef GRUB_MACHINE_EMU + if (file->size >= 0) + { +#endif + if (file->offset > file->size) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("attempt to read past the end of file")); + return -1; + } + + if (len > file->size - file->offset) + len = file->size - file->offset; +#ifdef GRUB_MACHINE_EMU + } +#endif /* Prevent an overflow. */ if ((grub_ssize_t) len < 0) len >>= 1; - if (len == 0) - return 0; - read_hook = file->read_hook; read_hook_data = file->read_hook_data; if (!file->read_hook) diff --git a/grub-core/lib/progress.c b/grub-core/lib/progress.c index 4b7cbbca6d..f3226b6898 100644 --- a/grub-core/lib/progress.c +++ b/grub-core/lib/progress.c @@ -71,7 +71,7 @@ grub_file_progress_hook_real (grub_disk_addr_t sector __attribute__ ((unused)), * 100ULL * 1000ULL, now - file->last_progress_time, 0); - if (file->size == 0) + if (file->size <= 0) percent = 100; else percent = grub_divmod64 (100 * file->progress_offset, diff --git a/grub-core/osdep/unix/hostdisk.c b/grub-core/osdep/unix/hostdisk.c index 3a00d7451a..e5f4b4d5f9 100644 --- a/grub-core/osdep/unix/hostdisk.c +++ b/grub-core/osdep/unix/hostdisk.c @@ -71,6 +71,12 @@ grub_util_get_fd_size (grub_util_fd_t fd, const char *name, unsigned *log_secsiz if (log_secsize) *log_secsize = 9; +#ifdef GRUB_MACHINE_EMU + /* /proc doesn't behave itself and gives 0 for file sizes to stat. */ + if (st.st_size == 0 && !grub_strncmp ("/proc", name, 5)) + return -1; +#endif + return st.st_size; } From f6364704e8fbd48515ea9c4e0dd0f4bc1ad87f66 Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Tue, 7 Mar 2023 18:59:40 -0500 Subject: [PATCH 324/367] blscfg: check for mounted /boot in emu Irritatingly, BLS defines paths relatives to the mountpoint of the filesystem which contains its snippets, not / or any other fixed location. So grub2-emu needs to know whether /boot is a separate filesysem from / and conditionally prepend a path. Signed-off-by: Robbie Harwood --- grub-core/commands/blscfg.c | 54 +++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/grub-core/commands/blscfg.c b/grub-core/commands/blscfg.c index dbd0899acf..6e398fc175 100644 --- a/grub-core/commands/blscfg.c +++ b/grub-core/commands/blscfg.c @@ -40,8 +40,9 @@ GRUB_MOD_LICENSE ("GPLv3+"); #include "loadenv.h" #define GRUB_BLS_CONFIG_PATH "/loader/entries/" + #ifdef GRUB_MACHINE_EMU -#define GRUB_BOOT_DEVICE "" +#define GRUB_BOOT_DEVICE "/boot" #else #define GRUB_BOOT_DEVICE "($root)" #endif @@ -54,8 +55,50 @@ struct keyval static struct bls_entry *entries = NULL; +/* Cache probing in frob_boot_device(). Used for linux entry also. + * Always true in non-emu, meaning to prefix things with GRUB_BOOT_DEVICE. */ +static int separate_boot = -1; + #define FOR_BLS_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries) +/* BLS appears to make paths relative to the filesystem that snippets are + * on, not /. Attempt to cope. */ +static char *frob_boot_device(char *tmp) +{ +#ifdef GRUB_MACHINE_EMU + grub_file_t f; + char *line = NULL; + + if (separate_boot != -1) + goto probed; + + separate_boot = 0; + + f = grub_file_open ("/proc/mounts", GRUB_FILE_TYPE_CONFIG); + if (f == NULL) + goto probed; + + while ((line = grub_file_getline (f))) + { + if (grub_strstr (line, " " GRUB_BOOT_DEVICE " ")) + { + separate_boot = 1; + grub_free (line); + break; + } + + grub_free(line); + } + + grub_file_close (f); + probed: + if (!separate_boot) + return grub_stpcpy (tmp, " "); +#endif + + return grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE); +} + static int bls_add_keyval(struct bls_entry *entry, char *key, char *val) { char *k, *v; @@ -842,7 +885,7 @@ static void create_entry (struct bls_entry *entry) for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++) { grub_dprintf ("blscfg", "adding early initrd %s\n", early_initrds[i]); - tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE); + tmp = frob_boot_device (tmp); tmp = grub_stpcpy (tmp, initrd_prefix); tmp = grub_stpcpy (tmp, early_initrds[i]); grub_free(early_initrds[i]); @@ -851,7 +894,7 @@ static void create_entry (struct bls_entry *entry) for (i = 0; initrds != NULL && initrds[i] != NULL; i++) { grub_dprintf ("blscfg", "adding initrd %s\n", initrds[i]); - tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE); + tmp = frob_boot_device (tmp); tmp = grub_stpcpy (tmp, initrds[i]); } tmp = grub_stpcpy (tmp, "\n"); @@ -888,7 +931,7 @@ static void create_entry (struct bls_entry *entry) } char *tmp = dt; tmp = grub_stpcpy (dt, "devicetree"); - tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE); + tmp = frob_boot_device (tmp); if (add_dt_prefix) tmp = grub_stpcpy (tmp, prefix); tmp = grub_stpcpy (tmp, devicetree); @@ -907,7 +950,8 @@ static void create_entry (struct bls_entry *entry) "linux %s%s%s%s\n" "%s%s", savedefault ? "savedefault\n" : "", - GRUB_BOOT_DEVICE, clinux, options ? " " : "", options ? options : "", + separate_boot ? GRUB_BOOT_DEVICE : "", + clinux, options ? " " : "", options ? options : "", initrd ? initrd : "", dt ? dt : ""); grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, &index, entry); From 43aed52eb94f3495d9f9003351eb8b569dec7356 Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Wed, 22 Mar 2023 14:19:43 -0400 Subject: [PATCH 325/367] emu/linux: work around systemctl kexec returning Per systemctl(1), it "is asynchronous; it will return after the reboot operation is enqueued, without waiting for it to complete". This differs from kexec(8), which calls reboot(2) and therefore does not return. When not using fallback, this results in the confusing-but-harmless: error trying to perform 'systemctl kexec': 0 Aborted. Press any key to exit. on screen for a bit, followed by successful kexec. To reduce the liklihood of hitting this case, add a delay on succesful return. Ultimately, the systemd interface is racy: we can't avoid it entirely unless we never fallback on success. Signed-off-by: Robbie Harwood --- grub-core/loader/emu/linux.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/grub-core/loader/emu/linux.c b/grub-core/loader/emu/linux.c index 0cf378a376..7de3f7f861 100644 --- a/grub-core/loader/emu/linux.c +++ b/grub-core/loader/emu/linux.c @@ -74,6 +74,10 @@ grub_linux_boot (void) (kexecute==1) ? "do-or-die" : "just-in-case"); rc = grub_util_exec (systemctl); + /* `systemctl kexec` is "asynchronous" and will return even on success. */ + if (rc == 0) + grub_sleep (10); + if (kexecute == 1) grub_fatal (N_("error trying to perform 'systemctl kexec': %d"), rc); From ae0100da2c69e2d60e9d224269f73d6e25377260 Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Mon, 27 Mar 2023 12:25:39 +0530 Subject: [PATCH 326/367] kern/ieee1275/init: Convert plain numbers to constants in Vec5 This patch converts the plain numbers used in Vec5 properties to constants. 1. LPAR: Client program supports logical partitioning and associated hcall()s. 2. SPLPAR: Client program supports the Shared Processor LPAR Option. 3. CMO: Enables the Cooperative Memory Over-commitment Option. 4. MAX_CPU: Defines maximum number of CPUs supported. Signed-off-by: Avnish Chouhan Reviewed-by: Daniel Kiper (cherry picked from commit 8406cfe4774eb2da3db4bf0bc2b2ff6592ecbdaf) --- grub-core/kern/ieee1275/init.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 72d4fed312..5d79580341 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -72,6 +72,12 @@ extern char _end[]; grub_addr_t grub_ieee1275_original_stack; #endif +#define LPAR 0x80 +#define SPLPAR 0x40 +#define BYTE2 (LPAR | SPLPAR) +#define CMO 0x80 +#define MAX_CPU 256 + void grub_exit (int rc __attribute__((unused))) { @@ -575,7 +581,7 @@ grub_ieee1275_ibm_cas (void) .vec4 = 0x0001, /* set required minimum capacity % to the lowest value */ .vec5_size = 1 + sizeof (struct option_vector5) - 2, .vec5 = { - 0, 192, 0, 128, 0, 0, 0, 0, 256 + 0, BYTE2, 0, CMO, 0, 0, 0, 0, MAX_CPU } }; From 2becfab142307b80078df9ec93a181bd10b4ac4a Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Mon, 27 Mar 2023 12:25:40 +0530 Subject: [PATCH 327/367] kern/ieee1275/init: Extended support in Vec5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch enables multiple options in Vec5 which are required and solves the boot issues seen on some machines which are looking for these specific options. 1. LPAR: Client program supports logical partitioning and associated hcall()s. 2. SPLPAR: Client program supports the Shared Processor LPAR Option. 3. DYN_RCON_MEM: Client program supports the “ibm,dynamic-reconfiguration-memory” property and it may be presented in the device tree. 4. LARGE_PAGES: Client supports pages larger than 4 KB. 5. DONATE_DCPU_CLS: Client supports donating dedicated processor cycles. 6. PCI_EXP: Client supports PCI Express implementations utilizing Message Signaled Interrupts (MSIs). 7. CMOC: Enables the Cooperative Memory Over-commitment Option. 8. EXT_CMO: Enables the Extended Cooperative Memory Over-commit Option. 9. ASSOC_REF: Enables “ibm,associativity” and “ibm,associativity-reference-points” properties. 10. AFFINITY: Enables Platform Resource Reassignment Notification. 11. NUMA: Supports NUMA Distance Lookup Table Option. 12. HOTPLUG_INTRPT: Supports Hotplug Interrupts. 13. HPT_RESIZE: Enable Hash Page Table Resize Option. 14. MAX_CPU: Defines maximum number of CPUs supported. 15. PFO_HWRNG: Supports Random Number Generator. 16. PFO_HW_COMP: Supports Compression Engine. 17. PFO_ENCRYPT: Supports Encryption Engine. 18. SUB_PROCESSORS: Supports Sub-Processors. 19. DY_MEM_V2: Client program supports the “ibm,dynamic-memory-v2” property in the “ibm,dynamic-reconfiguration-memory” node and it may be presented in the device tree. 20. DRC_INFO: Client program supports the “ibm,drc-info” property definition and it may be presented in the device tree. Signed-off-by: Avnish Chouhan Reviewed-by: Daniel Kiper (cherry picked from commit 98d0df0351fbff7a4acc64c7594d538889a43e2d) --- grub-core/kern/ieee1275/init.c | 47 +++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 5d79580341..3d4ad9d1f1 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -72,11 +72,41 @@ extern char _end[]; grub_addr_t grub_ieee1275_original_stack; #endif -#define LPAR 0x80 -#define SPLPAR 0x40 -#define BYTE2 (LPAR | SPLPAR) -#define CMO 0x80 -#define MAX_CPU 256 +/* Options vector5 properties. */ + +#define LPAR 0x80 +#define SPLPAR 0x40 +#define DYN_RCON_MEM 0x20 +#define LARGE_PAGES 0x10 +#define DONATE_DCPU_CLS 0x02 +#define PCI_EXP 0x01 +#define BYTE2 (LPAR | SPLPAR | DYN_RCON_MEM | LARGE_PAGES | DONATE_DCPU_CLS | PCI_EXP) + +#define CMOC 0x80 +#define EXT_CMO 0x40 +#define CMO (CMOC | EXT_CMO) + +#define ASSOC_REF 0x80 +#define AFFINITY 0x40 +#define NUMA 0x20 +#define ASSOCIATIVITY (ASSOC_REF | AFFINITY | NUMA) + +#define HOTPLUG_INTRPT 0x04 +#define HPT_RESIZE 0x01 +#define BIN_OPTS (HOTPLUG_INTRPT | HPT_RESIZE) + +#define MAX_CPU 256 + +#define PFO_HWRNG 0x80000000 +#define PFO_HW_COMP 0x40000000 +#define PFO_ENCRYPT 0x20000000 +#define PLATFORM_FACILITIES (PFO_HWRNG | PFO_HW_COMP | PFO_ENCRYPT) + +#define SUB_PROCESSORS 1 + +#define DY_MEM_V2 0x80 +#define DRC_INFO 0x40 +#define BYTE22 (DY_MEM_V2 | DRC_INFO) void grub_exit (int rc __attribute__((unused))) @@ -519,6 +549,11 @@ struct option_vector5 grub_uint8_t micro_checkpoint; grub_uint8_t reserved0; grub_uint32_t max_cpus; + grub_uint16_t base_papr; + grub_uint16_t mem_reference; + grub_uint32_t platform_facilities; + grub_uint8_t sub_processors; + grub_uint8_t byte22; } GRUB_PACKED; struct pvr_entry @@ -581,7 +616,7 @@ grub_ieee1275_ibm_cas (void) .vec4 = 0x0001, /* set required minimum capacity % to the lowest value */ .vec5_size = 1 + sizeof (struct option_vector5) - 2, .vec5 = { - 0, BYTE2, 0, CMO, 0, 0, 0, 0, MAX_CPU + 0, BYTE2, 0, CMO, ASSOCIATIVITY, BIN_OPTS, 0, 0, MAX_CPU, 0, 0, PLATFORM_FACILITIES, SUB_PROCESSORS, BYTE22 } }; From b09440df05755c665cac9b93dd654e661c655c38 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Wed, 22 Mar 2023 12:25:43 +0800 Subject: [PATCH 328/367] tpm: Disable the tpm verifier if the TPM device is not present When the tpm module is loaded, the verifier reads entire file into memory, measures it and uses verified content as a backing buffer for file accesses. However, this process may result in high memory utilization for file operations, sometimes causing a system to run out of memory which may finally lead to boot failure. To address this issue, among others, the commit 887f98f0d (mm: Allow dynamically requesting additional memory regions) have optimized memory management by dynamically allocating heap space to maximize memory usage and reduce threat of memory exhaustion. But in some cases problems may still arise, e.g., when large ISO images are mounted using loopback or when dealing with embedded systems with limited memory resources. Unfortunately current implementation of the tpm module doesn't allow elimination of the back buffer once it is loaded. Even if the TPM device is not present or it has been explicitly disabled. This may unnecessary allocate a lot memory. To solve this issue, a patch has been developed to detect the TPM status at module load and skip verifier registration if the device is missing or deactivated. This prevents allocation of memory for the back buffer, avoiding wasting memory when no real measure boot functionality is performed. Disabling the TPM device in the system can reduce memory usage in the GRUB. It is useful in scenarios where high memory utilization is a concern and measurements of loaded artifacts are not necessary. Signed-off-by: Michael Chang Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper (cherry picked from commit 30708dfe3bebd62a5487437554da8a24253f519f) --- grub-core/commands/efi/tpm.c | 37 +++++++++++++++++++++++++++ grub-core/commands/ieee1275/ibmvtpm.c | 20 +++++++-------- grub-core/commands/tpm.c | 10 ++++++++ include/grub/tpm.h | 1 + 4 files changed, 58 insertions(+), 10 deletions(-) diff --git a/grub-core/commands/efi/tpm.c b/grub-core/commands/efi/tpm.c index ae09c1bf8b..e1f343fea3 100644 --- a/grub-core/commands/efi/tpm.c +++ b/grub-core/commands/efi/tpm.c @@ -287,3 +287,40 @@ grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, else return grub_tpm2_log_event (tpm_handle, buf, size, pcr, description); } + +int +grub_tpm_present (void) +{ + grub_efi_handle_t tpm_handle; + grub_efi_uint8_t protocol_version; + + if (!grub_tpm_handle_find (&tpm_handle, &protocol_version)) + return 0; + + if (protocol_version == 1) + { + grub_efi_tpm_protocol_t *tpm; + + tpm = grub_efi_open_protocol (tpm_handle, &tpm_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (!tpm) + { + grub_dprintf ("tpm", "Cannot open TPM protocol\n"); + return 0; + } + return grub_tpm1_present (tpm); + } + else + { + grub_efi_tpm2_protocol_t *tpm; + + tpm = grub_efi_open_protocol (tpm_handle, &tpm2_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (!tpm) + { + grub_dprintf ("tpm", "Cannot open TPM protocol\n"); + return 0; + } + return grub_tpm2_present (tpm); + } +} diff --git a/grub-core/commands/ieee1275/ibmvtpm.c b/grub-core/commands/ieee1275/ibmvtpm.c index 239942d27e..a6fee5c516 100644 --- a/grub-core/commands/ieee1275/ibmvtpm.c +++ b/grub-core/commands/ieee1275/ibmvtpm.c @@ -135,16 +135,6 @@ grub_err_t grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, const char *description) { - /* - * Call tpm_init() 'late' rather than from GRUB_MOD_INIT() so that device nodes - * can be found. - */ - grub_err_t err = tpm_init (); - - /* Absence of a TPM isn't a failure. */ - if (err != GRUB_ERR_NONE) - return GRUB_ERR_NONE; - grub_dprintf ("tpm", "log_event, pcr = %d, size = 0x%" PRIxGRUB_SIZE ", %s\n", pcr, size, description); @@ -153,3 +143,13 @@ grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, return GRUB_ERR_NONE; } + +int +grub_tpm_present (void) +{ + /* + * Call tpm_init() "late" rather than from GRUB_MOD_INIT() so that device nodes + * can be found. + */ + return tpm_init() == GRUB_ERR_NONE; +} diff --git a/grub-core/commands/tpm.c b/grub-core/commands/tpm.c index e287d042e6..5839053d3d 100644 --- a/grub-core/commands/tpm.c +++ b/grub-core/commands/tpm.c @@ -86,10 +86,20 @@ struct grub_file_verifier grub_tpm_verifier = { GRUB_MOD_INIT (tpm) { + /* + * Even though this now calls ibmvtpm's grub_tpm_present() from GRUB_MOD_INIT(), + * it does seem to call it late enough in the initialization sequence so + * that whatever discovered "device nodes" before this GRUB_MOD_INIT() is + * called, enables the ibmvtpm driver to see the device nodes. + */ + if (!grub_tpm_present()) + return; grub_verifier_register (&grub_tpm_verifier); } GRUB_MOD_FINI (tpm) { + if (!grub_tpm_present()) + return; grub_verifier_unregister (&grub_tpm_verifier); } diff --git a/include/grub/tpm.h b/include/grub/tpm.h index 5c285cbc52..c19fcbd0a6 100644 --- a/include/grub/tpm.h +++ b/include/grub/tpm.h @@ -36,4 +36,5 @@ grub_err_t grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, const char *description); +int grub_tpm_present (void); #endif From ee58dd70d627df54b806e94409673a11caab5a5d Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Fri, 7 Apr 2023 14:54:35 +0200 Subject: [PATCH 329/367] grub_dl_set_mem_attrs(): fix format string The grub_dprintf() call for printing the message updating attributes for GOT and trampolines passes the argument "mod->name", but the format string doesn't accept that argument. Print the module name too. Example output: > kern/dl.c:736: updating attributes for GOT and trampolines ("video_fb") Fixes: ad1b904d325b (nx: set page permissions for loaded modules.) Signed-off-by: Laszlo Ersek --- grub-core/kern/dl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index ab9101a5ad..a97f4a8b13 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -733,7 +733,8 @@ grub_dl_set_mem_attrs (grub_dl_t mod, void *ehdr) { tgsz = ALIGN_UP(tgsz, arch_addralign); - grub_dprintf ("modules", "updating attributes for GOT and trampolines\n", + grub_dprintf ("modules", + "updating attributes for GOT and trampolines (\"%s\")\n", mod->name); grub_update_mem_attrs (tgaddr, tgsz, GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_X, GRUB_MEM_ATTR_W); From deb4c03d7658f0695db8217896c2830009b7469d Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Fri, 7 Apr 2023 16:21:54 +0200 Subject: [PATCH 330/367] grub_dl_set_mem_attrs(): add self-check for the tramp/GOT sizes On aarch64 UEFI, we currently have a crasher: grub_dl_load_core() grub_dl_load_core_noinit() /* independent allocation: must remain writable */ mod = grub_zalloc(); /* allocates module image with incorrect tail alignment */ grub_dl_load_segments() /* write-protecting the module image makes "mod" read-only! */ grub_dl_set_mem_attrs() grub_update_mem_attrs() grub_dl_init() /* page fault, crash */ mod->next = ...; - Commit 887f1d8fa976 ("modules: load module sections at page-aligned addresses", 2023-02-08) forgot to page-align the allocation of the trampolines and GOT areas of grub2 modules, in grub_dl_load_segments(). - Commit ad1b904d325b ("nx: set page permissions for loaded modules.", 2023-02-08) calculated a common bounding box for the trampolines and GOT areas in grub_dl_set_mem_attrs(), rounded the box size up to a whole multiple of EFI page size ("arch_addralign"), and write-protected the resultant page range. Consequently, grub_dl_load_segments() places the module image in memory such that its tail -- the end of the trampolines and GOT areas -- lands at the head of a page whose tail in turn contains independent memory allocations, such as "mod". grub_dl_set_mem_attrs() will then unwittingly write-protect these other allocations too. But "mod" must remain writable: we assign "mod->next" in grub_dl_init() subsequently. Currently we crash there with a page fault / permission fault. (The crash is not trivial to hit: the tramp/GOT areas are irrelevant on x86_64, plus the page protection depends on the UEFI platform firmware providing EFI_MEMORY_ATTRIBUTE_PROTOCOL. In practice, the crash is restricted to aarch64 edk2 (ArmVirtQemu) builds containing commit 1c4dfadb4611, "ArmPkg/CpuDxe: Implement EFI memory attributes protocol", 2023-03-16.) Example log before the patch: > kern/dl.c:736: updating attributes for GOT and trampolines ("video_fb") > kern/efi/mm.c:927: set +rx -w on 0x13b88b000-0x13b88bfff before:rwx after:r-x > kern/dl.c:744: done updating module memory attributes for "video_fb" > kern/dl.c:639: flushing 0xe4f0 bytes at 0x13b87d000 > kern/arm64/cache.c:42: D$ line size: 64 > kern/arm64/cache.c:43: I$ line size: 64 > kern/dl.c:839: module name: video_fb > kern/dl.c:840: init function: 0x0 > kern/dl.c:865: Initing module video_fb > > Synchronous Exception at 0x000000013B8A76EC > PC 0x00013B8A76EC > > X0 0x000000013B88B960 X1 0x0000000000000000 X2 0x000000013F93587C X3 0x0000000000000075 > > SP 0x00000000470745C0 ELR 0x000000013B8A76EC SPSR 0x60000205 FPSR 0x00000000 > ESR 0x9600004F FAR 0x000000013B88B9D0 > > ESR : EC 0x25 IL 0x1 ISS 0x0000004F > > Data abort: Permission fault, third level Note the following: - The whole 4K page at 0x1_3B88_B000 is write-protected. - The "video_fb" module actually lives at [0x1_3B87_D000, 0x1_3B88_B4F0) -- left-inclusive, right-exclusive --; that is, in the last page (at 0x1_3B88_B000), it only occupies the first 0x4F0 bytes. - The instruction at 0x1_3B8A_76EC faults. Not shown here, but it is a store instruction, which writes to the field at offset 0x70 of the structure pointed-to by the X0 register. This is the "mod->next" assignment from grub_dl_init(). - The faulting address is therefore (X0 + 0x70), i.e., 0x1_3B88_B9D0. This is indeed the value held in the FAR register. - The faulting address 0x1_3B88_B9D0 falls in the above-noted page (at 0x1_3B88_B000), namely at offset 0x9D0. This is *beyond* the first 0x4F0 bytes that the very tail of the "video_fb" module occupies at the front of that page. For now, add a self-check that reports this bug (and prevents the crash by skipping the write protection). Example log after the patch: > kern/dl.c:742:BUG: trying to protect pages outside of module allocation > ("video_fb"): module base 0x13b87d000, size 0xe4f0; tramp/GOT base > 0x13b88b000, size 0x1000 Signed-off-by: Laszlo Ersek --- grub-core/kern/dl.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index a97f4a8b13..3b66fa410e 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -682,7 +682,7 @@ grub_dl_set_mem_attrs (grub_dl_t mod, void *ehdr) #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) grub_size_t arch_addralign = grub_arch_dl_min_alignment (); grub_addr_t tgaddr; - grub_uint64_t tgsz; + grub_size_t tgsz; #endif grub_dprintf ("modules", "updating memory attributes for \"%s\"\n", @@ -736,6 +736,15 @@ grub_dl_set_mem_attrs (grub_dl_t mod, void *ehdr) grub_dprintf ("modules", "updating attributes for GOT and trampolines (\"%s\")\n", mod->name); + if (tgaddr < (grub_addr_t)mod->base || + tgsz > (grub_addr_t)-1 - tgaddr || + tgaddr + tgsz > (grub_addr_t)mod->base + mod->sz) + return grub_error (GRUB_ERR_BUG, + "BUG: trying to protect pages outside of module " + "allocation (\"%s\"): module base %p, size 0x%" + PRIxGRUB_SIZE "; tramp/GOT base 0x%" PRIxGRUB_ADDR + ", size 0x%" PRIxGRUB_SIZE, + mod->name, mod->base, mod->sz, tgaddr, tgsz); grub_update_mem_attrs (tgaddr, tgsz, GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_X, GRUB_MEM_ATTR_W); } From c3569e4a245c21a3806ca122ee88da7c91d3d454 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Fri, 7 Apr 2023 16:56:09 +0200 Subject: [PATCH 331/367] grub_dl_load_segments(): page-align the tramp/GOT areas too The tramp/GOT write-protection in grub_dl_set_mem_attrs() requires that the tramp/GOT areas of the module image *not* share a page with any other memory allocations. Page-align the tramp/GOT areas, while satisfying their intrinsic alignment requirements too. Fixes: 887f1d8fa976 (modules: load module sections at page-aligned addresses) Fixes: ad1b904d325b (nx: set page permissions for loaded modules.) Signed-off-by: Laszlo Ersek --- grub-core/kern/dl.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index 3b66fa410e..f3cdb9e0ba 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -280,7 +280,9 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) grub_size_t tsize = 0, talign = 1, arch_addralign = 1; #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) grub_size_t tramp; + grub_size_t tramp_align; grub_size_t got; + grub_size_t got_align; grub_err_t err; #endif char *ptr; @@ -311,12 +313,18 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got); if (err) return err; - tsize += ALIGN_UP (tramp, GRUB_ARCH_DL_TRAMP_ALIGN); - if (talign < GRUB_ARCH_DL_TRAMP_ALIGN) - talign = GRUB_ARCH_DL_TRAMP_ALIGN; - tsize += ALIGN_UP (got, GRUB_ARCH_DL_GOT_ALIGN); - if (talign < GRUB_ARCH_DL_GOT_ALIGN) - talign = GRUB_ARCH_DL_GOT_ALIGN; + tramp_align = GRUB_ARCH_DL_TRAMP_ALIGN; + if (tramp_align < arch_addralign) + tramp_align = arch_addralign; + tsize += ALIGN_UP (tramp, tramp_align); + if (talign < tramp_align) + talign = tramp_align; + got_align = GRUB_ARCH_DL_GOT_ALIGN; + if (got_align < arch_addralign) + got_align = arch_addralign; + tsize += ALIGN_UP (got, got_align); + if (talign < got_align) + talign = got_align; #endif #ifdef GRUB_MACHINE_EMU @@ -376,11 +384,11 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) } } #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) - ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_TRAMP_ALIGN); + ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, tramp_align); mod->tramp = ptr; mod->trampptr = ptr; ptr += tramp; - ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_GOT_ALIGN); + ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, got_align); mod->got = ptr; mod->gotptr = ptr; ptr += got; From 7de33f4072abf5f14c4fdad6e566c58bdbf3b26e Mon Sep 17 00:00:00 2001 From: Nicolas Frayer Date: Fri, 31 Mar 2023 20:47:58 +0200 Subject: [PATCH 332/367] emu: Add switch-root to grub-emu If the kernel running grub emu is the same as the one we want to boot, it makes sense that we just switch-root instead of kexec the same kernel again by doing grub2-emu --switch-root Signed-off-by: Nicolas Frayer --- grub-core/kern/emu/main.c | 5 +- grub-core/kern/emu/misc.c | 13 +++ grub-core/loader/emu/linux.c | 209 ++++++++++++++++++++++++++++++++++- include/grub/emu/exec.h | 2 +- include/grub/emu/misc.h | 2 + 5 files changed, 223 insertions(+), 8 deletions(-) diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c index 68e2b283bb..ccb2863f5b 100644 --- a/grub-core/kern/emu/main.c +++ b/grub-core/kern/emu/main.c @@ -108,6 +108,7 @@ static struct argp_option options[] = { {"verbose", 'v', 0, 0, N_("print verbose messages."), 0}, {"hold", 'H', N_("SECS"), OPTION_ARG_OPTIONAL, N_("wait until a debugger will attach"), 0}, {"kexec", 'X', 0, 0, N_("use kexec to boot Linux kernels via systemctl (pass twice to enable dangerous fallback to non-systemctl)."), 0}, + {"switch-root", 'W', 0, 0, N_("use switch-root to only switch root filesystem without restarting the kernel."), 0}, { 0, 0, 0, 0, 0, 0 } }; @@ -168,7 +169,9 @@ argp_parser (int key, char *arg, struct argp_state *state) case 'X': grub_util_set_kexecute (); break; - + case 'W': + grub_util_set_switch_root (); + break; case ARGP_KEY_ARG: { /* Too many arguments. */ diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c index 02d27c3440..4b5123ef96 100644 --- a/grub-core/kern/emu/misc.c +++ b/grub-core/kern/emu/misc.c @@ -40,6 +40,7 @@ int verbosity; int kexecute; +int switchroot = 0; void grub_util_warn (const char *fmt, ...) @@ -231,3 +232,15 @@ grub_util_get_kexecute (void) { return kexecute; } + +void +grub_util_set_switch_root (void) +{ + switchroot = 1; +} + +int +grub_util_get_switch_root (void) +{ + return switchroot; +} diff --git a/grub-core/loader/emu/linux.c b/grub-core/loader/emu/linux.c index 7de3f7f861..6feb0412c5 100644 --- a/grub-core/loader/emu/linux.c +++ b/grub-core/loader/emu/linux.c @@ -15,7 +15,6 @@ * You should have received a copy of the GNU General Public License * along with GRUB. If not, see . */ - #include #include #include @@ -33,6 +32,196 @@ static char *kernel_path; static char *initrd_path; static char *boot_cmdline; +static grub_err_t +grub_switch_root (void) +{ + char *tmp = NULL; + char *options_cmd = NULL; + char *options = NULL; + char *subvol = NULL; + char *root_uuid = NULL; + char *kernel_release = NULL; + grub_err_t rc = GRUB_ERR_NONE; + const char *subvol_param = "subvol="; + const char *kernel_release_prefix = "/boot/vmlinuz-"; + const char *root_prefix = "root="; + const char *systemctl[] = {"systemctl", "--force", "switch-root", "/sysroot", NULL}; + const char *mountrootfs[] = {"mount", root_uuid, "/sysroot", options_cmd, options, NULL}; + const char *unamer[] = {"uname", "-r", NULL}; + char *uname_buf = NULL; + int i = 0; + + /* Extract the kernel release tag from kernel_path */ + if (!kernel_path) + { + rc = GRUB_ERR_BAD_ARGUMENT; + grub_dprintf ("linux", "switch_root: No kernel_path found\n"); + goto out; + } + + if ((kernel_release = grub_xasprintf ("%s", (kernel_path + grub_strlen (kernel_release_prefix)))) == NULL) + { + grub_dprintf ("linux", "switch_root: Failed to allocate memory\n"); + rc = GRUB_ERR_BAD_ARGUMENT; + goto out; + } + + + /* Check for kernel mismatch */ + /* Retrieve the current kernel relase tag */ + grub_util_exec_redirect (unamer, NULL, "/tmp/version"); + + grub_file_t f = grub_file_open ("/tmp/version", GRUB_FILE_TYPE_FS_SEARCH); + + if (f == NULL) + { + grub_dprintf ("linux", "failed opening file.\n"); + rc = GRUB_ERR_FILE_NOT_FOUND; + goto out; + } + + if ((uname_buf = grub_malloc (f->size)) == NULL) + { + grub_dprintf ("linux", "switch_root: Failed to allocate memory\n"); + rc = GRUB_ERR_OUT_OF_MEMORY; + goto out; + } + + if (grub_file_read (f, uname_buf, f->size) < 0) + { + grub_dprintf ("linux", "switch_root: failed to read from file\n"); + rc = GRUB_ERR_FILE_READ_ERROR; + goto out; + } + + grub_file_close (f); + + if (grub_strstr (uname_buf, kernel_release) == NULL) + { + grub_dprintf ("linux", "switch_root: kernel mismatch, not performing switch-root ...\n"); + rc = GRUB_ERR_NO_KERNEL; + goto out; + } + + /* Extract the root partition from boot_cmdline */ + if (!boot_cmdline) + { + rc = GRUB_ERR_BAD_ARGUMENT; + goto out; + } + + tmp = grub_strdup (boot_cmdline); + + if (tmp == NULL) + { + rc = GRUB_ERR_OUT_OF_MEMORY; + goto out; + } + + if ((root_uuid = grub_strstr (tmp, root_prefix)) == NULL) + { + rc = GRUB_ERR_BAD_ARGUMENT; + grub_dprintf ("linux", "switch_root: Can't find rootfs\n"); + goto out; + } + + root_uuid += grub_strlen (root_prefix); + + while (root_uuid[i] != ' ' && root_uuid[i] != '\0') + i++; + + root_uuid[i] = '\0'; + + /* Allocate a new buffer holding root_uuid */ + root_uuid = grub_xasprintf ("%s", root_uuid); + + if (root_uuid == NULL) + { + grub_dprintf ("linux", "switch_root: Failed to allocated memory\n"); + rc = GRUB_ERR_OUT_OF_MEMORY; + goto out; + } + + /* Check for subvol parameter */ + grub_strcpy (tmp, boot_cmdline); + + if ((subvol = grub_strstr(tmp, subvol_param)) != NULL) + { + i = 0; + + while (subvol[i] != ' ' && subvol[i] != '\0') + i++; + + subvol[i] = '\0'; + + /* Allocate a new buffer holding subvol */ + subvol = grub_xasprintf("%s", subvol); + + if (subvol == NULL) + { + grub_dprintf ("linux", "switch_root: Failed to allocated memory\n"); + rc = GRUB_ERR_OUT_OF_MEMORY; + goto out; + } + + options_cmd = grub_xasprintf("%s", "-o"); + options = grub_xasprintf("%s", subvol); + } + + if (options == NULL) + { + mountrootfs[3] = NULL; + } + else + { + mountrootfs[3] = options_cmd; + mountrootfs[4] = options; + } + + mountrootfs[1] = root_uuid; + + grub_dprintf ("linux", "Executing:\n"); + grub_dprintf ("linux", "%s %s %s %s %s\n", mountrootfs[0], mountrootfs[1], + mountrootfs[2], mountrootfs[3], mountrootfs[4]); + + /* Mount the rootfs */ + rc = grub_util_exec (mountrootfs); + + if (rc != GRUB_ERR_NONE) + { + grub_dprintf ("linux", "switch_root: Failed.\n"); + rc = GRUB_ERR_INVALID_COMMAND; + goto out; + } + + grub_dprintf ("linux", "Done.\n"); + + grub_dprintf ("linux", "%s %s %s %s\n", systemctl[0], systemctl[1], + systemctl[2], systemctl[3]); + + /* Switch root */ + rc = grub_util_exec (systemctl); + + if (rc != GRUB_ERR_NONE) + { + grub_dprintf ("linux", "switch_root: Failed.\n"); + rc = GRUB_ERR_INVALID_COMMAND; + goto out; + } + + grub_dprintf ("linux", "Done.\n"); + +out: + grub_free (tmp); + grub_free (options_cmd); + grub_free (options); + grub_free (subvol); + grub_free (root_uuid); + grub_free (uname_buf); + grub_free (kernel_release); + return rc; +} + static grub_err_t grub_linux_boot (void) { @@ -51,12 +240,20 @@ grub_linux_boot (void) else initrd_param = grub_xasprintf ("%s", ""); - grub_dprintf ("linux", "%serforming 'kexec -la %s %s %s'\n", - (kexecute) ? "P" : "Not p", - kernel_path, initrd_param, boot_cmdline); + if (grub_util_get_switch_root() == 1) + { + rc = grub_switch_root(); + if (rc != GRUB_ERR_NONE) + grub_fatal (N_("Failed to execute switch_root\n")); + } + else if (kexecute) + { + grub_dprintf ("linux", "%serforming 'kexec -la %s %s %s'\n", + (kexecute) ? "P" : "Not p", + kernel_path, initrd_param, boot_cmdline); - if (kexecute) - rc = grub_util_exec (kexec); + rc = grub_util_exec (kexec); + } grub_free (initrd_param); diff --git a/include/grub/emu/exec.h b/include/grub/emu/exec.h index 1b61b4a2e5..e82f13215e 100644 --- a/include/grub/emu/exec.h +++ b/include/grub/emu/exec.h @@ -36,7 +36,7 @@ grub_util_exec_redirect_all (const char *const *argv, const char *stdin_file, int EXPORT_FUNC(grub_util_exec) (const char *const *argv); int -grub_util_exec_redirect (const char *const *argv, const char *stdin_file, +EXPORT_FUNC(grub_util_exec_redirect) (const char *const *argv, const char *stdin_file, const char *stdout_file); int grub_util_exec_redirect_null (const char *const *argv); diff --git a/include/grub/emu/misc.h b/include/grub/emu/misc.h index 01056954b9..f3a712a8b2 100644 --- a/include/grub/emu/misc.h +++ b/include/grub/emu/misc.h @@ -59,6 +59,8 @@ void EXPORT_FUNC(grub_util_error) (const char *fmt, ...) __attribute__ ((format void EXPORT_FUNC(grub_util_set_kexecute) (void); int EXPORT_FUNC(grub_util_get_kexecute) (void) WARN_UNUSED_RESULT; +void EXPORT_FUNC(grub_util_set_switch_root) (void); +int EXPORT_FUNC(grub_util_get_switch_root) (void); grub_uint64_t EXPORT_FUNC (grub_util_get_cpu_time_ms) (void); From c98f9ea0938bca80e3bf36f90212bc5403d4e597 Mon Sep 17 00:00:00 2001 From: Marta Lewandowska Date: Wed, 24 May 2023 11:22:47 +0200 Subject: [PATCH 333/367] util: Enable default kernel for updates Several kernel variants can be installed on a system in parallel. In order to allow the user to choose which kernel will be set to default after an update, re-enable grub's usage of DEFAULTKERNEL as set in /etc/sysconfig/kernel Signed-off-by: Marta Lewandowska --- util/grub-get-kernel-settings.in | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/util/grub-get-kernel-settings.in b/util/grub-get-kernel-settings.in index 7e87dfccc0..f71bc64360 100644 --- a/util/grub-get-kernel-settings.in +++ b/util/grub-get-kernel-settings.in @@ -68,6 +68,14 @@ if test -f /etc/sysconfig/kernel ; then . /etc/sysconfig/kernel fi +GRUB_DEFAULT_KERNEL_TYPE=${DEFAULTKERNEL/-core/} +if [ "$GRUB_DEFAULT_KERNEL_TYPE" != "kernel" ]; then + echo GRUB_NON_STANDARD_KERNEL=true + echo export GRUB_NON_STANDARD_KERNEL + GRUB_DEFAULT_KERNEL_TYPE=${GRUB_DEFAULT_KERNEL_TYPE/kernel-/} +fi +echo GRUB_DEFAULT_KERNEL_TYPE=$GRUB_DEFAULT_KERNEL_TYPE +echo export GRUB_DEFAULT_KERNEL_TYPE if [ "$MAKEDEBUG" = "yes" ]; then echo GRUB_LINUX_MAKE_DEBUG=true echo export GRUB_LINUX_MAKE_DEBUG From 205b7b44bb2a7911f1e8c1ce22d66c111a4b429e Mon Sep 17 00:00:00 2001 From: Keng-Yu Lin Date: Wed, 26 Apr 2023 01:43:16 -0400 Subject: [PATCH 334/367] efi/http: change uint32_t to uintn_t Modify UINT32 to UINTN in EFI_HTTP_MESSAGE to be UEFI 2.9 compliant. Signed-off-by: Keng-Yu Lin Signed-off-by: Nicolas Frayer --- include/grub/efi/http.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/grub/efi/http.h b/include/grub/efi/http.h index c5e9a89f50..ad164ba191 100644 --- a/include/grub/efi/http.h +++ b/include/grub/efi/http.h @@ -171,9 +171,9 @@ typedef struct { grub_efi_http_request_data_t *request; grub_efi_http_response_data_t *response; } data; - grub_efi_uint32_t header_count; + grub_efi_uintn_t header_count; grub_efi_http_header_t *headers; - grub_efi_uint32_t body_length; + grub_efi_uintn_t body_length; void *body; } grub_efi_http_message_t; From 46ca707fe0be12fa9fd1f379ba5dbb3cb23835b6 Mon Sep 17 00:00:00 2001 From: Christian Glombek Date: Wed, 9 Aug 2023 18:11:25 +0200 Subject: [PATCH 335/367] Add [Install] section to aux systemd units Currently in Fedora, these services are statically enabled by symlinks, with no other way to disable them than to manually delete those symlinks. This is problematic in Fedora IoT, where grub-boot-success.timer is not supposed to be enabled. This change adds `[Install]` sections to all systemd units that are currently enabled statically, so that they can be enabled dynamically via presets or manually instead. --- docs/grub-boot-indeterminate.service | 3 +++ docs/grub-boot-success.timer | 3 +++ util/systemd/grub-systemd-integration.service.in | 3 +++ 3 files changed, 9 insertions(+) diff --git a/docs/grub-boot-indeterminate.service b/docs/grub-boot-indeterminate.service index 6c8dcb186b..5bcb474a3d 100644 --- a/docs/grub-boot-indeterminate.service +++ b/docs/grub-boot-indeterminate.service @@ -9,3 +9,6 @@ Before=system-update-pre.target [Service] Type=oneshot ExecStart=/usr/bin/grub2-editenv - incr boot_indeterminate + +[Install] +WantedBy=system-update.target diff --git a/docs/grub-boot-success.timer b/docs/grub-boot-success.timer index 406f172005..1d124cccc1 100644 --- a/docs/grub-boot-success.timer +++ b/docs/grub-boot-success.timer @@ -5,3 +5,6 @@ ConditionVirtualization=!container [Timer] OnActiveSec=2min + +[Install] +WantedBy=timers.target diff --git a/util/systemd/grub-systemd-integration.service.in b/util/systemd/grub-systemd-integration.service.in index c81fb594ce..22ca1ca488 100644 --- a/util/systemd/grub-systemd-integration.service.in +++ b/util/systemd/grub-systemd-integration.service.in @@ -6,3 +6,6 @@ ConditionPathExists=/run/systemd/reboot-to-boot-loader-menu [Service] ExecStart=@libexecdir@/@grubdirname@/systemd-integration.sh + +[Install] +WantedBy=reboot.target From dcfbf44ade8e2c48a945e4c5976eb877ab8414ff Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Tue, 28 Jun 2022 23:06:46 +0200 Subject: [PATCH 336/367] arm64: Use proper memory type for kernel allocation Currently, the kernel pages are allocated with type EFI_LOADER_DATA. While the vast majority of systems will happily execute code from those pages (i.e. don't care about memory protection), the Microsoft Surface Pro X stalls, as this memory is not designated as "executable". Therefore, allocate the kernel pages as EFI_LOADER_CODE to request memory that is actually executable. Signed-off-by: Maximilian Luz --- grub-core/loader/arm64/linux.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c index 419f2201df..a3a193c255 100644 --- a/grub-core/loader/arm64/linux.c +++ b/grub-core/loader/arm64/linux.c @@ -26,7 +26,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -403,7 +405,10 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_loader_unset(); kernel_alloc_pages = GRUB_EFI_BYTES_TO_PAGES (kernel_size + align - 1); - kernel_alloc_addr = grub_efi_allocate_any_pages (kernel_alloc_pages); + kernel_alloc_addr = grub_efi_allocate_pages_real (GRUB_EFI_MAX_USABLE_ADDRESS, + kernel_alloc_pages, + GRUB_EFI_ALLOCATE_MAX_ADDRESS, + GRUB_EFI_LOADER_CODE); grub_dprintf ("linux", "kernel numpages: %d\n", kernel_alloc_pages); if (!kernel_alloc_addr) { From 2921df057ab3345c7aa0a6cff71a9442256c1922 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 29 Sep 2023 10:56:11 -0400 Subject: [PATCH 337/367] Fix missing #include in ofdisk.c Recently we started building with -Werror=implicit-function-declaration, and discovered that ofdisk.c is missing an include to declare grub_env_get(). This patch adds that #include. Signed-off-by: Peter Jones --- grub-core/disk/ieee1275/ofdisk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/grub-core/disk/ieee1275/ofdisk.c b/grub-core/disk/ieee1275/ofdisk.c index 55346849d3..6e33d5dcce 100644 --- a/grub-core/disk/ieee1275/ofdisk.c +++ b/grub-core/disk/ieee1275/ofdisk.c @@ -24,6 +24,7 @@ #include #include #include +#include static char *last_devpath; static grub_ieee1275_ihandle_t last_ihandle; From 8cfc6a3f43a00d3723ff1b11e863109d3127c933 Mon Sep 17 00:00:00 2001 From: Marta Lewandowska Date: Mon, 9 Oct 2023 08:53:18 +0200 Subject: [PATCH 338/367] add flag to only search root dev fixes bz#2223437 Signed-off-by: Marta Lewandowska --- grub-core/commands/search.c | 8 ++++++++ grub-core/commands/search_wrap.c | 5 +++++ include/grub/search.h | 3 ++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c index 57d26ced8a..188d4c49d1 100644 --- a/grub-core/commands/search.c +++ b/grub-core/commands/search.c @@ -59,6 +59,7 @@ static int iterate_device (const char *name, void *data) { struct search_ctx *ctx = data; + const char *root_dev; int found = 0; /* Skip floppy drives when requested. */ @@ -85,6 +86,13 @@ iterate_device (const char *name, void *data) grub_device_close (dev); } + /* Skip it if it's not the root device when requested. */ + root_dev = grub_env_get ("root"); + if (ctx->flags & SEARCH_FLAGS_ROOTDEV_ONLY && + (name[0] != root_dev[0] || name[1] != root_dev[1] || + name[2] != root_dev[2])) + return 0; + #ifdef DO_SEARCH_FS_UUID #define compare_fn grub_strcasecmp #else diff --git a/grub-core/commands/search_wrap.c b/grub-core/commands/search_wrap.c index 0b62acf853..06b5f51eef 100644 --- a/grub-core/commands/search_wrap.c +++ b/grub-core/commands/search_wrap.c @@ -41,6 +41,7 @@ static const struct grub_arg_option options[] = ARG_TYPE_STRING}, {"no-floppy", 'n', 0, N_("Do not probe any floppy drive."), 0, 0}, {"efidisk-only", 0, 0, N_("Only probe EFI disks."), 0, 0}, + {"root-dev-only", 'r', 0, N_("Only probe root device."), 0, 0}, {"hint", 'h', GRUB_ARG_OPTION_REPEATABLE, N_("First try the device HINT. If HINT ends in comma, " "also try subpartitions"), N_("HINT"), ARG_TYPE_STRING}, @@ -75,6 +76,7 @@ enum options SEARCH_SET, SEARCH_NO_FLOPPY, SEARCH_EFIDISK_ONLY, + SEARCH_ROOTDEV_ONLY, SEARCH_HINT, SEARCH_HINT_IEEE1275, SEARCH_HINT_BIOS, @@ -189,6 +191,9 @@ grub_cmd_search (grub_extcmd_context_t ctxt, int argc, char **args) if (state[SEARCH_EFIDISK_ONLY].set) flags |= SEARCH_FLAGS_EFIDISK_ONLY; + if (state[SEARCH_ROOTDEV_ONLY].set) + flags |= SEARCH_FLAGS_ROOTDEV_ONLY; + if (state[SEARCH_LABEL].set) grub_search_label (id, var, flags, hints, nhints); else if (state[SEARCH_FS_UUID].set) diff --git a/include/grub/search.h b/include/grub/search.h index 4190aeb2cb..321d1400e4 100644 --- a/include/grub/search.h +++ b/include/grub/search.h @@ -22,7 +22,8 @@ enum search_flags { SEARCH_FLAGS_NO_FLOPPY = 1, - SEARCH_FLAGS_EFIDISK_ONLY = 2 + SEARCH_FLAGS_EFIDISK_ONLY = 2, + SEARCH_FLAGS_ROOTDEV_ONLY = 4 }; void grub_search_fs_file (const char *key, const char *var, From 9f0f2dfe7802f6f8221e09b75d04b2c03e443eb4 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Tue, 25 Jul 2023 13:23:10 -0400 Subject: [PATCH 339/367] kern/ieee1275/init: ppc64: Restrict high memory in presence of fadump When a kernel dump is present then restrict the high memory regions to avoid allocating memory where the kernel dump resides. Use the ibm,kernel-dump node under /rtas to determine whether a kernel dump exists and up to which limit grub can use available memory. Set the upper_mem_limit to the size of the kernel dump section of type 'REAL_MODE_REGION' and therefore only allow grub's memory usage for high addresses from RMO_ADDR_MAX to 'upper_mem_limit'. This means that grub can use high memory in the range of RMO_ADDR_MAX (768MB) to upper_mem_limit and the kernel-dump memory regions above 'upper_mem_limit' remain untouched. This change has no effect on memory allocations below 'linux_rmo_save' (typically at 640MB). Also, fall back to allocating below rmo_linux_save in case the chunk of memory there would be larger than the chunk of memory above RMO_ADDR_MAX. This can for example occur if a free memory area is found starting at 300MB extending up to 1GB but a kernel dump is located at 768MB and therefore does not allow the allocation of the high memory area but requiring to use the chunk starting at 300MB to avoid an unnecessary out-of-memory condition. Signed-off-by: Stefan Berger Reviewed-by: Hari Bathini Cc: Pavithra Prakash Cc: Michael Ellerman Cc: Carolyn Scherrer Cc: Mahesh Salgaonkar Cc: Sourabh Jain --- v2: check_kernel dump returns void now --- grub-core/kern/ieee1275/init.c | 144 ++++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 2 deletions(-) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 3d4ad9d1f1..8e7f742fad 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -17,6 +17,8 @@ * along with GRUB. If not, see . */ +#include /* offsetof() */ + #include #include #include @@ -198,6 +200,96 @@ grub_claim_heap (void) #else /* Helpers for mm on powerpc. */ +/* ibm,kernel-dump data structures */ +struct kd_section +{ + grub_uint32_t flags; + grub_uint16_t src_datatype; +#define KD_SRC_DATATYPE_REAL_MODE_REGION 0x0011 + grub_uint16_t error_flags; + grub_uint64_t src_address; + grub_uint64_t num_bytes; + grub_uint64_t act_bytes; + grub_uint64_t dst_address; +} GRUB_PACKED; + +#define MAX_KD_SECTIONS 10 + +struct kernel_dump +{ + grub_uint32_t format; + grub_uint16_t num_sections; + grub_uint16_t status_flags; + grub_uint32_t offset_1st_section; + grub_uint32_t num_blocks; + grub_uint64_t start_block; + grub_uint64_t num_blocks_avail; + grub_uint32_t offet_path_string; + grub_uint32_t max_time_allowed; + struct kd_section kds[MAX_KD_SECTIONS]; /* offset_1st_section should point to kds[0] */ +} GRUB_PACKED; + +/* + * Determine if a kernel dump exists and if it does, then determine the highest + * address that grub can use for memory allocations. + * The caller must have initialized *highest to rmo_top. *highest will not + * be modified if no kernel dump is found. + */ +static void +check_kernel_dump (grub_uint64_t *highest) +{ + struct kernel_dump kernel_dump; + grub_ssize_t kernel_dump_size; + grub_ieee1275_phandle_t rtas; + struct kd_section *kds; + grub_size_t i; + + /* If there's a kernel-dump it must have at least one section */ + if (grub_ieee1275_finddevice ("/rtas", &rtas) || + grub_ieee1275_get_property (rtas, "ibm,kernel-dump", &kernel_dump, + sizeof (kernel_dump), &kernel_dump_size) || + kernel_dump_size <= (grub_ssize_t) offsetof (struct kernel_dump, kds[1])) + return; + + kernel_dump_size = grub_min (kernel_dump_size, (grub_ssize_t) sizeof (kernel_dump)); + + if (grub_be_to_cpu32 (kernel_dump.format) != 1) + { + grub_printf (_("Error: ibm,kernel-dump has an unexpected format version '%u'\n"), + grub_be_to_cpu32 (kernel_dump.format)); + return; + } + + if (grub_be_to_cpu16 (kernel_dump.num_sections) > MAX_KD_SECTIONS) + { + grub_printf (_("Error: Too many kernel dump sections: %d\n"), + grub_be_to_cpu32 (kernel_dump.num_sections)); + return; + } + + for (i = 0; i < grub_be_to_cpu16 (kernel_dump.num_sections); i++) + { + kds = (struct kd_section *) ((grub_addr_t) &kernel_dump + + grub_be_to_cpu32 (kernel_dump.offset_1st_section) + + i * sizeof (struct kd_section)); + /* sanity check the address is within the 'kernel_dump' struct */ + if ((grub_addr_t) kds > (grub_addr_t) &kernel_dump + kernel_dump_size + sizeof (*kds)) + { + grub_printf (_("Error: 'kds' address beyond last available section\n")); + return; + } + + if ((grub_be_to_cpu16 (kds->src_datatype) == KD_SRC_DATATYPE_REAL_MODE_REGION) && + (grub_be_to_cpu64 (kds->src_address) == 0)) + { + *highest = grub_min (*highest, grub_be_to_cpu64 (kds->num_bytes)); + break; + } + } + + return; +} + /* * How much memory does OF believe exists in total? * @@ -277,10 +369,31 @@ regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, * * Finally, we also want to make sure that when grub loads the kernel, * it isn't going to use up all the memory we're trying to reserve! So - * enforce our entire RUNTIME_MIN_SPACE here: + * enforce our entire RUNTIME_MIN_SPACE here (no fadump): + * + * | Top of memory == upper_mem_limit -| + * | | + * | available | + * | | + * |---------- 768 MB ----------| + * | | + * | reserved | + * | | + * |--- 768 MB - runtime min space ---| + * | | + * | available | + * | | + * |---------- 0 MB ----------| + * + * In case fadump is used, we allow the following: * * |---------- Top of memory ----------| * | | + * | unavailable | + * | (kernel dump area) | + * | | + * |--------- upper_mem_limit ---------| + * | | * | available | * | | * |---------- 768 MB ----------| @@ -335,17 +448,44 @@ regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, } else { + grub_uint64_t upper_mem_limit = rmo_top; + grub_uint64_t orig_addr = addr; + + check_kernel_dump (&upper_mem_limit); + /* * we order these cases to prefer higher addresses and avoid some * splitting issues + * The following shows the order of variables: + * no kernel dump: linux_rmo_save < RMO_ADDR_MAX <= upper_mem_limit == rmo_top + * with kernel dump: liuxx_rmo_save < RMO_ADDR_MAX <= upper_mem_limit <= rmo_top */ - if (addr < RMO_ADDR_MAX && (addr + len) > RMO_ADDR_MAX) + if (addr < RMO_ADDR_MAX && (addr + len) > RMO_ADDR_MAX && upper_mem_limit >= RMO_ADDR_MAX) { grub_dprintf ("ieee1275", "adjusting region for RUNTIME_MIN_SPACE: (%llx -> %llx) -> (%llx -> %llx)\n", addr, addr + len, RMO_ADDR_MAX, addr + len); len = (addr + len) - RMO_ADDR_MAX; addr = RMO_ADDR_MAX; + + /* We must not exceed the upper_mem_limit (assuming it's >= RMO_ADDR_MAX) */ + if (addr + len > upper_mem_limit) + { + /* take the bigger chunk from either below linux_rmo_save or above upper_mem_limit */ + len = upper_mem_limit - addr; + if (orig_addr < linux_rmo_save && linux_rmo_save - orig_addr > len) + { + /* lower part is bigger */ + addr = orig_addr; + len = linux_rmo_save - addr; + } + + grub_dprintf ("ieee1275", "re-adjusted region to: (%llx -> %llx)\n", + addr, addr + len); + + if (len == 0) + return 0; + } } else if ((addr < linux_rmo_save) && ((addr + len) > linux_rmo_save)) { From 0d383a5d4cf06e57e8cede31610118808af876fb Mon Sep 17 00:00:00 2001 From: Marta Lewandowska Date: Fri, 13 Oct 2023 09:13:41 +0200 Subject: [PATCH 340/367] grub-install on EFI if forced UEFI Secure Boot requires signed grub binaries to work, so grub- install should not be used. However, users who have Secure Boot disabled and wish to use the command should not be prevented from doing so if they invoke --force. fixes bz#1917213 / bz#2240994 Signed-off-by: Marta Lewandowska --- util/grub-install.c | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/util/grub-install.c b/util/grub-install.c index 5babc7af55..162162bec6 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -899,22 +899,6 @@ main (int argc, char *argv[]) platform = grub_install_get_target (grub_install_source_directory); - switch (platform) - { - case GRUB_INSTALL_PLATFORM_ARM_EFI: - case GRUB_INSTALL_PLATFORM_ARM64_EFI: - case GRUB_INSTALL_PLATFORM_I386_EFI: - case GRUB_INSTALL_PLATFORM_IA64_EFI: - case GRUB_INSTALL_PLATFORM_X86_64_EFI: - is_efi = 1; - grub_util_error (_("this utility cannot be used for EFI platforms" - " because it does not support UEFI Secure Boot")); - break; - default: - is_efi = 0; - break; - } - { char *platname = grub_install_get_platform_name (platform); fprintf (stderr, _("Installing for %s platform.\n"), platname); @@ -1027,6 +1011,32 @@ main (int argc, char *argv[]) grub_hostfs_init (); grub_host_init (); + switch (platform) + { + case GRUB_INSTALL_PLATFORM_I386_EFI: + case GRUB_INSTALL_PLATFORM_X86_64_EFI: + case GRUB_INSTALL_PLATFORM_ARM_EFI: + case GRUB_INSTALL_PLATFORM_ARM64_EFI: + case GRUB_INSTALL_PLATFORM_RISCV32_EFI: + case GRUB_INSTALL_PLATFORM_RISCV64_EFI: + case GRUB_INSTALL_PLATFORM_IA64_EFI: + is_efi = 1; + if (!force) + grub_util_error (_("This utility should not be used for EFI platforms" + " because it does not support UEFI Secure Boot." + " If you really wish to proceed, invoke the --force" + " option.\nMake sure Secure Boot is disabled before" + " proceeding")); + break; + default: + is_efi = 0; + break; + + /* pacify warning. */ + case GRUB_INSTALL_PLATFORM_MAX: + break; + } + /* Find the EFI System Partition. */ if (is_efi) { From 522adbffcebd55182be87ace96deef8b6ccc62ca Mon Sep 17 00:00:00 2001 From: Christian Glombek Date: Tue, 14 Nov 2023 07:54:59 +0100 Subject: [PATCH 341/367] Remove [Install] section from aux systemd units See https://bugzilla.redhat.com/show_bug.cgi?id=2247635#c7 --- docs/grub-boot-indeterminate.service | 3 --- util/systemd/grub-systemd-integration.service.in | 3 --- 2 files changed, 6 deletions(-) diff --git a/docs/grub-boot-indeterminate.service b/docs/grub-boot-indeterminate.service index 5bcb474a3d..6c8dcb186b 100644 --- a/docs/grub-boot-indeterminate.service +++ b/docs/grub-boot-indeterminate.service @@ -9,6 +9,3 @@ Before=system-update-pre.target [Service] Type=oneshot ExecStart=/usr/bin/grub2-editenv - incr boot_indeterminate - -[Install] -WantedBy=system-update.target diff --git a/util/systemd/grub-systemd-integration.service.in b/util/systemd/grub-systemd-integration.service.in index 22ca1ca488..c81fb594ce 100644 --- a/util/systemd/grub-systemd-integration.service.in +++ b/util/systemd/grub-systemd-integration.service.in @@ -6,6 +6,3 @@ ConditionPathExists=/run/systemd/reboot-to-boot-loader-menu [Service] ExecStart=@libexecdir@/@grubdirname@/systemd-integration.sh - -[Install] -WantedBy=reboot.target From 4017d58b00b4de44b40208aff62aa562d006b71e Mon Sep 17 00:00:00 2001 From: Elyes Haouas Date: Fri, 4 Mar 2022 07:41:59 +0100 Subject: [PATCH 342/367] fs: Remove trailing whitespaces Signed-off-by: Elyes Haouas Reviewed-by: Daniel Kiper --- grub-core/fs/affs.c | 4 +- grub-core/fs/archelp.c | 4 +- grub-core/fs/bfs.c | 10 +- grub-core/fs/cpio_common.c | 2 +- grub-core/fs/fat.c | 6 +- grub-core/fs/fshelp.c | 6 +- grub-core/fs/hfs.c | 14 +- grub-core/fs/hfsplus.c | 8 +- grub-core/fs/hfspluscomp.c | 6 +- grub-core/fs/iso9660.c | 12 +- grub-core/fs/minix.c | 6 +- grub-core/fs/nilfs2.c | 8 +- grub-core/fs/ntfs.c | 8 +- grub-core/fs/reiserfs.c | 2 +- grub-core/fs/romfs.c | 8 +- grub-core/fs/squash4.c | 26 ++-- grub-core/fs/udf.c | 2 +- grub-core/fs/ufs.c | 4 +- grub-core/fs/xfs.c | 2 +- grub-core/fs/zfs/zfs.c | 224 ++++++++++++++++---------------- grub-core/fs/zfs/zfs_fletcher.c | 12 +- grub-core/fs/zfs/zfs_sha256.c | 14 +- grub-core/fs/zfs/zfscrypt.c | 14 +- 23 files changed, 201 insertions(+), 201 deletions(-) diff --git a/grub-core/fs/affs.c b/grub-core/fs/affs.c index cafcd0fba9..0d90e9d1a8 100644 --- a/grub-core/fs/affs.c +++ b/grub-core/fs/affs.c @@ -345,7 +345,7 @@ grub_affs_create_node (grub_fshelp_node_t dir, if (len > sizeof (fil->name)) len = sizeof (fil->name); *grub_latin1_to_utf8 (name_u8, fil->name, len) = '\0'; - + (*node)->di = *fil; for (nest = 0; nest < 8; nest++) { @@ -408,7 +408,7 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir, node = orig_node = grub_zalloc (sizeof (*node)); if (!node) return 1; - + *node = *dir; if (hook (".", GRUB_FSHELP_DIR, node, hook_data)) return 1; diff --git a/grub-core/fs/archelp.c b/grub-core/fs/archelp.c index 0cf544f6fd..998de88b86 100644 --- a/grub-core/fs/archelp.c +++ b/grub-core/fs/archelp.c @@ -75,7 +75,7 @@ handle_symlink (struct grub_archelp_data *data, || !arcops->get_link_target) return GRUB_ERR_NONE; flen = grub_strlen (fn); - if (grub_memcmp (*name, fn, flen) != 0 + if (grub_memcmp (*name, fn, flen) != 0 || ((*name)[flen] != 0 && (*name)[flen] != '/')) return GRUB_ERR_NONE; rest = *name + flen; @@ -251,7 +251,7 @@ grub_archelp_open (struct grub_archelp_data *data, grub_uint32_t mode; grub_int32_t mtime; int restart; - + if (arcops->find_file (data, &fn, &mtime, &mode)) goto fail; diff --git a/grub-core/fs/bfs.c b/grub-core/fs/bfs.c index 47dbe2011a..a75876010d 100644 --- a/grub-core/fs/bfs.c +++ b/grub-core/fs/bfs.c @@ -530,13 +530,13 @@ iterate_in_b_tree (grub_disk_t disk, err = read_b_node (disk, sb, ino, node_off, &node, - &key_data, + &key_data, &keylen_idx, &key_values); if (err) return 0; - + for (i = 0; i < grub_bfs_to_cpu_treehead (node->count_keys); i++) { char c; @@ -682,7 +682,7 @@ find_in_b_tree (grub_disk_t disk, level--; grub_free (node); continue; - } + } } else if (level != 0 && i + 1 < grub_bfs_to_cpu_treehead (node->count_keys)) @@ -827,7 +827,7 @@ mount (grub_disk_t disk, struct grub_bfs_superblock *sb) grub_err_t err; err = grub_disk_read (disk, SUPERBLOCK, 0, sizeof (*sb), sb); if (err == GRUB_ERR_OUT_OF_RANGE) - return grub_error (GRUB_ERR_BAD_FS, + return grub_error (GRUB_ERR_BAD_FS, #ifdef MODE_AFS "not an AFS filesystem" #else @@ -843,7 +843,7 @@ mount (grub_disk_t disk, struct grub_bfs_superblock *sb) || (grub_bfs_to_cpu32 (sb->bsize) != (1U << grub_bfs_to_cpu32 (sb->log2_bsize))) || grub_bfs_to_cpu32 (sb->log2_bsize) < GRUB_DISK_SECTOR_BITS) - return grub_error (GRUB_ERR_BAD_FS, + return grub_error (GRUB_ERR_BAD_FS, #ifdef MODE_AFS "not an AFS filesystem" #else diff --git a/grub-core/fs/cpio_common.c b/grub-core/fs/cpio_common.c index 4e885d6231..5d41b6fdbe 100644 --- a/grub-core/fs/cpio_common.c +++ b/grub-core/fs/cpio_common.c @@ -117,7 +117,7 @@ grub_cpio_get_link_target (struct grub_archelp_data *data) if (!ret) return NULL; - err = grub_disk_read (data->disk, 0, data->dofs, data->size, + err = grub_disk_read (data->disk, 0, data->dofs, data->size, ret); if (err) { diff --git a/grub-core/fs/fat.c b/grub-core/fs/fat.c index ff6200c5be..c5efed7241 100644 --- a/grub-core/fs/fat.c +++ b/grub-core/fs/fat.c @@ -246,7 +246,7 @@ grub_fat_mount (grub_disk_t disk) #ifdef MODE_EXFAT if (grub_memcmp ((const char *) bpb.oem_name, "EXFAT ", sizeof (bpb.oem_name)) != 0) - goto fail; + goto fail; #endif /* Get the sizes of logical sectors and clusters. */ @@ -321,7 +321,7 @@ grub_fat_mount (grub_disk_t disk) #endif #ifdef MODE_EXFAT - data->cluster_sector = (grub_le_to_cpu32 (bpb.cluster_offset) + data->cluster_sector = (grub_le_to_cpu32 (bpb.cluster_offset) << data->logical_sector_bits); data->num_clusters = (grub_le_to_cpu32 (bpb.cluster_count) << data->logical_sector_bits); @@ -694,7 +694,7 @@ grub_fat_iterate_dir_next (grub_fshelp_node_t node, { int j; for (j = 0; j < 15; j++) - ctxt->unibuf[slots * 15 + j] + ctxt->unibuf[slots * 15 + j] = grub_le_to_cpu16 (sec.type_specific.file_name.str[j]); slots++; } diff --git a/grub-core/fs/fshelp.c b/grub-core/fs/fshelp.c index a2d0d297a5..cb41934b4f 100644 --- a/grub-core/fs/fshelp.c +++ b/grub-core/fs/fshelp.c @@ -215,7 +215,7 @@ find_file (char *currpath, break; push_node (ctx, foundnode, foundtype); - + /* Read in the symlink and follow it. */ if (ctx->currnode->type == GRUB_FSHELP_SYMLINK) { @@ -326,7 +326,7 @@ grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode, enum grub_fshelp_filetype expecttype) { return grub_fshelp_find_file_real (path, rootnode, foundnode, - iterate_dir, NULL, + iterate_dir, NULL, read_symlink, expecttype); } @@ -339,7 +339,7 @@ grub_fshelp_find_file_lookup (const char *path, grub_fshelp_node_t rootnode, enum grub_fshelp_filetype expecttype) { return grub_fshelp_find_file_real (path, rootnode, foundnode, - NULL, lookup_file, + NULL, lookup_file, read_symlink, expecttype); } diff --git a/grub-core/fs/hfs.c b/grub-core/fs/hfs.c index f419965d15..91dc0e69c3 100644 --- a/grub-core/fs/hfs.c +++ b/grub-core/fs/hfs.c @@ -883,7 +883,7 @@ grub_hfs_iterate_dir_it_dir (struct grub_hfs_node *hnd __attribute ((unused)), { struct grub_hfs_catalog_key *ckey = rec->key; struct grub_hfs_iterate_dir_node_found_ctx *ctx = hook_arg; - + /* Stop when the entries do not match anymore. */ if (ckey->parent_dir != ctx->dir_be) return 1; @@ -1077,7 +1077,7 @@ macroman_to_utf8 (char *to, const grub_uint8_t *from, grub_size_t len, { *optr++ = ':'; continue; - } + } if (!(*iptr & 0x80)) { *optr++ = *iptr; @@ -1094,7 +1094,7 @@ utf8_to_macroman (grub_uint8_t *to, const char *from) grub_uint8_t *end = to + 31; grub_uint8_t *optr = to; const char *iptr = from; - + while (*iptr && optr < end) { int i, clen; @@ -1104,7 +1104,7 @@ utf8_to_macroman (grub_uint8_t *to, const char *from) *optr++ = '/'; iptr++; continue; - } + } if (!(*iptr & 0x80)) { *optr++ = *iptr++; @@ -1165,7 +1165,7 @@ lookup_file (grub_fshelp_node_t dir, *foundnode = grub_malloc (sizeof (struct grub_fshelp_node)); if (!*foundnode) return grub_errno; - + (*foundnode)->inode = grub_be_to_cpu32 (fdrec.dir.dirid); (*foundnode)->fdrec = fdrec; (*foundnode)->data = dir->data; @@ -1266,7 +1266,7 @@ grub_hfs_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook, .hook_data = hook_data }; grub_fshelp_node_t found = NULL; - + grub_dl_ref (my_mod); data = grub_hfs_mount (device->disk); @@ -1295,7 +1295,7 @@ grub_hfs_open (struct grub_file *file, const char *name) { struct grub_hfs_data *data; grub_fshelp_node_t found = NULL; - + grub_dl_ref (my_mod); data = grub_hfs_mount (file->device->disk); diff --git a/grub-core/fs/hfsplus.c b/grub-core/fs/hfsplus.c index 19c7b33679..6337cbfcbe 100644 --- a/grub-core/fs/hfsplus.c +++ b/grub-core/fs/hfsplus.c @@ -19,7 +19,7 @@ /* HFS+ is documented at http://developer.apple.com/technotes/tn/tn1150.html */ -#define grub_fshelp_node grub_hfsplus_file +#define grub_fshelp_node grub_hfsplus_file #include #include #include @@ -146,7 +146,7 @@ grub_hfsplus_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { struct grub_hfsplus_btnode *nnode = 0; grub_disk_addr_t blksleft = fileblock; - struct grub_hfsplus_extent *extents = node->compressed + struct grub_hfsplus_extent *extents = node->compressed ? &node->resource_extents[0] : &node->extents[0]; while (1) @@ -477,7 +477,7 @@ grub_hfsplus_cmp_extkey (struct grub_hfsplus_key *keya, if (extkey_a->type < extkey_b->type) return -1; - + akey = grub_be_to_cpu32 (extkey_a->start); if (akey > extkey_b->start) return 1; @@ -568,7 +568,7 @@ grub_hfsplus_btree_search (struct grub_hfsplus_btree *btree, struct grub_hfsplus_key_internal *key, int (*compare_keys) (struct grub_hfsplus_key *keya, struct grub_hfsplus_key_internal *keyb), - struct grub_hfsplus_btnode **matchnode, + struct grub_hfsplus_btnode **matchnode, grub_off_t *keyoffset) { grub_uint64_t currnode; diff --git a/grub-core/fs/hfspluscomp.c b/grub-core/fs/hfspluscomp.c index d76f3f137a..095ea48c9a 100644 --- a/grub-core/fs/hfspluscomp.c +++ b/grub-core/fs/hfspluscomp.c @@ -179,7 +179,7 @@ hfsplus_read_compressed_real (struct grub_hfsplus_file *node, return len0; } -static grub_err_t +static grub_err_t hfsplus_open_compressed_real (struct grub_hfsplus_file *node) { grub_err_t err; @@ -306,8 +306,8 @@ hfsplus_open_compressed_real (struct grub_hfsplus_file *node) GRUB_MOD_INIT(hfspluscomp) { - grub_hfsplus_open_compressed = hfsplus_open_compressed_real; - grub_hfsplus_read_compressed = hfsplus_read_compressed_real; + grub_hfsplus_open_compressed = hfsplus_open_compressed_real; + grub_hfsplus_read_compressed = hfsplus_read_compressed_real; } GRUB_MOD_FINI(hfspluscomp) diff --git a/grub-core/fs/iso9660.c b/grub-core/fs/iso9660.c index ac011950a6..91817ec1f5 100644 --- a/grub-core/fs/iso9660.c +++ b/grub-core/fs/iso9660.c @@ -181,7 +181,7 @@ static grub_err_t iso9660_to_unixtime (const struct grub_iso9660_date *i, grub_int64_t *nix) { struct grub_datetime datetime; - + if (! i->year[0] && ! i->year[1] && ! i->year[2] && ! i->year[3] && ! i->month[0] && ! i->month[1] @@ -198,7 +198,7 @@ iso9660_to_unixtime (const struct grub_iso9660_date *i, grub_int64_t *nix) datetime.hour = (i->hour[0] - '0') * 10 + (i->hour[1] - '0'); datetime.minute = (i->minute[0] - '0') * 10 + (i->minute[1] - '0'); datetime.second = (i->second[0] - '0') * 10 + (i->second[1] - '0'); - + if (!grub_datetime2unixtime (&datetime, nix)) return grub_error (GRUB_ERR_BAD_NUMBER, "incorrect date"); *nix -= i->offset * 60 * 15; @@ -216,7 +216,7 @@ iso9660_to_unixtime2 (const struct grub_iso9660_date2 *i, grub_int64_t *nix) datetime.hour = i->hour; datetime.minute = i->minute; datetime.second = i->second; - + if (!grub_datetime2unixtime (&datetime, nix)) return 0; *nix -= i->offset * 60 * 15; @@ -499,7 +499,7 @@ grub_iso9660_mount (grub_disk_t disk) static char * grub_iso9660_read_symlink (grub_fshelp_node_t node) { - return node->have_symlink + return node->have_symlink ? grub_strdup (node->symlink + (node->have_dirents) * sizeof (node->dirents[0]) - sizeof (node->dirents)) : grub_strdup (""); @@ -549,7 +549,7 @@ add_part (struct iterate_dir_ctx *ctx, ctx->symlink = new; grub_memcpy (ctx->symlink + size, part, len2); - ctx->symlink[size + len2] = 0; + ctx->symlink[size + len2] = 0; } static grub_err_t @@ -1106,7 +1106,7 @@ grub_iso9660_uuid (grub_device_t device, char **uuid) } /* Get writing time of filesystem. */ -static grub_err_t +static grub_err_t grub_iso9660_mtime (grub_device_t device, grub_int64_t *timebuf) { struct grub_iso9660_data *data; diff --git a/grub-core/fs/minix.c b/grub-core/fs/minix.c index 3cd18c85b3..953df11916 100644 --- a/grub-core/fs/minix.c +++ b/grub-core/fs/minix.c @@ -98,10 +98,10 @@ struct grub_minix_sblock grub_uint32_t max_file_size; grub_uint32_t zones; grub_uint16_t magic; - + grub_uint16_t pad2; grub_uint16_t block_size; - grub_uint8_t disk_version; + grub_uint8_t disk_version; }; #else struct grub_minix_sblock @@ -351,7 +351,7 @@ grub_minix_read_inode (struct grub_minix_data *data, grub_minix_ino_t ino) int offs = (ino % (GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_minix_inode)) * sizeof (struct grub_minix_inode)); - + grub_disk_read (data->disk, block, offs, sizeof (struct grub_minix_inode), &data->inode); diff --git a/grub-core/fs/nilfs2.c b/grub-core/fs/nilfs2.c index 3c248a910b..fc7374ead4 100644 --- a/grub-core/fs/nilfs2.c +++ b/grub-core/fs/nilfs2.c @@ -1,5 +1,5 @@ -/* - * nilfs2.c - New Implementation of Log filesystem +/* + * nilfs2.c - New Implementation of Log filesystem * * Written by Jiro SEKIBA * @@ -680,12 +680,12 @@ grub_nilfs2_read_checkpoint (struct grub_nilfs2_data *data, grub_disk_t disk = data->disk; unsigned int nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data)); - /* Assume sizeof(struct grub_nilfs2_cpfile_header) < + /* Assume sizeof(struct grub_nilfs2_cpfile_header) < sizeof(struct grub_nilfs2_checkpoint). */ blockno = grub_divmod64 (cpno, NILFS2_BLOCK_SIZE (data) / sizeof (struct grub_nilfs2_checkpoint), &offset); - + pptr = grub_nilfs2_bmap_lookup (data, &data->sroot.sr_cpfile, blockno, 1); if (pptr == (grub_uint64_t) - 1) { diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index 2f34f76da8..3511e4e2cb 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#define grub_fshelp_node grub_ntfs_file +#define grub_fshelp_node grub_ntfs_file #include #include @@ -32,7 +32,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); static grub_dl_t my_mod; -#define grub_fshelp_node grub_ntfs_file +#define grub_fshelp_node grub_ntfs_file static inline grub_uint16_t u16at (void *ptr, grub_size_t ofs) @@ -725,7 +725,7 @@ grub_ntfs_read_symlink (grub_fshelp_node_t node) && grub_isalpha (buf[4])) { grub_memmove (buf, buf + 6, end - buf + 1 - 6); - end -= 6; + end -= 6; } return buf; } @@ -992,7 +992,7 @@ grub_ntfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype, grub_memset (&info, 0, sizeof (info)); info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); info.mtimeset = 1; - info.mtime = grub_divmod64 (node->mtime, 10000000, 0) + info.mtime = grub_divmod64 (node->mtime, 10000000, 0) - 86400ULL * 365 * (1970 - 1601) - 86400ULL * ((1970 - 1601) / 4) + 86400ULL * ((1970 - 1601) / 100); grub_free (node); diff --git a/grub-core/fs/reiserfs.c b/grub-core/fs/reiserfs.c index b8253da7fe..42818c3762 100644 --- a/grub-core/fs/reiserfs.c +++ b/grub-core/fs/reiserfs.c @@ -775,7 +775,7 @@ grub_reiserfs_iterate_dir (grub_fshelp_node_t item, char *entry_name; char *entry_name_end = 0; char c; - + if (!(entry_state & GRUB_REISERFS_VISIBLE_MASK)) continue; diff --git a/grub-core/fs/romfs.c b/grub-core/fs/romfs.c index d97b8fbb8c..1f7dcfca1d 100644 --- a/grub-core/fs/romfs.c +++ b/grub-core/fs/romfs.c @@ -112,7 +112,7 @@ grub_romfs_mount (grub_device_t dev) { grub_error (GRUB_ERR_BAD_FS, "not romfs"); return NULL; - } + } err = do_checksum (&sb, sizeof (sb) < grub_be_to_cpu32 (sb.sb.total_size) ? sizeof (sb) : grub_be_to_cpu32 (sb.sb.total_size)); if (err) @@ -271,7 +271,7 @@ grub_romfs_iterate_dir (grub_fshelp_node_t dir, while (1) { char buf[16]; - err = grub_disk_read (dir->data->disk, + err = grub_disk_read (dir->data->disk, laddr >> GRUB_DISK_SECTOR_BITS, laddr & (GRUB_DISK_SECTOR_SIZE - 1), 16, buf); @@ -298,7 +298,7 @@ grub_romfs_iterate_dir (grub_fshelp_node_t dir, node->data_addr = grub_be_to_cpu32 (node->file.spec); filetype = GRUB_FSHELP_DIR; } - + break; } } @@ -402,7 +402,7 @@ grub_romfs_read (grub_file_t file, char *buf, grub_size_t len) data->data->disk->read_hook_data = file->read_hook_data; grub_disk_read (data->data->disk, (data->data_addr + file->offset) >> GRUB_DISK_SECTOR_BITS, - (data->data_addr + file->offset) & (GRUB_DISK_SECTOR_SIZE - 1), + (data->data_addr + file->offset) & (GRUB_DISK_SECTOR_SIZE - 1), len, buf); data->data->disk->read_hook = NULL; diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 6dd731e231..02b1f9b6d1 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -210,7 +210,7 @@ struct grub_fshelp_node struct grub_squash_data *data; struct grub_squash_inode ino; grub_size_t stsize; - struct + struct { grub_disk_addr_t ino_chunk; grub_uint16_t ino_offset; @@ -243,7 +243,7 @@ read_chunk (struct grub_squash_data *data, void *buf, grub_size_t len, csize = SQUASH_CHUNK_SIZE - offset; if (csize > len) csize = len; - + if (grub_le_to_cpu16 (d) & SQUASH_CHUNK_UNCOMPRESSED) { grub_disk_addr_t a = chunk_start + 2 + offset; @@ -256,7 +256,7 @@ read_chunk (struct grub_squash_data *data, void *buf, grub_size_t len, else { char *tmp; - grub_size_t bsize = grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS; + grub_size_t bsize = grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS; grub_disk_addr_t a = chunk_start + 2; tmp = grub_malloc (bsize); if (!tmp) @@ -339,7 +339,7 @@ xz_decompress (char *inbuf, grub_size_t insize, grub_off_t off, while (len) { enum xz_ret xzret; - + buf.out_pos = 0; xzret = xz_dec_run (data->xzdec, &buf); @@ -399,9 +399,9 @@ squash_mount (grub_disk_t disk) return NULL; } - err = grub_disk_read (disk, + err = grub_disk_read (disk, grub_le_to_cpu64 (sb.unk1offset) - >> GRUB_DISK_SECTOR_BITS, + >> GRUB_DISK_SECTOR_BITS, grub_le_to_cpu64 (sb.unk1offset) & (GRUB_DISK_SECTOR_SIZE - 1), sizeof (frag), &frag); if (grub_errno == GRUB_ERR_OUT_OF_RANGE) @@ -448,7 +448,7 @@ squash_mount (grub_disk_t disk) } data->blksz = grub_le_to_cpu32 (data->sb.block_size); - for (data->log2_blksz = 0; + for (data->log2_blksz = 0; (1U << data->log2_blksz) < data->blksz; data->log2_blksz++); @@ -643,7 +643,7 @@ make_root_node (struct grub_squash_data *data, struct grub_fshelp_node *root) root->stack[0].ino_chunk = grub_le_to_cpu32 (data->sb.root_ino_chunk); root->stack[0].ino_offset = grub_cpu_to_le16 (data->sb.root_ino_offset); return read_chunk (data, &root->ino, sizeof (root->ino), - grub_le_to_cpu64 (data->sb.inodeoffset) + grub_le_to_cpu64 (data->sb.inodeoffset) + root->stack[0].ino_chunk, root->stack[0].ino_offset); } @@ -765,7 +765,7 @@ grub_squash_open (struct grub_file *file, const char *name) } static grub_ssize_t -direct_read (struct grub_squash_data *data, +direct_read (struct grub_squash_data *data, struct grub_squash_cache_inode *ino, grub_off_t off, char *buf, grub_size_t len) { @@ -882,7 +882,7 @@ direct_read (struct grub_squash_data *data, grub_free (block); } else - err = grub_disk_read (data->disk, + err = grub_disk_read (data->disk, (ino->cumulated_block_sizes[i] + a + boff) >> GRUB_DISK_SECTOR_BITS, (ino->cumulated_block_sizes[i] + a + boff) @@ -946,7 +946,7 @@ grub_squash_read (grub_file_t file, char *buf, grub_size_t len) } else off -= direct_len; - + err = read_chunk (data, &frag, sizeof (frag), data->fragments, sizeof (frag) * fragment); if (err) @@ -957,7 +957,7 @@ grub_squash_read (grub_file_t file, char *buf, grub_size_t len) b = grub_le_to_cpu32 (ino->ino.long_file.offset) + off; else b = grub_le_to_cpu32 (ino->ino.file.offset) + off; - + /* FIXME: cache uncompressed chunks. */ if (compressed) { @@ -1013,7 +1013,7 @@ grub_squash_mtime (grub_device_t dev, grub_int64_t *tm) *tm = grub_le_to_cpu32 (data->sb.creation_time); squash_unmount (data); return GRUB_ERR_NONE; -} +} static struct grub_fs grub_squash_fs = { diff --git a/grub-core/fs/udf.c b/grub-core/fs/udf.c index 2ac5c1d004..5a665093ed 100644 --- a/grub-core/fs/udf.c +++ b/grub-core/fs/udf.c @@ -567,7 +567,7 @@ grub_udf_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) (buf + sizeof (struct grub_udf_aed)); continue; } - + if (filebytes < adlen) { grub_uint32_t ad_block_num = ad->block.block_num; diff --git a/grub-core/fs/ufs.c b/grub-core/fs/ufs.c index 34a698b71b..a354c92d93 100644 --- a/grub-core/fs/ufs.c +++ b/grub-core/fs/ufs.c @@ -568,7 +568,7 @@ grub_ufs_find_file (struct grub_ufs_data *data, const char *path) { dirino = data->ino; grub_ufs_read_inode (data, grub_ufs_to_cpu32 (dirent.ino), 0); - + if ((INODE_MODE(data) & GRUB_UFS_ATTR_TYPE) == GRUB_UFS_ATTR_LNK) { @@ -613,7 +613,7 @@ grub_ufs_mount (grub_disk_t disk) && ((data->sblock.bsize & (data->sblock.bsize - 1)) == 0) && data->sblock.ino_per_group != 0) { - for (data->log2_blksz = 0; + for (data->log2_blksz = 0; (1U << data->log2_blksz) < grub_ufs_to_cpu32 (data->sblock.bsize); data->log2_blksz++); diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index e3816d1ec4..d6de7f1a2d 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -894,7 +894,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, tag, which is not used by GRUB. So it can be overwritten. */ filename[direntry->len] = '\0'; - if (iterate_dir_call_hook (grub_be_to_cpu64(direntry->inode), + if (iterate_dir_call_hook (grub_be_to_cpu64(direntry->inode), filename, &ctx)) { grub_free (dirblock); diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index cf4d2ab189..5961818abc 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -176,7 +176,7 @@ typedef struct decomp_entry /* * Signature for checksum functions. */ -typedef void zio_checksum_t(const void *data, grub_uint64_t size, +typedef void zio_checksum_t(const void *data, grub_uint64_t size, grub_zfs_endian_t endian, zio_cksum_t *zcp); /* @@ -297,7 +297,7 @@ check_feature(const char *name, grub_uint64_t val, struct grub_zfs_dir_ctx *ctx) static grub_err_t check_mos_features(dnode_phys_t *mosmdn_phys,grub_zfs_endian_t endian,struct grub_zfs_data* data ); -static grub_err_t +static grub_err_t zlib_decompress (void *s, void *d, grub_size_t slen, grub_size_t dlen) { @@ -310,7 +310,7 @@ zlib_decompress (void *s, void *d, return grub_errno; } -static grub_err_t +static grub_err_t zle_decompress (void *s, void *d, grub_size_t slen, grub_size_t dlen) { @@ -415,7 +415,7 @@ static zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = { */ static grub_err_t zio_checksum_verify (zio_cksum_t zc, grub_uint32_t checksum, - grub_zfs_endian_t endian, + grub_zfs_endian_t endian, char *buf, grub_size_t size) { zio_eck_t *zec = (zio_eck_t *) (buf + size) - 1; @@ -425,14 +425,14 @@ zio_checksum_verify (zio_cksum_t zc, grub_uint32_t checksum, if (checksum >= ZIO_CHECKSUM_FUNCTIONS || ci->ci_func == NULL) { grub_dprintf ("zfs", "unknown checksum function %d\n", checksum); - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "unknown checksum function %d", checksum); } if (ci->ci_eck) { - expected_cksum = zec->zec_cksum; - zec->zec_cksum = zc; + expected_cksum = zec->zec_cksum; + zec->zec_cksum = zc; ci->ci_func (buf, size, endian, &actual_cksum); zec->zec_cksum = expected_cksum; zc = expected_cksum; @@ -445,14 +445,14 @@ zio_checksum_verify (zio_cksum_t zc, grub_uint32_t checksum, { grub_dprintf ("zfs", "checksum %s verification failed\n", ci->ci_name); grub_dprintf ("zfs", "actual checksum %016llx %016llx %016llx %016llx\n", - (unsigned long long) actual_cksum.zc_word[0], + (unsigned long long) actual_cksum.zc_word[0], (unsigned long long) actual_cksum.zc_word[1], - (unsigned long long) actual_cksum.zc_word[2], + (unsigned long long) actual_cksum.zc_word[2], (unsigned long long) actual_cksum.zc_word[3]); grub_dprintf ("zfs", "expected checksum %016llx %016llx %016llx %016llx\n", - (unsigned long long) zc.zc_word[0], + (unsigned long long) zc.zc_word[0], (unsigned long long) zc.zc_word[1], - (unsigned long long) zc.zc_word[2], + (unsigned long long) zc.zc_word[2], (unsigned long long) zc.zc_word[3]); return grub_error (GRUB_ERR_BAD_FS, N_("checksum verification failed")); } @@ -485,17 +485,17 @@ vdev_uberblock_compare (uberblock_t * ub1, uberblock_t * ub2) else ub2_endian = GRUB_ZFS_BIG_ENDIAN; - if (grub_zfs_to_cpu64 (ub1->ub_txg, ub1_endian) + if (grub_zfs_to_cpu64 (ub1->ub_txg, ub1_endian) < grub_zfs_to_cpu64 (ub2->ub_txg, ub2_endian)) return -1; - if (grub_zfs_to_cpu64 (ub1->ub_txg, ub1_endian) + if (grub_zfs_to_cpu64 (ub1->ub_txg, ub1_endian) > grub_zfs_to_cpu64 (ub2->ub_txg, ub2_endian)) return 1; - if (grub_zfs_to_cpu64 (ub1->ub_timestamp, ub1_endian) + if (grub_zfs_to_cpu64 (ub1->ub_timestamp, ub1_endian) < grub_zfs_to_cpu64 (ub2->ub_timestamp, ub2_endian)) return -1; - if (grub_zfs_to_cpu64 (ub1->ub_timestamp, ub1_endian) + if (grub_zfs_to_cpu64 (ub1->ub_timestamp, ub1_endian) > grub_zfs_to_cpu64 (ub2->ub_timestamp, ub2_endian)) return 1; @@ -573,7 +573,7 @@ find_bestub (uberblock_phys_t * ub_array, grub_errno = GRUB_ERR_NONE; continue; } - if (ubbest == NULL + if (ubbest == NULL || vdev_uberblock_compare (&(ubptr->ubp_uberblock), &(ubbest->ubp_uberblock)) > 0) ubbest = ubptr; @@ -594,10 +594,10 @@ get_psize (blkptr_t * bp, grub_zfs_endian_t endian) static grub_uint64_t dva_get_offset (const dva_t *dva, grub_zfs_endian_t endian) { - grub_dprintf ("zfs", "dva=%llx, %llx\n", - (unsigned long long) dva->dva_word[0], + grub_dprintf ("zfs", "dva=%llx, %llx\n", + (unsigned long long) dva->dva_word[0], (unsigned long long) dva->dva_word[1]); - return grub_zfs_to_cpu64 ((dva)->dva_word[1], + return grub_zfs_to_cpu64 ((dva)->dva_word[1], endian) << SPA_MINBLOCKSHIFT; } @@ -720,7 +720,7 @@ fill_vdev_info_real (struct grub_zfs_data *data, if (!fill->children) { fill->n_children = nelm; - + fill->children = grub_zalloc (fill->n_children * sizeof (fill->children[0])); } @@ -839,7 +839,7 @@ nvlist_next_nvpair (const char *nvl, const char *nvpair) { /* skip over header, nvl_version and nvl_nvflag */ nvpair = nvl + 4 * 3; - } + } else { /* skip to the next nvpair */ @@ -873,10 +873,10 @@ nvlist_next_nvpair (const char *nvl, const char *nvpair) name_len = grub_be_to_cpu32 (grub_get_unaligned32 (nvp)); nvp += 4; - nvp = nvp + ((name_len + 3) & ~3); // align - if (nvp + 4 >= nvl + VDEV_PHYS_SIZE + nvp = nvp + ((name_len + 3) & ~3); // align + if (nvp + 4 >= nvl + VDEV_PHYS_SIZE || encode_size < 0 - || nvp + 4 + encode_size > nvl + VDEV_PHYS_SIZE) + || nvp + 4 + encode_size > nvl + VDEV_PHYS_SIZE) { grub_dprintf ("zfs", "nvlist overflow\n"); grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist"); @@ -896,7 +896,7 @@ nvpair_name (const char *nvp, char **buf, grub_size_t *buflen) { /* skip over encode/decode size */ nvp += 4 * 2; - + *buf = (char *) (nvp + 4); *buflen = grub_be_to_cpu32 (grub_get_unaligned32 (nvp)); @@ -943,7 +943,7 @@ nvpair_value (const char *nvp,char **val, /* skip over name */ nvp = nvp + ((name_len + 3) & ~3); /* align */ - + /* skip over type */ nvp += 4; nelm = grub_be_to_cpu32 (grub_get_unaligned32 (nvp)); @@ -957,7 +957,7 @@ nvpair_value (const char *nvp,char **val, *size_out = encode_size; if (nelm_out) *nelm_out = nelm; - + return 1; } @@ -1183,7 +1183,7 @@ scan_disk (grub_device_t dev, struct grub_zfs_data *data, desc.vdev_phys_sector = label * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT) + ((VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE) >> SPA_MINBLOCKSHIFT) - + (label < VDEV_LABELS / 2 ? 0 : + + (label < VDEV_LABELS / 2 ? 0 : ALIGN_DOWN (grub_disk_native_sectors (dev->disk), sizeof (vdev_label_t)) - VDEV_LABELS * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT)); @@ -1229,7 +1229,7 @@ scan_disk (grub_device_t dev, struct grub_zfs_data *data, grub_free (bh); return GRUB_ERR_NONE; } - + grub_free (ub_array); grub_free (bh); @@ -1269,7 +1269,7 @@ scan_devices_iter (const char *name, void *hook_data) if (!inserted) grub_device_close (dev); - + return 0; } @@ -1386,7 +1386,7 @@ recovery (grub_uint8_t *bufs[4], grub_size_t s, const int nbufs, for (i = 0; i < nbufs; i++) { grub_uint8_t mul; - for (j = i; j < nbufs; j++) + for (j = i; j < nbufs; j++) if (matrix1[i][j]) break; if (j == nbufs) @@ -1453,7 +1453,7 @@ recovery (grub_uint8_t *bufs[4], grub_size_t s, const int nbufs, } default: return grub_error (GRUB_ERR_BUG, "too big matrix"); - } + } } static grub_err_t @@ -1510,7 +1510,7 @@ read_device (grub_uint64_t offset, struct grub_zfs_device_desc *desc, int idx, orig_idx; if (desc->nparity < 1 || desc->nparity > 3) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "raidz%d is not supported", desc->nparity); if (desc->n_children <= desc->nparity || desc->n_children < 1) @@ -1659,7 +1659,7 @@ read_device (grub_uint64_t offset, struct grub_zfs_device_desc *desc, len -= csize; idx--; } - for (i = 0; i < failed_devices + for (i = 0; i < failed_devices && recovery_len[i] == recovery_len[0]; i++); /* Since the chunks have variable length handle the last block @@ -1782,7 +1782,7 @@ zio_read_gang (blkptr_t * bp, grub_zfs_endian_t endian, dva_t * dva, void *buf, * Read in a block of raw data to buf. */ static grub_err_t -zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian, void *buf, +zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian, void *buf, struct grub_zfs_data *data) { int i, psize; @@ -1850,7 +1850,7 @@ decode_embedded_bp_compressed(const blkptr_t *bp, void *buf) * and put the uncompressed data in buf. */ static grub_err_t -zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf, +zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf, grub_size_t *size, struct grub_zfs_data *data) { grub_size_t lsize, psize; @@ -1936,7 +1936,7 @@ zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf, if (encrypted) { if (!grub_zfs_decrypt) - err = grub_error (GRUB_ERR_BAD_FS, + err = grub_error (GRUB_ERR_BAD_FS, N_("module `%s' isn't loaded"), "zfscrypt"); else @@ -1960,7 +1960,7 @@ zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf, endian)); return grub_error (GRUB_ERR_BAD_FS, "no key found in keychain"); } - grub_dprintf ("zfs", "using key %u (%" PRIxGRUB_UINT64_T + grub_dprintf ("zfs", "using key %u (%" PRIxGRUB_UINT64_T ", %p) for txg %" PRIxGRUB_UINT64_T "\n", besti, data->subvol.keyring[besti].txg, data->subvol.keyring[besti].cipher, @@ -2008,7 +2008,7 @@ zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf, * */ static grub_err_t -dmu_read (dnode_end_t * dn, grub_uint64_t blkid, void **buf, +dmu_read (dnode_end_t * dn, grub_uint64_t blkid, void **buf, grub_zfs_endian_t *endian_out, struct grub_zfs_data *data) { int level; @@ -2038,8 +2038,8 @@ dmu_read (dnode_end_t * dn, grub_uint64_t blkid, void **buf, if (BP_IS_HOLE (bp)) { - grub_size_t size = grub_zfs_to_cpu16 (dn->dn.dn_datablkszsec, - dn->endian) + grub_size_t size = grub_zfs_to_cpu16 (dn->dn.dn_datablkszsec, + dn->endian) << SPA_MINBLOCKSHIFT; *buf = grub_malloc (size); if (!*buf) @@ -2103,7 +2103,7 @@ mzap_lookup (mzap_phys_t * zapobj, grub_zfs_endian_t endian, } static int -mzap_iterate (mzap_phys_t * zapobj, grub_zfs_endian_t endian, int objsize, +mzap_iterate (mzap_phys_t * zapobj, grub_zfs_endian_t endian, int objsize, int (*hook) (const char *name, grub_uint64_t val, struct grub_zfs_dir_ctx *ctx), struct grub_zfs_dir_ctx *ctx) @@ -2117,7 +2117,7 @@ mzap_iterate (mzap_phys_t * zapobj, grub_zfs_endian_t endian, int objsize, grub_dprintf ("zfs", "zap: name = %s, value = %llx, cd = %x\n", mzap_ent[i].mze_name, (long long)mzap_ent[i].mze_value, (int)mzap_ent[i].mze_cd); - if (hook (mzap_ent[i].mze_name, + if (hook (mzap_ent[i].mze_name, grub_zfs_to_cpu64 (mzap_ent[i].mze_value, endian), ctx)) return 1; } @@ -2178,12 +2178,12 @@ name_cmp (const char *s1, const char *s2, grub_size_t n, if (!case_insensitive) return grub_memcmp (t1, t2, n); - + while (n--) { if (grub_toupper (*t1) != grub_toupper (*t2)) return (int) grub_toupper (*t1) - (int) grub_toupper (*t2); - + t1++; t2++; } @@ -2221,7 +2221,7 @@ zap_leaf_array_equal (zap_leaf_phys_t * l, grub_zfs_endian_t endian, /* XXX */ static grub_err_t -zap_leaf_array_get (zap_leaf_phys_t * l, grub_zfs_endian_t endian, int blksft, +zap_leaf_array_get (zap_leaf_phys_t * l, grub_zfs_endian_t endian, int blksft, int chunk, grub_size_t array_len, char *buf) { grub_size_t bseen = 0; @@ -2285,7 +2285,7 @@ zap_leaf_lookup (zap_leaf_phys_t * l, grub_zfs_endian_t endian, grub_dprintf ("zfs", "fzap: length %d\n", (int) le->le_name_length); - if (zap_leaf_array_equal (l, endian, blksft, + if (zap_leaf_array_equal (l, endian, blksft, grub_zfs_to_cpu16 (le->le_name_chunk,endian), grub_zfs_to_cpu16 (le->le_name_length, endian), name, case_insensitive)) @@ -2334,7 +2334,7 @@ fzap_lookup (dnode_end_t * zap_dnode, zap_phys_t * zap, { void *l; grub_uint64_t hash, idx, blkid; - int blksft = zfs_log2 (grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, + int blksft = zfs_log2 (grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, zap_dnode->endian) << DNODE_SHIFT); grub_err_t err; grub_zfs_endian_t leafendian; @@ -2347,7 +2347,7 @@ fzap_lookup (dnode_end_t * zap_dnode, zap_phys_t * zap, /* get block id from index */ if (zap->zap_ptrtbl.zt_numblks != 0) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "external pointer tables not supported"); idx = ZAP_HASH_IDX (hash, zap->zap_ptrtbl.zt_shift); blkid = grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))], zap_dnode->endian); @@ -2379,7 +2379,7 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap, void *l_in; grub_uint64_t idx, idx2, blkid; grub_uint16_t chunk; - int blksft = zfs_log2 (grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, + int blksft = zfs_log2 (grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, zap_dnode->endian) << DNODE_SHIFT); grub_err_t err; grub_zfs_endian_t endian; @@ -2390,7 +2390,7 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap, /* get block id from index */ if (zap->zap_ptrtbl.zt_numblks != 0) { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "external pointer tables not supported"); return 0; } @@ -2517,7 +2517,7 @@ zap_lookup (dnode_end_t * zap_dnode, const char *name, grub_uint64_t *val, grub_dprintf ("zfs", "micro zap\n"); err = mzap_lookup (zapbuf, endian, size, name, val, case_insensitive); - grub_dprintf ("zfs", "returned %d\n", err); + grub_dprintf ("zfs", "returned %d\n", err); grub_free (zapbuf); return err; } @@ -2527,7 +2527,7 @@ zap_lookup (dnode_end_t * zap_dnode, const char *name, grub_uint64_t *val, /* this is a fat zap */ err = fzap_lookup (zap_dnode, zapbuf, name, val, data, case_insensitive); - grub_dprintf ("zfs", "returned %d\n", err); + grub_dprintf ("zfs", "returned %d\n", err); grub_free (zapbuf); return err; } @@ -2560,7 +2560,7 @@ zap_iterate_u64_transform (const void *name, } static int -zap_iterate_u64 (dnode_end_t * zap_dnode, +zap_iterate_u64 (dnode_end_t * zap_dnode, int (*hook) (const char *name, grub_uint64_t val, struct grub_zfs_dir_ctx *ctx), struct grub_zfs_data *data, struct grub_zfs_dir_ctx *ctx) @@ -2607,7 +2607,7 @@ zap_iterate_u64 (dnode_end_t * zap_dnode, } static int -zap_iterate (dnode_end_t * zap_dnode, +zap_iterate (dnode_end_t * zap_dnode, grub_size_t nameelemlen, int (*hook) (const void *name, grub_size_t namelen, const void *val_in, @@ -2667,7 +2667,7 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type, grub_err_t err; grub_zfs_endian_t endian; - blksz = grub_zfs_to_cpu16 (mdn->dn.dn_datablkszsec, + blksz = grub_zfs_to_cpu16 (mdn->dn.dn_datablkszsec, mdn->endian) << SPA_MINBLOCKSHIFT; epbs = zfs_log2 (blksz) - DNODE_SHIFT; @@ -2678,18 +2678,18 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type, blkid = objnum >> epbs; idx = objnum & ((1 << epbs) - 1); - if (data->dnode_buf != NULL && grub_memcmp (data->dnode_mdn, mdn, - sizeof (*mdn)) == 0 + if (data->dnode_buf != NULL && grub_memcmp (data->dnode_mdn, mdn, + sizeof (*mdn)) == 0 && objnum >= data->dnode_start && objnum < data->dnode_end) { grub_memmove (&(buf->dn), &(data->dnode_buf)[idx], DNODE_SIZE); buf->endian = data->dnode_endian; - if (type && buf->dn.dn_type != type) - return grub_error(GRUB_ERR_BAD_FS, "incorrect dnode type"); + if (type && buf->dn.dn_type != type) + return grub_error(GRUB_ERR_BAD_FS, "incorrect dnode type"); return GRUB_ERR_NONE; } - grub_dprintf ("zfs", "endian = %d, blkid=%llx\n", mdn->endian, + grub_dprintf ("zfs", "endian = %d, blkid=%llx\n", mdn->endian, (unsigned long long) blkid); err = dmu_read (mdn, blkid, &dnbuf, &endian, data); if (err) @@ -2715,8 +2715,8 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type, grub_memmove (&(buf->dn), (dnode_phys_t *) dnbuf + idx, DNODE_SIZE); buf->endian = endian; - if (type && buf->dn.dn_type != type) - return grub_error(GRUB_ERR_BAD_FS, "incorrect dnode type"); + if (type && buf->dn.dn_type != type) + return grub_error(GRUB_ERR_BAD_FS, "incorrect dnode type"); return GRUB_ERR_NONE; } @@ -2740,7 +2740,7 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, struct dnode_chain { struct dnode_chain *next; - dnode_end_t dn; + dnode_end_t dn; }; struct dnode_chain *dnode_path = 0, *dn_new, *root; @@ -2750,7 +2750,7 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, dn_new->next = 0; dnode_path = root = dn_new; - err = dnode_get (&subvol->mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE, + err = dnode_get (&subvol->mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE, &(dnode_path->dn), data); if (err) { @@ -2801,7 +2801,7 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, grub_free (dn_new); return grub_errno; } - + while (1) { /* skip leading slashes */ @@ -2827,7 +2827,7 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, } else { - err = grub_error (GRUB_ERR_FILE_NOT_FOUND, + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, "can't resolve .."); break; } @@ -2877,7 +2877,7 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, { grub_size_t block; grub_size_t blksz; - blksz = (grub_zfs_to_cpu16 (dnode_path->dn.dn.dn_datablkszsec, + blksz = (grub_zfs_to_cpu16 (dnode_path->dn.dn.dn_datablkszsec, dnode_path->dn.endian) << SPA_MINBLOCKSHIFT); @@ -2916,7 +2916,7 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, if (err) break; free_symval = 1; - } + } path = path_buf = grub_malloc (sym_sz + grub_strlen (oldpath) + 1); if (!path_buf) { @@ -2930,9 +2930,9 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, if (free_symval) grub_free (sym_value); path [sym_sz] = 0; - grub_memcpy (path + grub_strlen (path), oldpath, + grub_memcpy (path + grub_strlen (path), oldpath, grub_strlen (oldpath) + 1); - + grub_free (oldpathbuf); if (path[0] != '/') { @@ -2951,7 +2951,7 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, { void *sahdrp; int hdrsize; - + if (dnode_path->dn.dn.dn_bonuslen != 0) { sahdrp = DN_BONUS (&dnode_path->dn.dn); @@ -2959,7 +2959,7 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, else if (dnode_path->dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR) { blkptr_t *bp = &dnode_path->dn.dn.dn_spill; - + err = zio_read (bp, dnode_path->dn.endian, &sahdrp, NULL, data); if (err) break; @@ -2978,7 +2978,7 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, dnode_path->dn.endian) >> 12) & 0xf) == 0xa) { char *sym_value = (char *) sahdrp + hdrsize + SA_SYMLINK_OFFSET; - grub_size_t sym_sz = + grub_size_t sym_sz = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET), @@ -2993,9 +2993,9 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, } grub_memcpy (path, sym_value, sym_sz); path [sym_sz] = 0; - grub_memcpy (path + grub_strlen (path), oldpath, + grub_memcpy (path + grub_strlen (path), oldpath, grub_strlen (oldpath) + 1); - + grub_free (oldpathbuf); if (path[0] != '/') { @@ -3096,7 +3096,7 @@ get_filesystem_dnode (dnode_end_t * mosmdn, char *fsname, grub_dprintf ("zfs", "endian = %d\n", mosmdn->endian); - err = dnode_get (mosmdn, DMU_POOL_DIRECTORY_OBJECT, + err = dnode_get (mosmdn, DMU_POOL_DIRECTORY_OBJECT, DMU_OT_OBJECT_DIRECTORY, mdn, data); if (err) return err; @@ -3119,7 +3119,7 @@ get_filesystem_dnode (dnode_end_t * mosmdn, char *fsname, { grub_uint64_t childobj; char *cname, ch; - + while (*fsname == '/') fsname++; @@ -3279,7 +3279,7 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, filename = ptr_slash; else filename = "/"; - grub_dprintf ("zfs", "fsname = '%s' snapname='%s' filename = '%s'\n", + grub_dprintf ("zfs", "fsname = '%s' snapname='%s' filename = '%s'\n", fsname, snapname, filename); } grub_dprintf ("zfs", "alive\n"); @@ -3365,7 +3365,7 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, snapobj = grub_zfs_to_cpu64 (((dsl_dataset_phys_t *) DN_BONUS (&subvol->mdn.dn))->ds_snapnames_zapobj, subvol->mdn.endian); - err = dnode_get (&(data->mos), snapobj, + err = dnode_get (&(data->mos), snapobj, DMU_OT_DSL_DS_SNAP_MAP, &subvol->mdn, data); if (!err) err = zap_lookup (&subvol->mdn, snapname, &headobj, data, 0); @@ -3383,13 +3383,13 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, subvol->obj = headobj; make_mdn (&subvol->mdn, data); - + grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian); if (*isfs) { grub_free (fsname); - grub_free (snapname); + grub_free (snapname); return GRUB_ERR_NONE; } err = dnode_get_path (subvol, filename, dn, data); @@ -3409,9 +3409,9 @@ nvlist_find_value (const char *nvlist_in, const char *name, char *nvp_name; /* Verify if the 1st and 2nd byte in the nvlist are valid. */ - /* NOTE: independently of what endianness header announces all + /* NOTE: independently of what endianness header announces all subsequent values are big-endian. */ - if (nvlist[0] != NV_ENCODE_XDR || (nvlist[1] != NV_LITTLE_ENDIAN + if (nvlist[0] != NV_ENCODE_XDR || (nvlist[1] != NV_LITTLE_ENDIAN && nvlist[1] != NV_BIG_ENDIAN)) { grub_dprintf ("zfs", "incorrect nvlist header\n"); @@ -3532,13 +3532,13 @@ get_nvlist_size (const char *beg, const char *limit) { const char *ptr; grub_uint32_t encode_size; - + ptr = beg + 8; while (ptr < limit && (encode_size = grub_be_to_cpu32 (grub_get_unaligned32 (ptr)))) ptr += encode_size; /* goto the next nvpair */ - ptr += 8; + ptr += 8; return (ptr > limit) ? -1 : (ptr - beg); } @@ -3674,8 +3674,8 @@ zfs_mount (grub_device_t dev) } ub = &(data->current_uberblock); - ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic, - GRUB_ZFS_LITTLE_ENDIAN) == UBERBLOCK_MAGIC + ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic, + GRUB_ZFS_LITTLE_ENDIAN) == UBERBLOCK_MAGIC ? GRUB_ZFS_LITTLE_ENDIAN : GRUB_ZFS_BIG_ENDIAN); err = zio_read (&ub->ub_rootbp, ub_endian, @@ -3728,7 +3728,7 @@ grub_zfs_fetch_nvlist (grub_device_t dev, char **nvlist) return err; } -static grub_err_t +static grub_err_t zfs_label (grub_device_t device, char **label) { char *nvlist; @@ -3740,7 +3740,7 @@ zfs_label (grub_device_t device, char **label) return grub_errno; err = zfs_fetch_nvlist (data->device_original, &nvlist); - if (err) + if (err) { zfs_unmount (data); return err; @@ -3752,7 +3752,7 @@ zfs_label (grub_device_t device, char **label) return grub_errno; } -static grub_err_t +static grub_err_t zfs_uuid (grub_device_t device, char **uuid) { struct grub_zfs_data *data; @@ -3770,7 +3770,7 @@ zfs_uuid (grub_device_t device, char **uuid) return GRUB_ERR_NONE; } -static grub_err_t +static grub_err_t zfs_mtime (grub_device_t device, grub_int64_t *mt) { struct grub_zfs_data *data; @@ -3784,8 +3784,8 @@ zfs_mtime (grub_device_t device, grub_int64_t *mt) return grub_errno; ub = &(data->current_uberblock); - ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic, - GRUB_ZFS_LITTLE_ENDIAN) == UBERBLOCK_MAGIC + ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic, + GRUB_ZFS_LITTLE_ENDIAN) == UBERBLOCK_MAGIC ? GRUB_ZFS_LITTLE_ENDIAN : GRUB_ZFS_BIG_ENDIAN); *mt = grub_zfs_to_cpu64 (ub->ub_timestamp, ub_endian); @@ -3823,7 +3823,7 @@ grub_zfs_open (struct grub_file *file, const char *fsfilename) } /* We found the dnode for this file. Verify if it is a plain file. */ - if (data->dnode.dn.dn_type != DMU_OT_PLAIN_FILE_CONTENTS) + if (data->dnode.dn.dn_type != DMU_OT_PLAIN_FILE_CONTENTS) { zfs_unmount (data); return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file")); @@ -3898,7 +3898,7 @@ grub_zfs_read (grub_file_t file, char *buf, grub_size_t len) return len; } - blksz = grub_zfs_to_cpu16 (data->dnode.dn.dn_datablkszsec, + blksz = grub_zfs_to_cpu16 (data->dnode.dn.dn_datablkszsec, data->dnode.endian) << SPA_MINBLOCKSHIFT; if (blksz == 0) @@ -3991,11 +3991,11 @@ fill_fs_info (struct grub_dirhook_info *info, dnode_end_t dn; grub_uint64_t objnum; grub_uint64_t headobj; - + grub_memset (info, 0, sizeof (*info)); - + info->dir = 1; - + if (mdn.dn.dn_type == DMU_OT_DSL_DIR) { headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&mdn.dn))->dd_head_dataset_obj, mdn.endian); @@ -4010,28 +4010,28 @@ fill_fs_info (struct grub_dirhook_info *info, err = make_mdn (&mdn, data); if (err) return err; - err = dnode_get (&mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE, + err = dnode_get (&mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE, &dn, data); if (err) { grub_dprintf ("zfs", "failed here\n"); return err; } - + err = zap_lookup (&dn, ZFS_ROOT_OBJ, &objnum, data, 0); if (err) { grub_dprintf ("zfs", "failed here\n"); return err; } - + err = dnode_get (&mdn, objnum, 0, &dn, data); if (err) { grub_dprintf ("zfs", "failed here\n"); return err; } - + if (dn.dn.dn_bonustype == DMU_OT_SA) { void *sahdrp; @@ -4117,15 +4117,15 @@ iterate_zap (const char *name, grub_uint64_t val, struct grub_zfs_dir_ctx *ctx) info.mtime = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian); info.case_insensitive = ctx->data->subvol.case_insensitive; } - + if (dn.dn.dn_bonustype == DMU_OT_ZNODE) - { + { info.mtimeset = 1; info.mtime = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dn.dn))->zp_mtime[0], dn.endian); } info.dir = (dn.dn.dn_type == DMU_OT_DIRECTORY_CONTENTS); - grub_dprintf ("zfs", "type=%d, name=%s\n", + grub_dprintf ("zfs", "type=%d, name=%s\n", (int)dn.dn.dn_type, (char *)name); return ctx->hook (name, &info, ctx->hook_data); } @@ -4219,7 +4219,7 @@ grub_zfs_dir (grub_device_t device, const char *path, if (isfs) { - grub_uint64_t childobj, headobj; + grub_uint64_t childobj, headobj; grub_uint64_t snapobj; dnode_end_t dn; struct grub_dirhook_info info; @@ -4247,7 +4247,7 @@ grub_zfs_dir (grub_device_t device, const char *path, } zap_iterate_u64 (&dn, iterate_zap_fs, data, &ctx); - + err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, &dn, data); if (err) { @@ -4289,8 +4289,8 @@ check_feature (const char *name, grub_uint64_t val, return 0; if (name[0] == 0) return 0; - for (i = 0; spa_feature_names[i] != NULL; i++) - if (grub_strcmp (name, spa_feature_names[i]) == 0) + for (i = 0; spa_feature_names[i] != NULL; i++) + if (grub_strcmp (name, spa_feature_names[i]) == 0) return 0; return 1; } @@ -4303,7 +4303,7 @@ check_feature (const char *name, grub_uint64_t val, * 0: Success. * errnum: Failure. */ - + static grub_err_t check_mos_features(dnode_phys_t *mosmdn_phys,grub_zfs_endian_t endian,struct grub_zfs_data* data ) { @@ -4328,7 +4328,7 @@ check_mos_features(dnode_phys_t *mosmdn_phys,grub_zfs_endian_t endian,struct gru errnum = zap_lookup(&dn, DMU_POOL_FEATURES_FOR_READ, &objnum, data,0); if (errnum != 0) return errnum; - + errnum = dnode_get(&mosmdn, objnum, DMU_OTN_ZAP_METADATA, &dn, data); if (errnum != 0) return errnum; diff --git a/grub-core/fs/zfs/zfs_fletcher.c b/grub-core/fs/zfs/zfs_fletcher.c index 7d27b053dc..ad3be67054 100644 --- a/grub-core/fs/zfs/zfs_fletcher.c +++ b/grub-core/fs/zfs/zfs_fletcher.c @@ -39,14 +39,14 @@ #include void -fletcher_2(const void *buf, grub_uint64_t size, grub_zfs_endian_t endian, +fletcher_2(const void *buf, grub_uint64_t size, grub_zfs_endian_t endian, zio_cksum_t *zcp) { const grub_uint64_t *ip = buf; const grub_uint64_t *ipend = ip + (size / sizeof (grub_uint64_t)); grub_uint64_t a0, b0, a1, b1; - - for (a0 = b0 = a1 = b1 = 0; ip < ipend; ip += 2) + + for (a0 = b0 = a1 = b1 = 0; ip < ipend; ip += 2) { a0 += grub_zfs_to_cpu64 (ip[0], endian); a1 += grub_zfs_to_cpu64 (ip[1], endian); @@ -61,14 +61,14 @@ fletcher_2(const void *buf, grub_uint64_t size, grub_zfs_endian_t endian, } void -fletcher_4 (const void *buf, grub_uint64_t size, grub_zfs_endian_t endian, +fletcher_4 (const void *buf, grub_uint64_t size, grub_zfs_endian_t endian, zio_cksum_t *zcp) { const grub_uint32_t *ip = buf; const grub_uint32_t *ipend = ip + (size / sizeof (grub_uint32_t)); grub_uint64_t a, b, c, d; - - for (a = b = c = d = 0; ip < ipend; ip++) + + for (a = b = c = d = 0; ip < ipend; ip++) { a += grub_zfs_to_cpu32 (ip[0], endian);; b += a; diff --git a/grub-core/fs/zfs/zfs_sha256.c b/grub-core/fs/zfs/zfs_sha256.c index a181f076c5..f042fa61ab 100644 --- a/grub-core/fs/zfs/zfs_sha256.c +++ b/grub-core/fs/zfs/zfs_sha256.c @@ -116,23 +116,23 @@ zio_checksum_SHA256(const void *buf, grub_uint64_t size, grub_uint8_t pad[128]; unsigned padsize = size & 63; unsigned i; - + for (i = 0; i < size - padsize; i += 64) SHA256Transform(H, (grub_uint8_t *)buf + i); - + for (i = 0; i < padsize; i++) pad[i] = ((grub_uint8_t *)buf)[i]; - + for (pad[padsize++] = 0x80; (padsize & 63) != 56; padsize++) pad[padsize] = 0; - + for (i = 0; i < 8; i++) pad[padsize++] = (size << 3) >> (56 - 8 * i); - + for (i = 0; i < padsize && i <= 64; i += 64) SHA256Transform(H, pad + i); - - zcp->zc_word[0] = grub_cpu_to_zfs64 ((grub_uint64_t)H[0] << 32 | H[1], + + zcp->zc_word[0] = grub_cpu_to_zfs64 ((grub_uint64_t)H[0] << 32 | H[1], endian); zcp->zc_word[1] = grub_cpu_to_zfs64 ((grub_uint64_t)H[2] << 32 | H[3], endian); diff --git a/grub-core/fs/zfs/zfscrypt.c b/grub-core/fs/zfs/zfscrypt.c index de3b015f58..da30e9ab33 100644 --- a/grub-core/fs/zfs/zfscrypt.c +++ b/grub-core/fs/zfs/zfscrypt.c @@ -46,7 +46,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); /* - Mostly based on following article: + Mostly based on following article: https://blogs.oracle.com/darren/entry/zfs_encryption_what_is_on */ @@ -179,7 +179,7 @@ grub_gcm_mul (grub_uint8_t *a, const grub_uint8_t *b) grub_crypto_xor (res, res, bs, 16); grub_gcm_mul_x (bs); } - + grub_memcpy (a, res, 16); } @@ -275,7 +275,7 @@ algo_decrypt (grub_crypto_cipher_handle_t cipher, grub_uint64_t algo, } static grub_err_t -grub_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher, +grub_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher, grub_uint64_t algo, void *nonce, char *buf, grub_size_t size, @@ -286,7 +286,7 @@ grub_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher, unsigned i; grub_uint32_t sw[4]; gcry_err_code_t err; - + grub_memcpy (sw, nonce, 16); if (endian != GRUB_ZFS_BIG_ENDIAN) for (i = 0; i < 4; i++) @@ -302,7 +302,7 @@ grub_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher, sw + 1, 3, 12); if (err) return grub_crypto_gcry_error (err); - + for (i = 0; i < 3; i++) if (grub_zfs_to_cpu32 (expected_mac[i], endian) != grub_be_to_cpu32 (mac[i])) @@ -362,7 +362,7 @@ grub_zfs_load_key_real (const struct grub_zfs_key *key, grub_crypto_cipher_close (cipher); continue; } - + err = grub_crypto_cipher_set_key (cipher, wrap_key_real, keylen); if (err) @@ -371,7 +371,7 @@ grub_zfs_load_key_real (const struct grub_zfs_key *key, grub_crypto_cipher_close (cipher); continue; } - + err = algo_decrypt (cipher, algo, decrypted, key->unknown_purpose_key, 32, mac, key->unknown_purpose_nonce, 2, 16); if (err || (grub_crypto_memcmp (mac, key->unknown_purpose_key + 32, 16) From 172fdee782b3ed7dfd4b47c5f810514f08225b24 Mon Sep 17 00:00:00 2001 From: "t.feng" Date: Tue, 29 Nov 2022 17:14:15 +0800 Subject: [PATCH 343/367] fs/xfs: Fix memory leaks in XFS module Signed-off-by: t.feng Reviewed-by: Daniel Kiper --- grub-core/fs/xfs.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index d6de7f1a2d..b67407690c 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -585,7 +585,10 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) if (grub_disk_read (node->data->disk, GRUB_XFS_FSB_TO_BLOCK (node->data, get_fsb (keys, i - 1 + recoffset)) << (node->data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS), 0, node->data->bsize, leaf)) - return 0; + { + grub_free (leaf); + return 0; + } if ((!node->data->hascrc && grub_strncmp ((char *) leaf->magic, "BMAP", 4)) || @@ -751,6 +754,7 @@ static int iterate_dir_call_hook (grub_uint64_t ino, const char *filename, if (err) { grub_print_error (); + grub_free (fdiro); return 0; } @@ -861,7 +865,10 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, blk << dirblk_log2, dirblk_size, dirblock, 0); if (numread != dirblk_size) - return 0; + { + grub_free (dirblock); + return 0; + } entries = (grub_be_to_cpu32 (tail->leaf_count) - grub_be_to_cpu32 (tail->leaf_stale)); From 64b189b1e068f4e33d333b077735d39d019600f5 Mon Sep 17 00:00:00 2001 From: Darren Kenny Date: Fri, 2 Jun 2023 18:08:44 +0000 Subject: [PATCH 344/367] fs/xfs: Fix issues found while fuzzing the XFS filesystem While performing fuzz testing with XFS filesystem images with ASAN enabled, several issues were found where the memory accesses are made beyond the data that is allocated into the struct grub_xfs_data structure's data field. The existing structure didn't store the size of the memory allocated into the buffer in the data field and had no way to check it. To resolve these issues, the data size is stored to enable checks into the data buffer. With these checks in place, the fuzzing corpus no longer cause any crashes. Signed-off-by: Darren Kenny Signed-off-by: Robbie Harwood Signed-off-by: Marta Lewandowska Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/fs/xfs.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index b67407690c..b91cd32b49 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -239,6 +239,7 @@ struct grub_fshelp_node struct grub_xfs_data { + grub_size_t data_size; struct grub_xfs_sblock sblock; grub_disk_t disk; int pos; @@ -611,8 +612,20 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) } else if (node->inode.format == XFS_INODE_FORMAT_EXT) { + grub_addr_t exts_end = 0; + grub_addr_t data_end = 0; + nrec = grub_be_to_cpu32 (node->inode.nextents); exts = (struct grub_xfs_extent *) grub_xfs_inode_data(&node->inode); + + if (grub_mul (sizeof (struct grub_xfs_extent), nrec, &exts_end) || + grub_add ((grub_addr_t) node->data, exts_end, &exts_end) || + grub_add ((grub_addr_t) node->data, node->data->data_size, &data_end) || + exts_end > data_end) + { + grub_error (GRUB_ERR_BAD_FS, "invalid number of XFS extents"); + return 0; + } } else { @@ -803,6 +816,9 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, grub_uint8_t *inopos = grub_xfs_inline_de_inopos(dir->data, de); grub_uint8_t c; + if ((inopos + (smallino ? 4 : 8)) > (grub_uint8_t *) dir + grub_xfs_fshelp_size (dir->data)) + return grub_error (GRUB_ERR_BAD_FS, "not a correct XFS inode"); + /* inopos might be unaligned. */ if (smallino) ino = (((grub_uint32_t) inopos[0]) << 24) @@ -829,6 +845,10 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, de->name[de->len] = c; de = grub_xfs_inline_next_de(dir->data, head, de); + + if ((grub_uint8_t *) de >= (grub_uint8_t *) dir + grub_xfs_fshelp_size (dir->data)) + return grub_error (GRUB_ERR_BAD_FS, "invalid XFS directory entry"); + } break; } @@ -897,6 +917,9 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, } filename = (char *)(direntry + 1); + if (filename + direntry->len - 1 > (char *) tail) + return grub_error (GRUB_ERR_BAD_FS, "invalid XFS directory entry"); + /* The byte after the filename is for the filetype, padding, or tag, which is not used by GRUB. So it can be overwritten. */ filename[direntry->len] = '\0'; @@ -941,6 +964,8 @@ grub_xfs_mount (grub_disk_t disk) if (!data) return 0; + data->data_size = sizeof (struct grub_xfs_data); + grub_dprintf("xfs", "Reading sb\n"); /* Read the superblock. */ if (grub_disk_read (disk, 0, 0, @@ -962,6 +987,7 @@ grub_xfs_mount (grub_disk_t disk) if (! data) goto fail; + data->data_size = sz; data->diropen.data = data; data->diropen.ino = grub_be_to_cpu64(data->sblock.rootino); data->diropen.inode_read = 1; From 05454614aae765182d9205f63d4cae49697148c2 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Thu, 28 Sep 2023 22:33:44 +0000 Subject: [PATCH 345/367] fs/xfs: Incorrect short form directory data boundary check After parsing of the current entry, the entry pointer is advanced to the next entry at the end of the "for" loop. In case where the last entry is at the end of the data boundary, the advanced entry pointer can point off the data boundary. The subsequent boundary check for the advanced entry pointer can cause a failure. The fix is to include the boundary check into the "for" loop condition. Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper Tested-by: Sebastian Andrzej Siewior Tested-by: Marta Lewandowska --- grub-core/fs/xfs.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index b91cd32b49..ebf962793f 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -810,7 +810,8 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, if (iterate_dir_call_hook (parent, "..", &ctx)) return 1; - for (i = 0; i < head->count; i++) + for (i = 0; i < head->count && + (grub_uint8_t *) de < ((grub_uint8_t *) dir + grub_xfs_fshelp_size (dir->data)); i++) { grub_uint64_t ino; grub_uint8_t *inopos = grub_xfs_inline_de_inopos(dir->data, de); @@ -845,10 +846,6 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, de->name[de->len] = c; de = grub_xfs_inline_next_de(dir->data, head, de); - - if ((grub_uint8_t *) de >= (grub_uint8_t *) dir + grub_xfs_fshelp_size (dir->data)) - return grub_error (GRUB_ERR_BAD_FS, "invalid XFS directory entry"); - } break; } From a5582461b36a92a1cc3bc5495dae359143450b5f Mon Sep 17 00:00:00 2001 From: Jon DeVree Date: Tue, 17 Oct 2023 23:03:47 -0400 Subject: [PATCH 346/367] fs/xfs: Fix XFS directory extent parsing The XFS directory entry parsing code has never been completely correct for extent based directories. The parser correctly handles the case where the directory is contained in a single extent, but then mistakenly assumes the data blocks for the multiple extent case are each identical to the single extent case. The difference in the format of the data blocks between the two cases is tiny enough that its gone unnoticed for a very long time. A recent change introduced some additional bounds checking into the XFS parser. Like GRUB's existing parser, it is correct for the single extent case but incorrect for the multiple extent case. When parsing a directory with multiple extents, this new bounds checking is sometimes (but not always) tripped and triggers an "invalid XFS directory entry" error. This probably would have continued to go unnoticed but the /boot/grub/ directory is large enough that it often has multiple extents. The difference between the two cases is that when there are multiple extents, the data blocks do not contain a trailer nor do they contain any leaf information. That information is stored in a separate set of extents dedicated to just the leaf information. These extents come after the directory entry extents and are not included in the inode size. So the existing parser already ignores the leaf extents. The only reason to read the trailer/leaf information at all is so that the parser can avoid misinterpreting that data as directory entries. So this updates the parser as follows: For the single extent case the parser doesn't change much: 1. Read the size of the leaf information from the trailer 2. Set the end pointer for the parser to the start of the leaf information. (The previous bounds checking set the end pointer to the start of the trailer, so this is actually a small improvement.) 3. Set the entries variable to the expected number of directory entries. For the multiple extent case: 1. Set the end pointer to the end of the block. 2. Do not set up the entries variable. Figuring out how many entries are in each individual block is complex and does not seem worth it when it appears to be safe to just iterate over the entire block. The bounds check itself was also dependent upon the faulty XFS parser because it accidentally used "filename + length - 1". Presumably this was able to pass the fuzzer because in the old parser there was always 8 bytes of slack space between the tail pointer and the actual end of the block. Since this is no longer the case the bounds check needs to be updated to "filename + length + 1" in order to prevent a regression in the handling of corrupt fliesystems. Notes: * When there is only one extent there will only ever be one block. If more than one block is required then XFS will always switch to holding leaf information in a separate extent. * B-tree based directories seems to be parsed properly by the same code that handles multiple extents. This is unlikely to ever occur within /boot though because its only used when there are an extremely large number of directory entries. Fixes: ef7850c75 (fs/xfs: Fix issues found while fuzzing the XFS filesystem) Fixes: b2499b29c (Adds support for the XFS filesystem.) Fixes: https://savannah.gnu.org/bugs/?64376 Signed-off-by: Jon DeVree Reviewed-by: Daniel Kiper Tested-by: Sebastian Andrzej Siewior Tested-by: Marta Lewandowska --- grub-core/fs/xfs.c | 52 +++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index ebf962793f..18edfcff48 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -223,6 +223,12 @@ struct grub_xfs_inode /* Size of struct grub_xfs_inode v2, up to unused4 member included. */ #define XFS_V2_INODE_SIZE (XFS_V3_INODE_SIZE - 76) +struct grub_xfs_dir_leaf_entry +{ + grub_uint32_t hashval; + grub_uint32_t address; +} GRUB_PACKED; + struct grub_xfs_dirblock_tail { grub_uint32_t leaf_count; @@ -874,9 +880,8 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, { struct grub_xfs_dir2_entry *direntry = grub_xfs_first_de(dir->data, dirblock); - int entries; - struct grub_xfs_dirblock_tail *tail = - grub_xfs_dir_tail(dir->data, dirblock); + int entries = -1; + char *end = dirblock + dirblk_size; numread = grub_xfs_read_file (dir, 0, 0, blk << dirblk_log2, @@ -887,14 +892,27 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, return 0; } - entries = (grub_be_to_cpu32 (tail->leaf_count) - - grub_be_to_cpu32 (tail->leaf_stale)); + /* + * Leaf and tail information are only in the data block if the number + * of extents is 1. + */ + if (dir->inode.nextents == grub_cpu_to_be32_compile_time (1)) + { + struct grub_xfs_dirblock_tail *tail = grub_xfs_dir_tail (dir->data, dirblock); + + end = (char *) tail; + + /* Subtract the space used by leaf nodes. */ + end -= grub_be_to_cpu32 (tail->leaf_count) * sizeof (struct grub_xfs_dir_leaf_entry); - if (!entries) - continue; + entries = grub_be_to_cpu32 (tail->leaf_count) - grub_be_to_cpu32 (tail->leaf_stale); + + if (!entries) + continue; + } /* Iterate over all entries within this block. */ - while ((char *)direntry < (char *)tail) + while ((char *) direntry < (char *) end) { grub_uint8_t *freetag; char *filename; @@ -914,7 +932,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, } filename = (char *)(direntry + 1); - if (filename + direntry->len - 1 > (char *) tail) + if (filename + direntry->len + 1 > (char *) end) return grub_error (GRUB_ERR_BAD_FS, "invalid XFS directory entry"); /* The byte after the filename is for the filetype, padding, or @@ -928,11 +946,17 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, return 1; } - /* Check if last direntry in this block is - reached. */ - entries--; - if (!entries) - break; + /* + * The expected number of directory entries is only tracked for the + * single extent case. + */ + if (dir->inode.nextents == grub_cpu_to_be32_compile_time (1)) + { + /* Check if last direntry in this block is reached. */ + entries--; + if (!entries) + break; + } /* Select the next directory entry. */ direntry = grub_xfs_next_de(dir->data, direntry); From 4fb2d9619ca67e4fc2770012f35c216bb6e806a5 Mon Sep 17 00:00:00 2001 From: Anthony Iliopoulos Date: Thu, 26 Oct 2023 11:53:39 +0200 Subject: [PATCH 347/367] fs/xfs: Add large extent counters incompat feature support XFS introduced 64-bit extent counters for inodes via a series of upstream commits and the feature was marked as stable in v6.5 via commit 61d7e8274cd8 (xfs: drop EXPERIMENTAL tag for large extent counts). Further, xfsprogs release v6.5.0 switched this feature on by default in mkfs.xfs via commit e5b18d7d1d96 (mkfs: enable large extent counts by default). Filesystems formatted with large extent count support, nrext64=1, are thus currently not recognizable by GRUB, since this is an incompat feature. Add the required support so that those filesystems and inodes with large extent counters can be read by GRUB. Signed-off-by: Anthony Iliopoulos Reviewed-by: Andrey Albershteyn Reviewed-by: Daniel Kiper Tested-by: Marta Lewandowska Tested-by: Sebastian Andrzej Siewior --- grub-core/fs/xfs.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index 18edfcff48..bc2224dbb4 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -79,6 +79,8 @@ GRUB_MOD_LICENSE ("GPLv3+"); /* Inode flags2 flags */ #define XFS_DIFLAG2_BIGTIME_BIT 3 #define XFS_DIFLAG2_BIGTIME (1 << XFS_DIFLAG2_BIGTIME_BIT) +#define XFS_DIFLAG2_NREXT64_BIT 4 +#define XFS_DIFLAG2_NREXT64 (1 << XFS_DIFLAG2_NREXT64_BIT) /* incompat feature flags */ #define XFS_SB_FEAT_INCOMPAT_FTYPE (1 << 0) /* filetype in dirent */ @@ -86,6 +88,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define XFS_SB_FEAT_INCOMPAT_META_UUID (1 << 2) /* metadata UUID */ #define XFS_SB_FEAT_INCOMPAT_BIGTIME (1 << 3) /* large timestamps */ #define XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR (1 << 4) /* needs xfs_repair */ +#define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 5) /* large extent counters */ /* * Directory entries with ftype are explicitly handled by GRUB code. @@ -101,7 +104,8 @@ GRUB_MOD_LICENSE ("GPLv3+"); XFS_SB_FEAT_INCOMPAT_SPINODES | \ XFS_SB_FEAT_INCOMPAT_META_UUID | \ XFS_SB_FEAT_INCOMPAT_BIGTIME | \ - XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR) + XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR | \ + XFS_SB_FEAT_INCOMPAT_NREXT64) struct grub_xfs_sblock { @@ -203,7 +207,8 @@ struct grub_xfs_inode grub_uint16_t mode; grub_uint8_t version; grub_uint8_t format; - grub_uint8_t unused2[26]; + grub_uint8_t unused2[18]; + grub_uint64_t nextents_big; grub_uint64_t atime; grub_uint64_t mtime; grub_uint64_t ctime; @@ -545,11 +550,26 @@ get_fsb (const void *keys, int idx) return grub_be_to_cpu64 (grub_get_unaligned64 (p)); } +static int +grub_xfs_inode_has_large_extent_counts (const struct grub_xfs_inode *inode) +{ + return inode->version >= 3 && + (inode->flags2 & grub_cpu_to_be64_compile_time (XFS_DIFLAG2_NREXT64)); +} + +static grub_uint64_t +grub_xfs_get_inode_nextents (struct grub_xfs_inode *inode) +{ + return (grub_xfs_inode_has_large_extent_counts (inode)) ? + grub_be_to_cpu64 (inode->nextents_big) : + grub_be_to_cpu32 (inode->nextents); +} + static grub_disk_addr_t grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { struct grub_xfs_btree_node *leaf = 0; - int ex, nrec; + grub_uint64_t ex, nrec; struct grub_xfs_extent *exts; grub_uint64_t ret = 0; @@ -574,7 +594,7 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) / (2 * sizeof (grub_uint64_t)); do { - int i; + grub_uint64_t i; for (i = 0; i < nrec; i++) { @@ -621,7 +641,7 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) grub_addr_t exts_end = 0; grub_addr_t data_end = 0; - nrec = grub_be_to_cpu32 (node->inode.nextents); + nrec = grub_xfs_get_inode_nextents (&node->inode); exts = (struct grub_xfs_extent *) grub_xfs_inode_data(&node->inode); if (grub_mul (sizeof (struct grub_xfs_extent), nrec, &exts_end) || From c7572f1174490e78e4d8d5c6bb831b527267fe44 Mon Sep 17 00:00:00 2001 From: raravind Date: Tue, 9 May 2023 11:29:35 +0200 Subject: [PATCH 348/367] chainloader: remove device path debug message Remove the debug message "/EndEntire" while using GRUB chainloader command. Signed-off-by: raravind (cherry picked from commit f75f5386b7a6a7cb2e10d30f817a3564c0a28dd7) --- grub-core/loader/efi/chainloader.c | 1 - 1 file changed, 1 deletion(-) diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index dd31ac9bb3..b1c86dab2b 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -210,7 +210,6 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename) /* Fill the file path for the directory. */ d = (grub_efi_device_path_t *) ((char *) file_path + ((char *) d - (char *) dp)); - grub_efi_print_device_path (d); if (copy_file_path ((grub_efi_file_path_device_path_t *) d, dir_start, dir_end - dir_start) != GRUB_ERR_NONE) { From fb31d80088f153227903894cd79eae3efc7a7876 Mon Sep 17 00:00:00 2001 From: Solar Designer Date: Tue, 6 Feb 2024 21:39:41 +0100 Subject: [PATCH 349/367] grub-set-bootflag: Conservative partial fix for CVE-2024-1048 Following up on CVE-2019-14865 and taking a fresh look at grub2-set-bootflag now (through my work at CIQ on Rocky Linux), I saw some other ways in which users could still abuse this little program: 1. After CVE-2019-14865 fix, grub2-set-bootflag no longer rewrites the grubenv file in-place, but writes into a temporary file and renames it over the original, checking for error returns from each call first. This prevents the original file truncation vulnerability, but it can leave the temporary file around if the program is killed before it can rename or remove the file. There are still many ways to get the program killed, such as through RLIMIT_FSIZE triggering SIGXFSZ (tested, reliable) or by careful timing (tricky) of signals sent by process group leader, pty, pre-scheduled timers, SIGXCPU (probably not an exhaustive list). Invoking the program multiple times fills up /boot (or if /boot is not separate, then it can fill up the root filesystem). Since the files are tiny, the filesystem is likely to run out of free inodes before it'd run out of blocks, but the effect is similar - can't create new files after this point (but still can add data to existing files, such as logs). 2. After CVE-2019-14865 fix, grub2-set-bootflag naively tries to protect itself from signals by becoming full root. (This does protect it from signals sent by the user directly to the PID, but e.g. "kill -9 -1" by the user still works.) A side effect of such "protection" is that it's possible to invoke more concurrent instances of grub2-set-bootflag than the user's RLIMIT_NPROC would normally permit (as specified e.g. in /etc/security/limits.conf, or say in Apache httpd's RLimitNPROC if grub2-set-bootflag would be abused by a website script), thereby exhausting system resources (e.g., bypassing RAM usage limit if RLIMIT_AS was also set). 3. umask is inherited. Again, due to how the CVE-2019-14865 fix creates a new file, and due to how mkstemp() works, this affects grubenv's new file permissions. Luckily, mkstemp() forces them to be no more relaxed than 0600, but the user ends up being able to set them e.g. to 0. Luckily, at least in my testing GRUB still works fine even when the file has such (lack of) permissions. This commit deals with the abuses above as follows: 1. RLIMIT_FSIZE is pre-checked, so this specific way to get the process killed should no longer work. However, this isn't a complete fix because there are other ways to get the process killed after it has created the temporary file. The commit also fixes bug 1975892 ("RFE: grub2-set-bootflag should not write the grubenv when the flag being written is already set") and similar for "menu_show_once", which further reduces the abuse potential. 2. RLIMIT_NPROC bypass should be avoided by not becoming full root (aka dropping the partial "kill protection"). 3. A safe umask is set. This is a partial fix (temporary files can still accumulate, but this is harder to trigger). While at it, this commit also fixes potential 1- or 2-byte over-read of env[] if its content is malformed - this was not a security issue since the grubenv file is trusted input, and the fix is just for robustness. Signed-off-by: Solar Designer --- util/grub-set-bootflag.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/util/grub-set-bootflag.c b/util/grub-set-bootflag.c index 3b4c25ca2a..5bbbef8043 100644 --- a/util/grub-set-bootflag.c +++ b/util/grub-set-bootflag.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include "progname.h" @@ -57,12 +59,17 @@ static void usage(FILE *out) int main(int argc, char *argv[]) { /* NOTE buf must be at least the longest bootflag length + 4 bytes */ - char env[GRUBENV_SIZE + 1], buf[64], *s; + char env[GRUBENV_SIZE + 1 + 2], buf[64], *s; /* +1 for 0 termination, +6 for "XXXXXX" in tmp filename */ char env_filename[PATH_MAX + 1], tmp_filename[PATH_MAX + 6 + 1]; const char *bootflag; int i, fd, len, ret; FILE *f; + struct rlimit rlim; + + if (getrlimit(RLIMIT_FSIZE, &rlim) || rlim.rlim_cur < GRUBENV_SIZE || rlim.rlim_max < GRUBENV_SIZE) + return 1; + umask(077); if (argc != 2) { @@ -94,20 +101,11 @@ int main(int argc, char *argv[]) len = strlen (bootflag); /* - * Really become root. setuid avoids an user killing us, possibly leaking - * the tmpfile. setgid avoids the new grubenv's gid being that of the user. + * setegid avoids the new grubenv's gid being that of the user. */ - ret = setuid(0); - if (ret) - { - perror ("Error setuid(0) failed"); - return 1; - } - - ret = setgid(0); - if (ret) + if (setegid(0)) { - perror ("Error setgid(0) failed"); + perror ("Error setegid(0) failed"); return 1; } @@ -136,6 +134,9 @@ int main(int argc, char *argv[]) /* 0 terminate env */ env[GRUBENV_SIZE] = 0; + /* not a valid flag value */ + env[GRUBENV_SIZE + 1] = 0; + env[GRUBENV_SIZE + 2] = 0; if (strncmp (env, GRUB_ENVBLK_SIGNATURE, strlen (GRUB_ENVBLK_SIGNATURE))) { @@ -171,6 +172,8 @@ int main(int argc, char *argv[]) /* The grubenv is not 0 terminated, so memcpy the name + '=' , '1', '\n' */ snprintf(buf, sizeof(buf), "%s=1\n", bootflag); + if (!memcmp(s, buf, len + 3)) + return 0; /* nothing to do */ memcpy(s, buf, len + 3); From 074973a4d05cab5b9b0e0883d7d04c49cc2885a2 Mon Sep 17 00:00:00 2001 From: Solar Designer Date: Tue, 6 Feb 2024 21:56:21 +0100 Subject: [PATCH 350/367] grub-set-bootflag: More complete fix for CVE-2024-1048 Switch to per-user fixed temporary filenames along with a weird locking mechanism, which is explained in source code comments. This is a more complete fix than the previous commit (temporary files can't accumulate). Unfortunately, it introduces new risks (by working on a temporary file shared between the user's invocations), which are _hopefully_ avoided by the patch's elaborate logic. I actually got it wrong at first, which suggests that this logic is hard to reason about, and more errors or omissions are possible. It also relies on the kernel's primitives' exact semantics to a greater extent (nothing out of the ordinary, though). Remaining issues that I think cannot reasonably be fixed without a redesign (e.g., having per-flag files with nothing else in them) and without introducing new issues: A. A user can still revert a concurrent user's attempt of setting the other flag - or of making other changes to grubenv by means other than this program. B. One leftover temporary file per user is still possible. Signed-off-by: Solar Designer --- util/grub-set-bootflag.c | 87 ++++++++++++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 12 deletions(-) diff --git a/util/grub-set-bootflag.c b/util/grub-set-bootflag.c index 5bbbef8043..514c4f9091 100644 --- a/util/grub-set-bootflag.c +++ b/util/grub-set-bootflag.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -60,15 +61,12 @@ int main(int argc, char *argv[]) { /* NOTE buf must be at least the longest bootflag length + 4 bytes */ char env[GRUBENV_SIZE + 1 + 2], buf[64], *s; - /* +1 for 0 termination, +6 for "XXXXXX" in tmp filename */ - char env_filename[PATH_MAX + 1], tmp_filename[PATH_MAX + 6 + 1]; + /* +1 for 0 termination, +11 for ".%u" in tmp filename */ + char env_filename[PATH_MAX + 1], tmp_filename[PATH_MAX + 11 + 1]; const char *bootflag; int i, fd, len, ret; FILE *f; - struct rlimit rlim; - if (getrlimit(RLIMIT_FSIZE, &rlim) || rlim.rlim_cur < GRUBENV_SIZE || rlim.rlim_max < GRUBENV_SIZE) - return 1; umask(077); if (argc != 2) @@ -105,7 +103,7 @@ int main(int argc, char *argv[]) */ if (setegid(0)) { - perror ("Error setegid(0) failed"); + perror ("setegid(0) failed"); return 1; } @@ -176,19 +174,82 @@ int main(int argc, char *argv[]) return 0; /* nothing to do */ memcpy(s, buf, len + 3); + struct rlimit rlim; + if (getrlimit(RLIMIT_FSIZE, &rlim) || rlim.rlim_cur < GRUBENV_SIZE || rlim.rlim_max < GRUBENV_SIZE) + { + fprintf (stderr, "Resource limits undetermined or too low\n"); + return 1; + } + + /* + * Here we work under the premise that we shouldn't write into the target + * file directly because we might not be able to have all of our changes + * written completely and atomically. That was CVE-2019-14865, known to + * have been triggerable via RLIMIT_FSIZE. While we've dealt with that + * specific attack via the check above, there may be other possibilities. + */ /* * Create a tempfile for writing the new env. Use the canonicalized filename * for the template so that the tmpfile is in the same dir / on same fs. + * + * We now use per-user fixed temporary filenames, so that a user cannot cause + * multiple files to accumulate. + * + * We don't use O_EXCL so that a stale temporary file doesn't prevent further + * usage of the program by the user. */ - snprintf(tmp_filename, sizeof(tmp_filename), "%sXXXXXX", env_filename); - fd = mkstemp(tmp_filename); + snprintf(tmp_filename, sizeof(tmp_filename), "%s.%u", env_filename, getuid()); + fd = open(tmp_filename, O_CREAT | O_WRONLY, 0600); if (fd == -1) { perror ("Creating tmpfile failed"); return 1; } + /* + * The lock prevents the same user from reaching further steps ending in + * rename() concurrently, in which case the temporary file only partially + * written by one invocation could be renamed to the target file by another. + * + * The lock also guards the slow fsync() from concurrent calls. After the + * first time that and the rename() complete, further invocations for the + * same flag become no-ops. + * + * We lock the temporary file rather than the target file because locking the + * latter would allow any user having SIGSTOP'ed their process to make all + * other users' invocations fail (or lock up if we'd use blocking mode). + * + * We use non-blocking mode (LOCK_NB) because the lock having been taken by + * another process implies that the other process would normally have already + * renamed the file to target by the time it releases the lock (and we could + * acquire it), so we'd be working directly on the target if we proceeded, + * which is undesirable, and we'd kind of fail on the already-done rename. + */ + if (flock(fd, LOCK_EX | LOCK_NB)) + { + perror ("Locking tmpfile failed"); + return 1; + } + + /* + * Deal with the potential that another invocation proceeded all the way to + * rename() and process exit while we were between open() and flock(). + */ + { + struct stat st1, st2; + if (fstat(fd, &st1) || stat(tmp_filename, &st2)) + { + perror ("stat of tmpfile failed"); + return 1; + } + if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) + { + fprintf (stderr, "Another invocation won race\n"); + return 1; + } + } + f = fdopen (fd, "w"); if (!f) { @@ -213,23 +274,25 @@ int main(int argc, char *argv[]) return 1; } - ret = fsync (fileno (f)); + ret = ftruncate (fileno (f), GRUBENV_SIZE); if (ret) { - perror ("Error syncing tmpfile"); + perror ("Error truncating tmpfile"); unlink(tmp_filename); return 1; } - ret = fclose (f); + ret = fsync (fileno (f)); if (ret) { - perror ("Error closing tmpfile"); + perror ("Error syncing tmpfile"); unlink(tmp_filename); return 1; } /* + * We must not close the file before rename() as that would remove the lock. + * * And finally rename the tmpfile with the new env over the old env, the * linux kernel guarantees that this is atomic (from a syscall pov). */ From 54259192453a49ced0e731c721ae97edc77f5d0d Mon Sep 17 00:00:00 2001 From: Solar Designer Date: Tue, 6 Feb 2024 22:05:45 +0100 Subject: [PATCH 351/367] grub-set-bootflag: Exit calmly when not running as root Exit calmly when not installed SUID root and invoked by non-root. This allows installing user/grub-boot-success.service unconditionally while supporting non-SUID installation of the program for some limited usage. Signed-off-by: Solar Designer --- util/grub-set-bootflag.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/util/grub-set-bootflag.c b/util/grub-set-bootflag.c index 514c4f9091..31a868aeca 100644 --- a/util/grub-set-bootflag.c +++ b/util/grub-set-bootflag.c @@ -98,6 +98,17 @@ int main(int argc, char *argv[]) bootflag = bootflags[i]; len = strlen (bootflag); + /* + * Exit calmly when not installed SUID root and invoked by non-root. This + * allows installing user/grub-boot-success.service unconditionally while + * supporting non-SUID installation of the program for some limited usage. + */ + if (geteuid()) + { + printf ("grub-set-bootflag not running as root, no action taken\n"); + return 0; + } + /* * setegid avoids the new grubenv's gid being that of the user. */ From d1d0c0d5e8bed8398687fd6eebc3a4895576ab89 Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Tue, 3 Oct 2023 19:12:23 +0200 Subject: [PATCH 352/367] fs/ntfs: Fix an OOB write when parsing the $ATTRIBUTE_LIST attribute for the $MFT file When parsing an extremely fragmented $MFT file, i.e., the file described using the $ATTRIBUTE_LIST attribute, current NTFS code will reuse a buffer containing bytes read from the underlying drive to store sector numbers, which are consumed later to read data from these sectors into another buffer. These sectors numbers, two 32-bit integers, are always stored at predefined offsets, 0x10 and 0x14, relative to first byte of the selected entry within the $ATTRIBUTE_LIST attribute. Usually, this won't cause any problem. However, when parsing a specially-crafted file system image, this may cause the NTFS code to write these integers beyond the buffer boundary, likely causing the GRUB memory allocator to misbehave or fail. These integers contain values which are controlled by on-disk structures of the NTFS file system. Such modification and resulting misbehavior may touch a memory range not assigned to the GRUB and owned by firmware or another EFI application/driver. This fix introduces checks to ensure that these sector numbers are never written beyond the boundary. Fixes: CVE-2023-4692 Reported-by: Maxim Suhanov Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index 3511e4e2cb..4681c7ac32 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -184,7 +184,7 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) } if (at->attr_end) { - grub_uint8_t *pa; + grub_uint8_t *pa, *pa_end; at->emft_buf = grub_malloc (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); if (at->emft_buf == NULL) @@ -209,11 +209,13 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) } at->attr_nxt = at->edat_buf; at->attr_end = at->edat_buf + u32at (pa, 0x30); + pa_end = at->edat_buf + n; } else { at->attr_nxt = at->attr_end + u16at (pa, 0x14); at->attr_end = at->attr_end + u32at (pa, 4); + pa_end = at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); } at->flags |= GRUB_NTFS_AF_ALST; while (at->attr_nxt < at->attr_end) @@ -230,6 +232,13 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) at->flags |= GRUB_NTFS_AF_GPOS; at->attr_cur = at->attr_nxt; pa = at->attr_cur; + + if ((pa >= pa_end) || (pa_end - pa < 0x18)) + { + grub_error (GRUB_ERR_BAD_FS, "can\'t parse attribute list"); + return NULL; + } + grub_set_unaligned32 ((char *) pa + 0x10, grub_cpu_to_le32 (at->mft->data->mft_start)); grub_set_unaligned32 ((char *) pa + 0x14, @@ -240,6 +249,13 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) { if (*pa != attr) break; + + if ((pa >= pa_end) || (pa_end - pa < 0x18)) + { + grub_error (GRUB_ERR_BAD_FS, "can\'t parse attribute list"); + return NULL; + } + if (read_attr (at, pa + 0x10, u32at (pa, 0x10) * (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR), From ca53ebfddf9cfbabe9c505e79bedeca89eecae8d Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Tue, 3 Oct 2023 19:12:24 +0200 Subject: [PATCH 353/367] fs/ntfs: Fix an OOB read when reading data from the resident $DATA attribute When reading a file containing resident data, i.e., the file data is stored in the $DATA attribute within the NTFS file record, not in external clusters, there are no checks that this resident data actually fits the corresponding file record segment. When parsing a specially-crafted file system image, the current NTFS code will read the file data from an arbitrary, attacker-chosen memory offset and of arbitrary, attacker-chosen length. This allows an attacker to display arbitrary chunks of memory, which could contain sensitive information like password hashes or even plain-text, obfuscated passwords from BS EFI variables. This fix implements a check to ensure that resident data is read from the corresponding file record segment only. Fixes: CVE-2023-4693 Reported-by: Maxim Suhanov Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index 4681c7ac32..1949d48a49 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -401,7 +401,18 @@ read_data (struct grub_ntfs_attr *at, grub_uint8_t *pa, grub_uint8_t *dest, { if (ofs + len > u32at (pa, 0x10)) return grub_error (GRUB_ERR_BAD_FS, "read out of range"); - grub_memcpy (dest, pa + u32at (pa, 0x14) + ofs, len); + + if (u32at (pa, 0x10) > (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + return grub_error (GRUB_ERR_BAD_FS, "resident attribute too large"); + + if (pa >= at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + return grub_error (GRUB_ERR_BAD_FS, "resident attribute out of range"); + + if (u16at (pa, 0x14) + u32at (pa, 0x10) > + (grub_addr_t) at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR) - (grub_addr_t) pa) + return grub_error (GRUB_ERR_BAD_FS, "resident attribute out of range"); + + grub_memcpy (dest, pa + u16at (pa, 0x14) + ofs, len); return 0; } From cd3ea53250643fbfa12800a2a80ce8cbb91706cc Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Tue, 3 Oct 2023 19:12:25 +0200 Subject: [PATCH 354/367] fs/ntfs: Fix an OOB read when parsing directory entries from resident and non-resident index attributes This fix introduces checks to ensure that index entries are never read beyond the corresponding directory index. The lack of this check is a minor issue, likely not exploitable in any way. Reported-by: Maxim Suhanov Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index 1949d48a49..7230203328 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -599,7 +599,7 @@ get_utf8 (grub_uint8_t *in, grub_size_t len) } static int -list_file (struct grub_ntfs_file *diro, grub_uint8_t *pos, +list_file (struct grub_ntfs_file *diro, grub_uint8_t *pos, grub_uint8_t *end_pos, grub_fshelp_iterate_dir_hook_t hook, void *hook_data) { grub_uint8_t *np; @@ -610,6 +610,9 @@ list_file (struct grub_ntfs_file *diro, grub_uint8_t *pos, grub_uint8_t namespace; char *ustr; + if ((pos >= end_pos) || (end_pos - pos < 0x52)) + break; + if (pos[0xC] & 2) /* end signature */ break; @@ -617,6 +620,9 @@ list_file (struct grub_ntfs_file *diro, grub_uint8_t *pos, ns = *(np++); namespace = *(np++); + if (2 * ns > end_pos - pos - 0x52) + break; + /* * Ignore files in DOS namespace, as they will reappear as Win32 * names. @@ -802,7 +808,9 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, } cur_pos += 0x10; /* Skip index root */ - ret = list_file (mft, cur_pos + u16at (cur_pos, 0), hook, hook_data); + ret = list_file (mft, cur_pos + u16at (cur_pos, 0), + at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR), + hook, hook_data); if (ret) goto done; @@ -889,6 +897,7 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, (const grub_uint8_t *) "INDX"))) goto done; ret = list_file (mft, &indx[0x18 + u16at (indx, 0x18)], + indx + (mft->data->idx_size << GRUB_NTFS_BLK_SHR), hook, hook_data); if (ret) goto done; From c7145e6b05babdda1816eab085004a356156043e Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Tue, 3 Oct 2023 19:12:26 +0200 Subject: [PATCH 355/367] fs/ntfs: Fix an OOB read when parsing bitmaps for index attributes This fix introduces checks to ensure that bitmaps for directory indices are never read beyond their actual sizes. The lack of this check is a minor issue, likely not exploitable in any way. Reported-by: Maxim Suhanov Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index 7230203328..7451511428 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -839,6 +839,25 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, if (is_resident) { + if (bitmap_len > (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + { + grub_error (GRUB_ERR_BAD_FS, "resident bitmap too large"); + goto done; + } + + if (cur_pos >= at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + { + grub_error (GRUB_ERR_BAD_FS, "resident bitmap out of range"); + goto done; + } + + if (u16at (cur_pos, 0x14) + u32at (cur_pos, 0x10) > + (grub_addr_t) at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR) - (grub_addr_t) cur_pos) + { + grub_error (GRUB_ERR_BAD_FS, "resident bitmap out of range"); + goto done; + } + grub_memcpy (bmp, cur_pos + u16at (cur_pos, 0x14), bitmap_len); } From 41e38af7b464236727012d9912a2411c3ce541ca Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Tue, 3 Oct 2023 19:12:27 +0200 Subject: [PATCH 356/367] fs/ntfs: Fix an OOB read when parsing a volume label This fix introduces checks to ensure that an NTFS volume label is always read from the corresponding file record segment. The current NTFS code allows the volume label string to be read from an arbitrary, attacker-chosen memory location. However, the bytes read are always treated as UTF-16LE. So, the final string displayed is mostly unreadable and it can't be easily converted back to raw bytes. The lack of this check is a minor issue, likely not causing a significant data leak. Reported-by: Maxim Suhanov Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index 7451511428..32ba8276dd 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -1209,13 +1209,29 @@ grub_ntfs_label (grub_device_t device, char **label) init_attr (&mft->attr, mft); pa = find_attr (&mft->attr, GRUB_NTFS_AT_VOLUME_NAME); + + if (pa >= mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + { + grub_error (GRUB_ERR_BAD_FS, "can\'t parse volume label"); + goto fail; + } + + if (mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR) - pa < 0x16) + { + grub_error (GRUB_ERR_BAD_FS, "can\'t parse volume label"); + goto fail; + } + if ((pa) && (pa[8] == 0) && (u32at (pa, 0x10))) { int len; len = u32at (pa, 0x10) / 2; pa += u16at (pa, 0x14); - *label = get_utf8 (pa, len); + if (mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR) - pa >= 2 * len) + *label = get_utf8 (pa, len); + else + grub_error (GRUB_ERR_BAD_FS, "can\'t parse volume label"); } fail: From 511c49d7fc1ddc777768898e337c68675198d8d2 Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Tue, 3 Oct 2023 19:12:28 +0200 Subject: [PATCH 357/367] fs/ntfs: Make code more readable Move some calls used to access NTFS attribute header fields into functions with human-readable names. Suggested-by: Daniel Kiper Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 48 +++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index 32ba8276dd..991b1c2094 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -52,6 +52,24 @@ u64at (void *ptr, grub_size_t ofs) return grub_le_to_cpu64 (grub_get_unaligned64 ((char *) ptr + ofs)); } +static grub_uint16_t +first_attr_off (void *mft_buf_ptr) +{ + return u16at (mft_buf_ptr, 0x14); +} + +static grub_uint16_t +res_attr_data_off (void *res_attr_ptr) +{ + return u16at (res_attr_ptr, 0x14); +} + +static grub_uint32_t +res_attr_data_len (void *res_attr_ptr) +{ + return u32at (res_attr_ptr, 0x10); +} + grub_ntfscomp_func_t grub_ntfscomp_func; static grub_err_t @@ -106,7 +124,7 @@ init_attr (struct grub_ntfs_attr *at, struct grub_ntfs_file *mft) { at->mft = mft; at->flags = (mft == &mft->data->mmft) ? GRUB_NTFS_AF_MMFT : 0; - at->attr_nxt = mft->buf + u16at (mft->buf, 0x14); + at->attr_nxt = mft->buf + first_attr_off (mft->buf); at->attr_end = at->emft_buf = at->edat_buf = at->sbuf = NULL; } @@ -154,7 +172,7 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) return NULL; } - new_pos = &at->emft_buf[u16at (at->emft_buf, 0x14)]; + new_pos = &at->emft_buf[first_attr_off (at->emft_buf)]; while (*new_pos != 0xFF) { if ((*new_pos == *at->attr_cur) @@ -213,7 +231,7 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) } else { - at->attr_nxt = at->attr_end + u16at (pa, 0x14); + at->attr_nxt = at->attr_end + res_attr_data_off (pa); at->attr_end = at->attr_end + u32at (pa, 4); pa_end = at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); } @@ -399,20 +417,20 @@ read_data (struct grub_ntfs_attr *at, grub_uint8_t *pa, grub_uint8_t *dest, if (pa[8] == 0) { - if (ofs + len > u32at (pa, 0x10)) + if (ofs + len > res_attr_data_len (pa)) return grub_error (GRUB_ERR_BAD_FS, "read out of range"); - if (u32at (pa, 0x10) > (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + if (res_attr_data_len (pa) > (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) return grub_error (GRUB_ERR_BAD_FS, "resident attribute too large"); if (pa >= at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) return grub_error (GRUB_ERR_BAD_FS, "resident attribute out of range"); - if (u16at (pa, 0x14) + u32at (pa, 0x10) > + if (res_attr_data_off (pa) + res_attr_data_len (pa) > (grub_addr_t) at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR) - (grub_addr_t) pa) return grub_error (GRUB_ERR_BAD_FS, "resident attribute out of range"); - grub_memcpy (dest, pa + u16at (pa, 0x14) + ofs, len); + grub_memcpy (dest, pa + res_attr_data_off (pa) + ofs, len); return 0; } @@ -556,7 +574,7 @@ init_file (struct grub_ntfs_file *mft, grub_uint64_t mftno) (unsigned long long) mftno); if (!pa[8]) - mft->size = u32at (pa, 0x10); + mft->size = res_attr_data_len (pa); else mft->size = u64at (pa, 0x30); @@ -801,7 +819,7 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, (u32at (cur_pos, 0x18) != 0x490024) || (u32at (cur_pos, 0x1C) != 0x300033)) continue; - cur_pos += u16at (cur_pos, 0x14); + cur_pos += res_attr_data_off (cur_pos); if (*cur_pos != 0x30) /* Not filename index */ continue; break; @@ -830,7 +848,7 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, { int is_resident = (cur_pos[8] == 0); - bitmap_len = ((is_resident) ? u32at (cur_pos, 0x10) : + bitmap_len = ((is_resident) ? res_attr_data_len (cur_pos) : u32at (cur_pos, 0x28)); bmp = grub_malloc (bitmap_len); @@ -851,14 +869,14 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, goto done; } - if (u16at (cur_pos, 0x14) + u32at (cur_pos, 0x10) > + if (res_attr_data_off (cur_pos) + res_attr_data_len (cur_pos) > (grub_addr_t) at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR) - (grub_addr_t) cur_pos) { grub_error (GRUB_ERR_BAD_FS, "resident bitmap out of range"); goto done; } - grub_memcpy (bmp, cur_pos + u16at (cur_pos, 0x14), + grub_memcpy (bmp, cur_pos + res_attr_data_off (cur_pos), bitmap_len); } else @@ -1222,12 +1240,12 @@ grub_ntfs_label (grub_device_t device, char **label) goto fail; } - if ((pa) && (pa[8] == 0) && (u32at (pa, 0x10))) + if ((pa) && (pa[8] == 0) && (res_attr_data_len (pa))) { int len; - len = u32at (pa, 0x10) / 2; - pa += u16at (pa, 0x14); + len = res_attr_data_len (pa) / 2; + pa += res_attr_data_off (pa); if (mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR) - pa >= 2 * len) *label = get_utf8 (pa, len); else From c5b706d4116748a4d93ef5a7a70d01e5a972a28c Mon Sep 17 00:00:00 2001 From: Nicolas Frayer Date: Tue, 19 Dec 2023 16:52:05 +0100 Subject: [PATCH 358/367] normal: Remove grub_env_set prefix in grub_try_normal_prefix Commit de735a453aa35 added a grub_env_set where the prefix contains the arch name in the pathname. This create issues when trying to load modules using this prefix as the pathname contains a "doubled" arch name in it (ie .../powerpc-ieee1275/powerpc-ieee1275/). Signed-off-by: Nicolas Frayer --- grub-core/normal/main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index d59145f861..bac7b8a0e1 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -372,7 +372,6 @@ grub_try_normal_prefix (const char *prefix) file = grub_file_open (config, GRUB_FILE_TYPE_CONFIG); if (file) { - grub_env_set ("prefix", prefix); grub_file_close (file); err = GRUB_ERR_NONE; } From 423962ac7cb045dd70d491b5ebb51fab96af36dc Mon Sep 17 00:00:00 2001 From: Leo Sandoval Date: Mon, 6 May 2024 15:35:41 -0600 Subject: [PATCH 359/367] grub-mkconfig.in: turn off executable owner bit Stricker permissions are required on the grub.cfg file, resulting in at most 0600 owner's file permissions. This resolves conflicting requirement permissions on grub2-pc package's grub2.cfg file. Signed-off-by: Leo Sandoval --- util/grub-mkconfig.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 util/grub-mkconfig.in diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in old mode 100644 new mode 100755 index 520a672cd2..fb382b648b --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -311,7 +311,7 @@ and /etc/grub.d/* files or please file a bug report with exit 1 else # none of the children aborted with error, install the new grub.cfg - oldumask=$(umask); umask 077 + oldumask=$(umask); umask 177 cat ${grub_cfg}.new > ${grub_cfg} umask $oldumask rm -f ${grub_cfg}.new From b60dfa5ba74640aa00c2394c2be7525fcc7343d8 Mon Sep 17 00:00:00 2001 From: Jon DeVree Date: Sun, 11 Feb 2024 10:34:58 -0500 Subject: [PATCH 360/367] fs/xfs: Handle non-continuous data blocks in directory extents The directory extent list does not have to be a continuous list of data blocks. When GRUB tries to read a non-existant member of the list, grub_xfs_read_file() will return a block of zero'ed memory. Checking for a zero'ed magic number is sufficient to skip this non-existant data block. Prior to commit 07318ee7e (fs/xfs: Fix XFS directory extent parsing) this was handled as a subtle side effect of reading the (non-existant) tail data structure. Since the block was zero'ed the computation of the number of directory entries in the block would return 0 as well. Fixes: 07318ee7e (fs/xfs: Fix XFS directory extent parsing) Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2254370 Signed-off-by: Jon DeVree Reviewed-By: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/fs/xfs.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index bc2224dbb4..8e02ab4a30 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -902,6 +902,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, grub_xfs_first_de(dir->data, dirblock); int entries = -1; char *end = dirblock + dirblk_size; + grub_uint32_t magic; numread = grub_xfs_read_file (dir, 0, 0, blk << dirblk_log2, @@ -912,6 +913,15 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, return 0; } + /* + * If this data block isn't actually part of the extent list then + * grub_xfs_read_file() returns a block of zeros. So, if the magic + * number field is all zeros then this block should be skipped. + */ + magic = *(grub_uint32_t *)(void *) dirblock; + if (!magic) + continue; + /* * Leaf and tail information are only in the data block if the number * of extents is 1. From 111f0f24b2f6396fdf0b0c9d040acf6bdd81807d Mon Sep 17 00:00:00 2001 From: Marta Lewandowska Date: Mon, 9 Oct 2023 08:53:18 +0200 Subject: [PATCH 361/367] add flag to only search root dev fixes bz#2223437 Signed-off-by: Marta Lewandowska --- grub-core/commands/search.c | 39 ++++++++++++++++++++++++++++++++----- grub-core/kern/misc.c | 30 ++++++++++++++++++++++++++++ include/grub/misc.h | 1 + 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c index 188d4c49d1..4735feefcd 100644 --- a/grub-core/commands/search.c +++ b/grub-core/commands/search.c @@ -87,11 +87,40 @@ iterate_device (const char *name, void *data) } /* Skip it if it's not the root device when requested. */ - root_dev = grub_env_get ("root"); - if (ctx->flags & SEARCH_FLAGS_ROOTDEV_ONLY && - (name[0] != root_dev[0] || name[1] != root_dev[1] || - name[2] != root_dev[2])) - return 0; + if (ctx->flags & SEARCH_FLAGS_ROOTDEV_ONLY) + { + const char *root_dev; + root_dev = grub_env_get ("root"); + if (root_dev != NULL && *root_dev != '\0') + { + char *root_disk = grub_malloc (grub_strlen(root_dev) + 1); + char *name_disk = grub_malloc (grub_strlen(name) + 1); + char *rem_1 = grub_malloc(grub_strlen(root_dev) + 1); + char *rem_2 = grub_malloc(grub_strlen(name) + 1); + + if (root_disk != NULL && name_disk != NULL && + rem_1 != NULL && rem_2 != NULL) + { + /* get just the disk name; partitions will be different. */ + grub_str_sep (root_dev, root_disk, ',', rem_1); + grub_str_sep (name, name_disk, ',', rem_2); + if (root_disk != NULL && *root_disk != '\0' && + name_disk != NULL && *name_disk != '\0') + if (grub_strcmp(root_disk, name_disk) != 0) + { + grub_free (root_disk); + grub_free (name_disk); + grub_free (rem_1); + grub_free (rem_2); + return 0; + } + } + grub_free (root_disk); + grub_free (name_disk); + grub_free (rem_1); + grub_free (rem_2); + } + } #ifdef DO_SEARCH_FS_UUID #define compare_fn grub_strcasecmp diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index cb45461402..c0ac7fee6c 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -619,6 +619,36 @@ grub_reverse (char *str) } } +/* Separate string into two parts, broken up by delimiter delim. */ +void +grub_str_sep (const char *s, char *p, char delim, char *r) +{ + char* t = grub_strndup(s, grub_strlen(s)); + + if (t != NULL && *t != '\0') + { + char* tmp = t; + + while (((*p = *t) != '\0') && ((*p = *t) != delim)) + { + p++; + t++; + } + *p = '\0'; + + if (*t != '\0') + { + t++; + while ((*r++ = *t++) != '\0') + ; + *r = '\0'; + } + grub_free (tmp); + } + else + grub_free (t); +} + /* Divide N by D, return the quotient, and store the remainder in *R. */ grub_uint64_t grub_divmod64 (grub_uint64_t n, grub_uint64_t d, grub_uint64_t *r) diff --git a/include/grub/misc.h b/include/grub/misc.h index faae0ae860..981526644d 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -314,6 +314,7 @@ void *EXPORT_FUNC(grub_memset) (void *s, int c, grub_size_t n); grub_size_t EXPORT_FUNC(grub_strlen) (const char *s) WARN_UNUSED_RESULT; int EXPORT_FUNC(grub_printf) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2))); int EXPORT_FUNC(grub_printf_) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2))); +void EXPORT_FUNC(grub_str_sep) (const char *s, char *p, char delim, char *r); /* Replace all `ch' characters of `input' with `with' and copy the result into `output'; return EOS address of `output'. */ From eb2b213dc48ba9bba2ef8e15dea54241e0a8fef3 Mon Sep 17 00:00:00 2001 From: Nicolas Frayer Date: Thu, 16 May 2024 10:58:32 +0200 Subject: [PATCH 362/367] cmd/search: Rework of CVE-2023-4001 fix The initial fix implemented a new flag that forces the grub cfg stub to be located on the same disk as grub. This created several issues such as RAID machines not being able to boot as their partition names under grub were different from the partition where grub is located. It also simply means that any machines with the /boot partition located on a disk other than the one containing grub won't boot. This commit denies booting if the grub cfg stub is located on a USB drive with a duplicated UUID (UUID being the same as the partition containing the actual grub cfg stub) Signed-off-by: Nicolas Frayer --- grub-core/commands/search.c | 134 +++++++++++++++++++++++++++++++++--- 1 file changed, 126 insertions(+), 8 deletions(-) diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c index 4735feefcd..5956b39dca 100644 --- a/grub-core/commands/search.c +++ b/grub-core/commands/search.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -54,6 +56,100 @@ struct search_ctx int is_cache; }; +static int +is_device_usb (const char *name) +{ + int ret = 0; + + grub_device_t dev = grub_device_open(name); + + if (dev) + { + struct grub_efidisk_data + { + grub_efi_handle_t handle; + grub_efi_device_path_t *device_path; + grub_efi_device_path_t *last_device_path; + grub_efi_block_io_t *block_io; + struct grub_efidisk_data *next; + }; + + if (dev->disk && dev->disk->data) + { + struct grub_efidisk_data *dp = dev->disk->data; + + if ( GRUB_EFI_DEVICE_PATH_TYPE (dp->last_device_path) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE && + GRUB_EFI_DEVICE_PATH_SUBTYPE (dp->last_device_path) == GRUB_EFI_USB_DEVICE_PATH_SUBTYPE) + { + ret = 1; + } + } + grub_device_close(dev); + } + + return ret; +} + +static int +get_device_uuid(const char *name, char** quid) +{ + int ret = 0; + + grub_device_t dev_part = grub_device_open(name); + + if (dev_part) + { + grub_fs_t fs; + + fs = grub_fs_probe (dev_part); + +#ifdef DO_SEARCH_FS_UUID +#define read_fn fs_uuid +#else +#define read_fn fs_label +#endif + if (fs && fs->read_fn) + { + fs->read_fn (dev_part, quid); + + if (grub_errno == GRUB_ERR_NONE && *quid) + { + ret = 1; + } + + } + grub_device_close (dev_part); + } + + return ret; +} +struct uuid_context { + char* name; + char* uuid; +}; + +static int +check_for_duplicate (const char *name, void *data) +{ + int ret = 0; + struct uuid_context * uuid_ctx = (struct uuid_context *)data; + char *quid = 0; + + get_device_uuid(name, &quid); + + if (quid == NULL) + return 0; + + if (!grub_strcasecmp(quid, uuid_ctx->uuid) && grub_strcasecmp(name, uuid_ctx->name)) + { + ret = 1; + } + + grub_free(quid); + + return ret; +} + /* Helper for FUNC_NAME. */ static int iterate_device (const char *name, void *data) @@ -106,14 +202,36 @@ iterate_device (const char *name, void *data) grub_str_sep (name, name_disk, ',', rem_2); if (root_disk != NULL && *root_disk != '\0' && name_disk != NULL && *name_disk != '\0') - if (grub_strcmp(root_disk, name_disk) != 0) - { - grub_free (root_disk); - grub_free (name_disk); - grub_free (rem_1); - grub_free (rem_2); - return 0; - } + { + grub_device_t dev, dev_part; + + if (is_device_usb(name) && !is_device_usb(root_dev)) + { + char *quid_name = NULL; + int longlist = 0; + struct uuid_context uuid_ctx; + int ret = 0; + + get_device_uuid(name, &quid_name); + if (!grub_strcmp(quid_name, ctx->key)) + { + uuid_ctx.name = name; + uuid_ctx.uuid = quid_name; + + ret = grub_device_iterate (check_for_duplicate, &uuid_ctx); + + if (ret) + { + grub_printf("Duplicated media UUID found, rebooting ...\n"); + grub_sleep(10); + grub_reboot(); + } + } + + if (quid_name) grub_free (quid_name); + + } + } } grub_free (root_disk); grub_free (name_disk); From 8db5849a6ac1a6abdfc409c800b6420f8e390b4d Mon Sep 17 00:00:00 2001 From: Leo Sandoval Date: Mon, 29 Apr 2024 12:09:11 -0600 Subject: [PATCH 363/367] Set non-executable stack sections on EFI assembly files For those manual assembly files created where no '.note.GNU-stack' section is explicitly added, linker defaults it as executable and this is the reason that RHEL CI rpminspect & annocheck tests are failing. The proposed change sets the corresponding GNU-stack sections otherwise CI detects the following security vulnerability $ annocheck annocheck --ignore-unknown --verbose --profile=el9 *.rpm 2>&1 | grep FAIL | grep stack (standard input):(standard input):Hardened: ./usr/lib/grub/x86_64-efi/kernel.exec: FAIL: gnu-stack test because .note.GNU-stack section has execute permission (standard input):(standard input):Hardened: ./usr/lib/grub/x86_64-efi/kernel.img: FAIL: gnu-stack test because .note.GNU-stack section has execute permission Signed-off-by: Leo Sandoval --- grub-core/kern/i386/efi/startup.S | 5 +++++ grub-core/kern/x86_64/efi/callwrap.S | 5 +++++ grub-core/kern/x86_64/efi/startup.S | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/grub-core/kern/i386/efi/startup.S b/grub-core/kern/i386/efi/startup.S index fc5ea3dac8..36d1b1a689 100644 --- a/grub-core/kern/i386/efi/startup.S +++ b/grub-core/kern/i386/efi/startup.S @@ -34,3 +34,8 @@ _start: movl %eax, EXT_C(grub_efi_system_table) call EXT_C(grub_main) ret + +/* An executable stack is not required for these functions. */ +#if defined (__linux__) && defined (__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/grub-core/kern/x86_64/efi/callwrap.S b/grub-core/kern/x86_64/efi/callwrap.S index 1337fd9fc8..db35ea7ef3 100644 --- a/grub-core/kern/x86_64/efi/callwrap.S +++ b/grub-core/kern/x86_64/efi/callwrap.S @@ -127,3 +127,8 @@ FUNCTION(efi_wrap_10) call *%rdi addq $88, %rsp ret + +/* An executable stack is not required for these functions. */ +#if defined (__linux__) && defined (__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/grub-core/kern/x86_64/efi/startup.S b/grub-core/kern/x86_64/efi/startup.S index 9357e5c5dd..f5c6bc3d8e 100644 --- a/grub-core/kern/x86_64/efi/startup.S +++ b/grub-core/kern/x86_64/efi/startup.S @@ -33,3 +33,8 @@ _start: andq $~0xf, %rsp call EXT_C(grub_main) /* Doesn't return. */ + +/* An executable stack is not required for these functions. */ +#if defined (__linux__) && defined (__ELF__) +.section .note.GNU-stack,"",%progbits +#endif From 9b97ca6b5268001d263c5689ea50bb4edea2eab8 Mon Sep 17 00:00:00 2001 From: Leo Sandoval Date: Thu, 16 May 2024 14:42:08 -0600 Subject: [PATCH 364/367] util/grub-mkconfig.in: revert mode to 644 By mistake, PR [1] changed the permission bits to 755 so revert it to 644 again. [1] https://github.com/rhboot/grub2/pull/167 Signed-off-by: Leo Sandoval --- util/grub-mkconfig.in | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 util/grub-mkconfig.in diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in old mode 100755 new mode 100644 From 6b8cb2092a6c18c8dcb088e5f784d643bf7d2e9b Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Thu, 23 May 2024 18:43:14 +0530 Subject: [PATCH 365/367] kern/ieee1275/init: Add IEEE 1275 Radix support for KVM on Power This patch adds support for Radix, Xive and Radix_gtse in Options vector5 which is required for KVM LPARs. KVM LPARs ONLY support Radix and not the Hash. Not enabling Radix on any PowerVM KVM LPARs will result in boot failure. Signed-off-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/kern/ieee1275/init.c | 63 +++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 8e7f742fad..4ec6598cfb 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -110,6 +110,16 @@ grub_addr_t grub_ieee1275_original_stack; #define DRC_INFO 0x40 #define BYTE22 (DY_MEM_V2 | DRC_INFO) +/* For ibm,arch-vec-5-platform-support. */ +#define XIVE_INDEX 0x17 +#define MMU_INDEX 0x18 +#define RADIX_GTSE_INDEX 0x1a +#define RADIX_ENABLED 0x40 +#define XIVE_ENABLED 0x40 +#define HASH_ENABLED 0x00 +#define MAX_SUPPORTED 0xC0 +#define RADIX_GTSE_ENABLED 0x40 + void grub_exit (int rc __attribute__((unused))) { @@ -694,6 +704,10 @@ struct option_vector5 grub_uint32_t platform_facilities; grub_uint8_t sub_processors; grub_uint8_t byte22; + grub_uint8_t xive; + grub_uint8_t mmu; + grub_uint8_t hpt_ext; + grub_uint8_t radix_gtse; } GRUB_PACKED; struct pvr_entry @@ -732,6 +746,13 @@ grub_ieee1275_ibm_cas (void) { int rc; grub_ieee1275_ihandle_t root; + grub_uint8_t ibm_arch_platform_support[8]; + grub_ssize_t actual; + grub_uint8_t xive_support = 0; + grub_uint8_t mmu_support = 0; + grub_uint8_t radix_gtse_support = 0; + int i = 0; + int prop_len = 8; struct cas_args { struct grub_ieee1275_common_hdr common; @@ -740,6 +761,46 @@ grub_ieee1275_ibm_cas (void) grub_ieee1275_cell_t cas_addr; grub_ieee1275_cell_t result; } args; + + grub_ieee1275_get_integer_property (grub_ieee1275_chosen, + "ibm,arch-vec-5-platform-support", + (grub_uint32_t *) ibm_arch_platform_support, + sizeof (ibm_arch_platform_support), + &actual); + + for (i = 0; i < prop_len; i++) + { + switch (ibm_arch_platform_support[i]) + { + case XIVE_INDEX: + if (ibm_arch_platform_support[i + 1] & MAX_SUPPORTED) + xive_support = XIVE_ENABLED; + else + xive_support = 0; + break; + + case MMU_INDEX: + if (ibm_arch_platform_support[i + 1] & MAX_SUPPORTED) + mmu_support = RADIX_ENABLED; + else + mmu_support = HASH_ENABLED; + break; + + case RADIX_GTSE_INDEX: + if (mmu_support == RADIX_ENABLED) + radix_gtse_support = ibm_arch_platform_support[i + 1] & RADIX_GTSE_ENABLED; + else + radix_gtse_support = 0; + break; + + default: + /* Ignoring the other indexes of ibm,arch-vec-5-platform-support. */ + break; + } + /* Skipping the property value. */ + i++; + } + struct cas_vector vector = { .pvr_list = { { 0x00000000, 0xffffffff } }, /* any processor */ @@ -756,7 +817,7 @@ grub_ieee1275_ibm_cas (void) .vec4 = 0x0001, /* set required minimum capacity % to the lowest value */ .vec5_size = 1 + sizeof (struct option_vector5) - 2, .vec5 = { - 0, BYTE2, 0, CMO, ASSOCIATIVITY, BIN_OPTS, 0, 0, MAX_CPU, 0, 0, PLATFORM_FACILITIES, SUB_PROCESSORS, BYTE22 + 0, BYTE2, 0, CMO, ASSOCIATIVITY, BIN_OPTS, 0, 0, MAX_CPU, 0, 0, PLATFORM_FACILITIES, SUB_PROCESSORS, BYTE22, xive_support, mmu_support, 0, radix_gtse_support } }; From 2b9a830f2208733d6a2260e21a86359e1de888d7 Mon Sep 17 00:00:00 2001 From: Nicolas Frayer Date: Tue, 16 Jul 2024 11:11:43 +0200 Subject: [PATCH 366/367] grub2-mkconfig: Ensure grub cfg stub is not overwritten /boot/efi/EFI/$os_name/grub.cfg contains a grub cfg stub that should not be overwritten by grub2-mkconfig. Ensure that we prevent this from happening. Signed-off-by: Marta Lewandowska Signed-off-by: Nicolas Frayer --- util/grub-mkconfig.in | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index fb382b648b..efa36cc45a 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -109,6 +109,20 @@ do esac done +os_name=$(grep '^ID=' /etc/os-release | sed 's/ID=//') +if test "$os_name" = '"rhel"'; then + os_name=redhat +elif test "$os_name" = '"centos"'; then + os_name=centos +fi +if test "x${grub_cfg}" = "x/boot/efi/EFI/$os_name/grub.cfg" &&\ + mountpoint -q /boot/efi; then + gettext_printf "Running \`grub2-mkconfig -o %s' will overwrite the GRUB wrapper.\n" "$grub_cfg" 1>&2 + gettext_printf "Please run \`grub2-mkconfig -o /boot/grub2/grub.cfg' instead to update grub.cfg.\n" 1>&2 + gettext_printf "GRUB configuration file was not updated.\n" 1>&2 + exit 1 +fi + if [ "x$EUID" = "x" ] ; then EUID=`id -u` fi From d634aae0191db81d2660b5b996a05e4fa8b4adfa Mon Sep 17 00:00:00 2001 From: Matteo Croce Date: Fri, 19 Jul 2024 23:48:14 +0200 Subject: [PATCH 367/367] skip empty lines in entry file grub_file_getline() doesn't make any distinction between empty lines or EOF, so the following entry: title Fedora Linux (6.10.0-matteo) 40 (KDE Plasma) version 6.10.0-matteo linux /boot/vmlinuz-6.10.0-matteo options root=/dev/nvme0n1p2 ro cgroup_no_v1=all boots the kernel with an empty command line. Fix this by also checking grub_errno, which correctly signals the EOF. Signed-off-by: Matteo Croce --- grub-core/commands/blscfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/commands/blscfg.c b/grub-core/commands/blscfg.c index 6e398fc175..fc34f568ae 100644 --- a/grub-core/commands/blscfg.c +++ b/grub-core/commands/blscfg.c @@ -538,7 +538,7 @@ static int read_entry ( char *separator; buf = grub_file_getline (f); - if (!buf) + if (!buf && grub_errno) break; while (buf && buf[0] && (buf[0] == ' ' || buf[0] == '\t'))